Removed currentCoroutineContext without replacement
diff --git a/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/Builders.kt b/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/Builders.kt
index 9676dd5..a8f4c9c 100644
--- a/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/Builders.kt
+++ b/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/Builders.kt
@@ -7,14 +7,16 @@
/**
* Launches new coroutine without blocking current thread and returns a reference to the coroutine as a [Job].
- * The [context] for the new coroutine must be explicitly specified and must include [CoroutineDispatcher] element.
+ * The running coroutine is cancelled when the resulting job is [cancelled][Job.cancel].
+ *
+ * The [context] for the new coroutine must be explicitly specified.
* See [CoroutineDispatcher] for the standard [context] implementations that are provided by `kotlinx.coroutines`.
- * The specified context is added to the context of the parent running coroutine (if any) inside which this function
- * is invoked. The [Job] of the resulting coroutine is a child of the job of the parent coroutine (if any).
+ * The [context][CoroutineScope.context] of the parent coroutine from its [scope][CoroutineScope] may be used,
+ * in which case the [Job] of the resulting coroutine is a child of the job of the parent coroutine.
*
* Uncaught exceptions in this coroutine cancel parent job in the context by default
- * (unless [CoroutineExceptionHandler] is explicitly specified), which means that when `launch` is used from another
- * coroutine, any uncaught exception leads to the cancellation of parent coroutine.
+ * (unless [CoroutineExceptionHandler] is explicitly specified), which means that when `launch` is used with
+ * the context of another coroutine, then any uncaught exception leads to the cancellation of parent coroutine.
*
* See [newCoroutineContext] for a description of debugging facilities that are available for newly created coroutine.
*/
@@ -23,9 +25,11 @@
/**
* Calls the specified suspending block with a given coroutine context, suspends until it completes, and returns
- * the result. It immediately applies dispatcher from the new context, shifting execution of the block into the
+ * the result.
+ *
+ * This function immediately applies dispatcher from the new context, shifting execution of the block into the
* different thread inside the block, and back when it completes.
- * The specified [context] is merged onto the current coroutine context.
+ * The specified [context] is added onto the current coroutine context for the execution of the block.
*/
public suspend fun <T> run(context: CoroutineContext, block: suspend CoroutineScope.() -> T): T =
suspendCoroutine { cont ->
@@ -41,8 +45,6 @@
* The default [CoroutineDispatcher] for this builder in an implementation of [EventLoop] that processes continuations
* in this blocked thread until the completion of this coroutine.
* See [CoroutineDispatcher] for the other implementations that are provided by `kotlinx.coroutines`.
- * The specified [context] is added to the context of the parent running coroutine (if any) inside which this function
- * is invoked. The [Job] of the resulting coroutine is a child of the job of the parent coroutine (if any).
*
* If this blocked thread is interrupted (see [Thread.interrupt]), then the coroutine job is cancelled and
* this `runBlocking` invocation throws [InterruptedException].
@@ -52,7 +54,7 @@
@Throws(InterruptedException::class)
public fun <T> runBlocking(context: CoroutineContext = EmptyCoroutineContext, block: suspend CoroutineScope.() -> T): T {
val currentThread = Thread.currentThread()
- val privateEventLoop = if (context[ContinuationInterceptor] as? CoroutineDispatcher == null)
+ val privateEventLoop = if (context[ContinuationInterceptor] == null)
EventLoopImpl(currentThread) else null
val newContext = newCoroutineContext(context + (privateEventLoop ?: EmptyCoroutineContext))
val coroutine = BlockingCoroutine<T>(newContext, currentThread, privateEventLoop != null)
diff --git a/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/CoroutineContext.kt b/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/CoroutineContext.kt
new file mode 100644
index 0000000..2d8a0a0
--- /dev/null
+++ b/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/CoroutineContext.kt
@@ -0,0 +1,89 @@
+package kotlinx.coroutines.experimental
+
+import java.util.concurrent.atomic.AtomicLong
+import kotlin.coroutines.AbstractCoroutineContextElement
+import kotlin.coroutines.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 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 Here : CoroutineDispatcher() {
+ override fun isDispatchNeeded(context: CoroutineContext): Boolean = false
+ override fun dispatch(context: CoroutineContext, block: Runnable) { throw UnsupportedOperationException() }
+}
+
+/**
+ * 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)"
+}
diff --git a/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/CoroutineDispatcher.kt b/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/CoroutineDispatcher.kt
index 78d810c..fb46490 100644
--- a/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/CoroutineDispatcher.kt
+++ b/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/CoroutineDispatcher.kt
@@ -17,14 +17,9 @@
* a common pool of shared background threads.
* This is an appropriate choice for compute-intensive coroutines that consume a lot of CPU resources.
* * Private thread pools can be created with [newSingleThreadContext] and [newFixedThreadPoolContext].
- * * [currentCoroutineContext] -- inherits the context of the parent coroutine,
- * but throws [IllegalStateException] if used outside of coroutine. Use [currentCoroutineContextOrDefault]
- * if a default is needed when outside of coroutine.
- * This is an appropriate choice for libraries that need to inherit parent coroutine context.
- * * There are context implementations for UI libraries like `Swing` and `JavaFx` in separate modules.
+ * * An arbitrary [Executor][java.util.concurrent.Executor] can be converted to dispatcher with [toCoroutineDispatcher] extension function.
*
- * This class ensures that [currentCoroutineContext] is correctly transferred to a new thread and that
- * debugging facilities in [newCoroutineContext] function work properly.
+ * This class ensures that debugging facilities in [newCoroutineContext] function work properly.
*/
public abstract class CoroutineDispatcher :
AbstractCoroutineContextElement(ContinuationInterceptor), ContinuationInterceptor {
@@ -50,12 +45,12 @@
val context = continuation.context
if (dispatcher.isDispatchNeeded(context))
dispatcher.dispatch(context, Runnable {
- withDefaultCoroutineContext(context) {
+ withCoroutineContext(context) {
continuation.resume(value)
}
})
else
- withDefaultCoroutineContext(context) {
+ withCoroutineContext(context) {
continuation.resume(value)
}
}
@@ -64,12 +59,12 @@
val context = continuation.context
if (dispatcher.isDispatchNeeded(context))
dispatcher.dispatch(context, Runnable {
- withDefaultCoroutineContext(context) {
+ withCoroutineContext(context) {
continuation.resumeWithException(exception)
}
})
else
- withDefaultCoroutineContext(context) {
+ withCoroutineContext(context) {
continuation.resumeWithException(exception)
}
}
diff --git a/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/CurrentCoroutineContext.kt b/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/CurrentCoroutineContext.kt
deleted file mode 100644
index 31740b7..0000000
--- a/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/CurrentCoroutineContext.kt
+++ /dev/null
@@ -1,131 +0,0 @@
-package kotlinx.coroutines.experimental
-
-import java.util.concurrent.atomic.AtomicLong
-import kotlin.coroutines.AbstractCoroutineContextElement
-import kotlin.coroutines.ContinuationInterceptor
-import kotlin.coroutines.CoroutineContext
-import kotlin.coroutines.EmptyCoroutineContext
-
-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()
-
-@PublishedApi
-internal val CURRENT_CONTEXT = ThreadLocal<CoroutineContext>()
-
-/**
- * A coroutine dispatcher that 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 Here : CoroutineDispatcher() {
- override fun isDispatchNeeded(context: CoroutineContext): Boolean = false
- override fun dispatch(context: CoroutineContext, block: Runnable) { throw UnsupportedOperationException() }
-}
-
-/**
- * Returns the context of the coroutine that this function is invoked in or throws
- * [IllegalStateException] if not invoked inside a coroutine.
- * This function can be used to inherit execution context of the parent coroutine if needed,
- * like in `launch(currentCoroutineContext()) { ... }`.
- * This function properly works only for coroutines that are created using [newCoroutineContext] function,
- * as all coroutine builders in `kotlinx.coroutines` do.
- */
-public val currentCoroutineContext: CoroutineContext
- get() = CURRENT_CONTEXT.get() ?: throw IllegalStateException("Not inside a coroutine")
-
-/**
- * Returns the context of the coroutine that this function is invoked in or a specified [default]
- * if not invoked inside a coroutine. A [default] must be a singleton [CoroutineDispatcher] element.
- * See [CoroutineDispatcher] for the standard implementations that are provided by `kotlinx.coroutines`.
- */
-public fun currentCoroutineContextOrDefault(default: CoroutineDispatcher): CoroutineContext =
- CURRENT_CONTEXT.get() ?: default
-
-/**
- * Creates context for the new coroutine with user-specified overrides from [context] parameter.
- * The [context] for the new coroutine must be explicitly specified and must include [CoroutineDispatcher] element.
- * This function shall be used to start new coroutines.
- *
- * **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 in the same thread and a [CoroutineDispatcher]
- * is not explicitly or dispatcher executes continuation 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 {
- val current = CURRENT_CONTEXT.get()
- if (context !== current) {
- check(context[ContinuationInterceptor] is CoroutineDispatcher) {
- "Context of new coroutine must include CoroutineDispatcher"
- }
- }
- return ((current ?: EmptyCoroutineContext) + context).let {
- if (DEBUG) it + CoroutineId(COROUTINE_ID.incrementAndGet()) else it
- }
-}
-
-/**
- * Executes a block using a given default coroutine context.
- * This context affects all new coroutines that are started withing the block.
- */
-internal inline fun <T> withDefaultCoroutineContext(context: CoroutineContext, block: () -> T): T {
- val oldContext = CURRENT_CONTEXT.get()
- val oldName = updateContext(oldContext, context)
- try {
- return block()
- } finally {
- restoreContext(oldContext, oldName)
- }
-}
-
-@PublishedApi
-internal fun updateContext(oldContext: CoroutineContext?, newContext: CoroutineContext): String? {
- if (newContext === oldContext) return null
- CURRENT_CONTEXT.set(newContext)
- if (!DEBUG) return null
- val newId = newContext[CoroutineId] ?: return null
- val oldId = oldContext?.get(CoroutineId)
- if (newId === oldId) return null
- val currentThread = Thread.currentThread()
- val oldName = currentThread.name
- val coroutineName = newContext[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(oldContext: CoroutineContext?, oldName: String?) {
- if (oldName != null) Thread.currentThread().name = oldName
- CURRENT_CONTEXT.set(oldContext)
-}
-
-private class CoroutineId(val id: Long) : AbstractCoroutineContextElement(CoroutineId) {
- companion object Key : CoroutineContext.Key<CoroutineId>
- override fun toString(): String = "CoroutineId($id)"
-}
diff --git a/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/Deferred.kt b/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/Deferred.kt
index d1727aa..70a5335 100644
--- a/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/Deferred.kt
+++ b/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/Deferred.kt
@@ -27,11 +27,12 @@
/**
* Starts new coroutine and returns its result as an implementation of [Deferred].
- * The running coroutine is cancelled when the resulting job is cancelled.
- * The [context] for the new coroutine must be explicitly specified and must include [CoroutineDispatcher] element.
+ * The running coroutine is cancelled when the resulting object is [cancelled][Job.cancel].
+ *
+ * The [context] for the new coroutine must be explicitly specified.
* See [CoroutineDispatcher] for the standard [context] implementations that are provided by `kotlinx.coroutines`.
- * The specified context is added to the context of the parent running coroutine (if any) inside which this function
- * is invoked. The [Job] of the resulting coroutine is a child of the job of the parent coroutine (if any).
+ * The [context][CoroutineScope.context] of the parent coroutine from its [scope][CoroutineScope] may be used,
+ * in which case the [Job] of the resulting coroutine is a child of the job of the parent coroutine.
*/
public fun <T> defer(context: CoroutineContext, block: suspend CoroutineScope.() -> T) : Deferred<T> =
DeferredCoroutine<T>(newCoroutineContext(context)).also { block.startCoroutine(it, it) }
diff --git a/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/Job.kt b/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/Job.kt
index dd0a1b0..f3fd0a3 100644
--- a/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/Job.kt
+++ b/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/Job.kt
@@ -12,13 +12,14 @@
/**
* A background job. It has two states: _active_ (initial state) and _completed_ (final state).
- * It can be _cancelled_ at any time with [cancel] function that forces it to become completed immediately.
- * A job in the coroutine context represents the coroutine itself.
+ *
+ * A job can be _cancelled_ at any time with [cancel] function that forces it to become completed immediately.
+ * A job in the coroutine [context][CoroutineScope.context] represents the coroutine itself.
* A job is active while the coroutine is working and job's cancellation aborts the coroutine when
* the coroutine is suspended on a _cancellable_ suspension point by throwing [CancellationException]
* inside the coroutine.
*
- * Jobs can have a _parent_. A job with a parent is cancelled when its parent completes.
+ * A job can have a _parent_. A job with a parent is cancelled when its parent completes.
*
* All functions on this interface are thread-safe.
*/