blob: fd6ced501542a04fc2e121ea4cae9af6d5873d63 [file] [log] [blame]
/*
* Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/
package kotlinx.coroutines.experimental
import kotlinx.coroutines.experimental.internal.*
import kotlinx.coroutines.experimental.scheduling.*
import java.util.concurrent.atomic.*
import kotlin.coroutines.experimental.*
/**
* Name of the property that controls coroutine debugging. See [newCoroutineContext].
*/
public const val DEBUG_PROPERTY_NAME = "kotlinx.coroutines.debug"
/**
* Automatic debug configuration value for [DEBUG_PROPERTY_NAME]. See [newCoroutineContext].
*/
public const val DEBUG_PROPERTY_VALUE_AUTO = "auto"
/**
* Debug turned on value for [DEBUG_PROPERTY_NAME]. See [newCoroutineContext].
*/
public const val DEBUG_PROPERTY_VALUE_ON = "on"
/**
* Debug turned on value for [DEBUG_PROPERTY_NAME]. See [newCoroutineContext].
*/
public const val DEBUG_PROPERTY_VALUE_OFF = "off"
internal val DEBUG = systemProp(DEBUG_PROPERTY_NAME).let { value ->
when (value) {
DEBUG_PROPERTY_VALUE_AUTO, null -> CoroutineId::class.java.desiredAssertionStatus()
DEBUG_PROPERTY_VALUE_ON, "" -> true
DEBUG_PROPERTY_VALUE_OFF -> false
else -> error("System property '$DEBUG_PROPERTY_NAME' has unrecognized value '$value'")
}
}
private val COROUTINE_ID = AtomicLong()
// for tests only
internal fun resetCoroutineId() {
COROUTINE_ID.set(0)
}
internal const val COROUTINES_SCHEDULER_PROPERTY_NAME = "kotlinx.coroutines.scheduler"
internal val useCoroutinesScheduler = systemProp(COROUTINES_SCHEDULER_PROPERTY_NAME).let { value ->
when (value) {
null -> false
"", "on" -> true
else -> error("System property '$COROUTINES_SCHEDULER_PROPERTY_NAME' has unrecognized value '$value'")
}
}
/**
* The default [CoroutineDispatcher] that is used by all standard builders like
* [launch], [async], etc if no dispatcher nor any other [ContinuationInterceptor] is specified in their context.
*
* It is currently equal to [CommonPool], but the value is subject to change in the future.
* You can set system property "`kotlinx.coroutines.scheduler`" (either no value or to the value of "`on`")
* to use an experimental coroutine dispatcher that shares threads with [IO] dispatcher and thus can switch to
* [IO] context without performing an actual thread context switch.
*/
@Suppress("PropertyName")
public actual val DefaultDispatcher: CoroutineDispatcher =
if (useCoroutinesScheduler) BackgroundDispatcher else CommonPool
/**
* Name of the property that defines the maximal number of threads that are used by [IO] coroutines dispatcher.
*/
public const val IO_PARALLELISM_PROPERTY_NAME = "kotlinx.coroutines.io.parallelism"
/**
* The [CoroutineDispatcher] that is designed for offloading blocking IO tasks to a shared pool of threads.
*
* Additional threads in this pool are created and are shutdown on demand.
* The number of threads used by this dispatcher is limited by the value of
* "`kotlinx.coroutines.io.parallelism`" ([IO_PARALLELISM_PROPERTY_NAME]) system property.
* It defaults to the limit of 64 threads or the number of cores (whichever is larger).
*/
public val IO by lazy {
BackgroundDispatcher.blocking(systemProp(IO_PARALLELISM_PROPERTY_NAME, 64.coerceAtLeast(AVAILABLE_PROCESSORS)))
}
/**
* Creates context for the new coroutine. It installs [DefaultDispatcher] when no other dispatcher nor
* [ContinuationInterceptor] is specified, and adds optional support for debugging facilities (when turned on).
*
* **Debugging facilities:** In debug mode every coroutine is assigned a unique consecutive identifier.
* Every thread that executes a coroutine has its name modified to include the name and identifier of the
* currently running coroutine.
* When one coroutine is suspended and resumes another coroutine that is dispatched in the same thread,
* then the thread name displays
* the whole stack of coroutine descriptions that are being executed on this thread.
*
* Enable debugging facilities with "`kotlinx.coroutines.debug`" ([DEBUG_PROPERTY_NAME]) system property
* , use the following values:
* * "`auto`" (default mode, [DEBUG_PROPERTY_VALUE_AUTO]) -- enabled when assertions are enabled with "`-ea`" JVM option.
* * "`on`" ([DEBUG_PROPERTY_VALUE_ON]) or empty string -- enabled.
* * "`off`" ([DEBUG_PROPERTY_VALUE_OFF]) -- disabled.
*
* Coroutine name can be explicitly assigned using [CoroutineName] context element.
* The string "coroutine" is used as a default name.
*/
@JvmOverloads // for binary compatibility with newCoroutineContext(context: CoroutineContext) version
@Suppress("ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS")
public actual fun newCoroutineContext(context: CoroutineContext, parent: Job? = null): CoroutineContext {
val debug = if (DEBUG) context + CoroutineId(COROUTINE_ID.incrementAndGet()) else context
val wp = if (parent == null) debug else debug + parent
return if (context !== DefaultDispatcher && context[ContinuationInterceptor] == null)
wp + DefaultDispatcher else wp
}
/**
* Executes a block using a given coroutine context.
*/
internal actual inline fun <T> withCoroutineContext(context: CoroutineContext, block: () -> T): T {
val oldValue = updateThreadContext(context)
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
}
}