blob: 3d3d1c087ead2169209e48cca89a9e6abe43f0d9 [file] [log] [blame]
package kotlinx.coroutines.experimental
import java.util.concurrent.atomic.AtomicLong
import kotlin.coroutines.experimental.AbstractCoroutineContextElement
import kotlin.coroutines.experimental.CoroutineContext
private const val DEBUG_PROPERTY_NAME = "kotlinx.coroutines.debug"
private val DEBUG = run {
val value = System.getProperty(DEBUG_PROPERTY_NAME)
when (value) {
"auto", null -> CoroutineId::class.java.desiredAssertionStatus()
"on", "" -> true
"off" -> false
else -> error("System property '$DEBUG_PROPERTY_NAME' has unrecognized value '$value'")
}
}
private val COROUTINE_ID = AtomicLong()
/**
* A coroutine dispatcher that is not confined to any specific thread.
* It executes initial continuation of the coroutine _right here_ in the current call-frame
* and let the coroutine resume in whatever thread that is used by the corresponding suspending function, without
* mandating any specific threading policy.
*/
public object Unconfined : CoroutineDispatcher() {
override fun isDispatchNeeded(context: CoroutineContext): Boolean = false
override fun dispatch(context: CoroutineContext, block: Runnable) { throw UnsupportedOperationException() }
}
@Deprecated(message = "`Here` was renamed to `Unconfined`",
replaceWith = ReplaceWith(expression = "Unconfined"))
public typealias Here = Unconfined
/**
* Creates context for the new coroutine with 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 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`" system property, use the following values:
* * "`auto`" (default mode) -- enabled when assertions are enabled with "`-ea`" JVM option.
* * "`on`" or empty string -- enabled.
* * "`off`" -- disabled.
*
* Coroutine name can be explicitly assigned using [CoroutineName] context element.
* The string "coroutine" is used as a default name.
*/
public fun newCoroutineContext(context: CoroutineContext): CoroutineContext =
if (DEBUG) context + CoroutineId(COROUTINE_ID.incrementAndGet()) else context
/**
* Executes a block using a given coroutine context.
*/
internal inline fun <T> withCoroutineContext(context: CoroutineContext, block: () -> T): T {
val oldName = updateContext(context)
try {
return block()
} finally {
restoreContext(oldName)
}
}
@PublishedApi
internal fun updateContext(context: CoroutineContext): String? {
if (!DEBUG) return null
val newId = context[CoroutineId] ?: return null
val currentThread = Thread.currentThread()
val oldName = currentThread.name
val coroutineName = context[CoroutineName]?.name ?: "coroutine"
currentThread.name = buildString(oldName.length + coroutineName.length + 10) {
append(oldName)
append(" @")
append(coroutineName)
append('#')
append(newId.id)
}
return oldName
}
@PublishedApi
internal fun restoreContext(oldName: String?) {
if (oldName != null) Thread.currentThread().name = oldName
}
private class CoroutineId(val id: Long) : AbstractCoroutineContextElement(CoroutineId) {
companion object Key : CoroutineContext.Key<CoroutineId>
override fun toString(): String = "CoroutineId($id)"
}