| /* |
| * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. |
| */ |
| |
| package kotlinx.coroutines |
| |
| import kotlinx.coroutines.internal.* |
| import kotlinx.coroutines.scheduling.* |
| import java.util.concurrent.atomic.* |
| import kotlin.coroutines.* |
| |
| internal const val COROUTINES_SCHEDULER_PROPERTY_NAME = "kotlinx.coroutines.scheduler" |
| |
| internal val useCoroutinesScheduler = systemProp(COROUTINES_SCHEDULER_PROPERTY_NAME).let { value -> |
| when (value) { |
| null, "", "on" -> true |
| "off" -> false |
| else -> error("System property '$COROUTINES_SCHEDULER_PROPERTY_NAME' has unrecognized value '$value'") |
| } |
| } |
| |
| internal actual fun createDefaultDispatcher(): CoroutineDispatcher = |
| if (useCoroutinesScheduler) DefaultScheduler else CommonPool |
| |
| /** |
| * Creates context for the new coroutine. It installs [Dispatchers.Default] when no other dispatcher nor |
| * [ContinuationInterceptor] is specified, and adds optional support for debugging facilities (when turned on). |
| * |
| * See [DEBUG_PROPERTY_NAME] for description of debugging facilities on JVM. |
| */ |
| @ExperimentalCoroutinesApi |
| public actual fun CoroutineScope.newCoroutineContext(context: CoroutineContext): CoroutineContext { |
| val combined = coroutineContext + context |
| val debug = if (DEBUG) combined + CoroutineId(COROUTINE_ID.incrementAndGet()) else combined |
| return if (combined !== Dispatchers.Default && combined[ContinuationInterceptor] == null) |
| debug + Dispatchers.Default else debug |
| } |
| |
| /** |
| * Executes a block using a given coroutine context. |
| */ |
| internal actual inline fun <T> withCoroutineContext(context: CoroutineContext, countOrElement: Any?, block: () -> T): T { |
| val oldValue = updateThreadContext(context, countOrElement) |
| try { |
| return block() |
| } finally { |
| restoreThreadContext(context, oldValue) |
| } |
| } |
| |
| internal actual val CoroutineContext.coroutineName: String? get() { |
| if (!DEBUG) return null |
| val coroutineId = this[CoroutineId] ?: return null |
| val coroutineName = this[CoroutineName]?.name ?: "coroutine" |
| return "$coroutineName#${coroutineId.id}" |
| } |
| |
| private const val DEBUG_THREAD_NAME_SEPARATOR = " @" |
| |
| internal data class CoroutineId( |
| val id: Long |
| ) : ThreadContextElement<String>, AbstractCoroutineContextElement(CoroutineId) { |
| companion object Key : CoroutineContext.Key<CoroutineId> |
| override fun toString(): String = "CoroutineId($id)" |
| |
| override fun updateThreadContext(context: CoroutineContext): String { |
| val coroutineName = context[CoroutineName]?.name ?: "coroutine" |
| val currentThread = Thread.currentThread() |
| val oldName = currentThread.name |
| var lastIndex = oldName.lastIndexOf(DEBUG_THREAD_NAME_SEPARATOR) |
| if (lastIndex < 0) lastIndex = oldName.length |
| currentThread.name = buildString(lastIndex + coroutineName.length + 10) { |
| append(oldName.substring(0, lastIndex)) |
| append(DEBUG_THREAD_NAME_SEPARATOR) |
| append(coroutineName) |
| append('#') |
| append(id) |
| } |
| return oldName |
| } |
| |
| override fun restoreThreadContext(context: CoroutineContext, oldState: String) { |
| Thread.currentThread().name = oldState |
| } |
| } |