Skip to content

Commit 44ba4b1

Browse files
committed
Removed currentCoroutineContext without replacement
1 parent 6c53d76 commit 44ba4b1

File tree

6 files changed

+116
-159
lines changed

6 files changed

+116
-159
lines changed

kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/Builders.kt

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,16 @@ import kotlin.coroutines.*
77

88
/**
99
* Launches new coroutine without blocking current thread and returns a reference to the coroutine as a [Job].
10-
* The [context] for the new coroutine must be explicitly specified and must include [CoroutineDispatcher] element.
10+
* The running coroutine is cancelled when the resulting job is [cancelled][Job.cancel].
11+
*
12+
* The [context] for the new coroutine must be explicitly specified.
1113
* See [CoroutineDispatcher] for the standard [context] implementations that are provided by `kotlinx.coroutines`.
12-
* The specified context is added to the context of the parent running coroutine (if any) inside which this function
13-
* is invoked. The [Job] of the resulting coroutine is a child of the job of the parent coroutine (if any).
14+
* The [context][CoroutineScope.context] of the parent coroutine from its [scope][CoroutineScope] may be used,
15+
* in which case the [Job] of the resulting coroutine is a child of the job of the parent coroutine.
1416
*
1517
* Uncaught exceptions in this coroutine cancel parent job in the context by default
16-
* (unless [CoroutineExceptionHandler] is explicitly specified), which means that when `launch` is used from another
17-
* coroutine, any uncaught exception leads to the cancellation of parent coroutine.
18+
* (unless [CoroutineExceptionHandler] is explicitly specified), which means that when `launch` is used with
19+
* the context of another coroutine, then any uncaught exception leads to the cancellation of parent coroutine.
1820
*
1921
* See [newCoroutineContext] for a description of debugging facilities that are available for newly created coroutine.
2022
*/
@@ -23,9 +25,11 @@ fun launch(context: CoroutineContext, block: suspend CoroutineScope.() -> Unit):
2325

2426
/**
2527
* Calls the specified suspending block with a given coroutine context, suspends until it completes, and returns
26-
* the result. It immediately applies dispatcher from the new context, shifting execution of the block into the
28+
* the result.
29+
*
30+
* This function immediately applies dispatcher from the new context, shifting execution of the block into the
2731
* different thread inside the block, and back when it completes.
28-
* The specified [context] is merged onto the current coroutine context.
32+
* The specified [context] is added onto the current coroutine context for the execution of the block.
2933
*/
3034
public suspend fun <T> run(context: CoroutineContext, block: suspend CoroutineScope.() -> T): T =
3135
suspendCoroutine { cont ->
@@ -41,8 +45,6 @@ public suspend fun <T> run(context: CoroutineContext, block: suspend CoroutineSc
4145
* The default [CoroutineDispatcher] for this builder in an implementation of [EventLoop] that processes continuations
4246
* in this blocked thread until the completion of this coroutine.
4347
* See [CoroutineDispatcher] for the other implementations that are provided by `kotlinx.coroutines`.
44-
* The specified [context] is added to the context of the parent running coroutine (if any) inside which this function
45-
* is invoked. The [Job] of the resulting coroutine is a child of the job of the parent coroutine (if any).
4648
*
4749
* If this blocked thread is interrupted (see [Thread.interrupt]), then the coroutine job is cancelled and
4850
* this `runBlocking` invocation throws [InterruptedException].
@@ -52,7 +54,7 @@ public suspend fun <T> run(context: CoroutineContext, block: suspend CoroutineSc
5254
@Throws(InterruptedException::class)
5355
public fun <T> runBlocking(context: CoroutineContext = EmptyCoroutineContext, block: suspend CoroutineScope.() -> T): T {
5456
val currentThread = Thread.currentThread()
55-
val privateEventLoop = if (context[ContinuationInterceptor] as? CoroutineDispatcher == null)
57+
val privateEventLoop = if (context[ContinuationInterceptor] == null)
5658
EventLoopImpl(currentThread) else null
5759
val newContext = newCoroutineContext(context + (privateEventLoop ?: EmptyCoroutineContext))
5860
val coroutine = BlockingCoroutine<T>(newContext, currentThread, privateEventLoop != null)
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
package kotlinx.coroutines.experimental
2+
3+
import java.util.concurrent.atomic.AtomicLong
4+
import kotlin.coroutines.AbstractCoroutineContextElement
5+
import kotlin.coroutines.CoroutineContext
6+
7+
private const val DEBUG_PROPERTY_NAME = "kotlinx.coroutines.debug"
8+
9+
private val DEBUG = run {
10+
val value = System.getProperty(DEBUG_PROPERTY_NAME)
11+
when (value) {
12+
"auto", null -> CoroutineId::class.java.desiredAssertionStatus()
13+
"on", "" -> true
14+
"off" -> false
15+
else -> error("System property '$DEBUG_PROPERTY_NAME' has unrecognized value '$value'")
16+
}
17+
}
18+
19+
private val COROUTINE_ID = AtomicLong()
20+
21+
/**
22+
* A coroutine dispatcher that executes initial continuation of the coroutine _right here_ in the current call-frame
23+
* and let the coroutine resume in whatever thread that is used by the corresponding suspending function, without
24+
* mandating any specific threading policy.
25+
*/
26+
public object Here : CoroutineDispatcher() {
27+
override fun isDispatchNeeded(context: CoroutineContext): Boolean = false
28+
override fun dispatch(context: CoroutineContext, block: Runnable) { throw UnsupportedOperationException() }
29+
}
30+
31+
/**
32+
* Creates context for the new coroutine with optional support for debugging facilities (when turned on).
33+
*
34+
* **Debugging facilities:** In debug mode every coroutine is assigned a unique consecutive identifier.
35+
* Every thread that executes a coroutine has its name modified to include the name and identifier of the
36+
* currently currently running coroutine.
37+
* When one coroutine is suspended and resumes another coroutine that is dispatched in the same thread,
38+
* then the thread name displays
39+
* the whole stack of coroutine descriptions that are being executed on this thread.
40+
*
41+
* Enable debugging facilities with "`kotlinx.coroutines.debug`" system property, use the following values:
42+
* * "`auto`" (default mode) -- enabled when assertions are enabled with "`-ea`" JVM option.
43+
* * "`on`" or empty string -- enabled.
44+
* * "`off`" -- disabled.
45+
*
46+
* Coroutine name can be explicitly assigned using [CoroutineName] context element.
47+
* The string "coroutine" is used as a default name.
48+
*/
49+
public fun newCoroutineContext(context: CoroutineContext): CoroutineContext =
50+
if (DEBUG) context + CoroutineId(COROUTINE_ID.incrementAndGet()) else context
51+
52+
/**
53+
* Executes a block using a given coroutine context.
54+
*/
55+
internal inline fun <T> withCoroutineContext(context: CoroutineContext, block: () -> T): T {
56+
val oldName = updateContext(context)
57+
try {
58+
return block()
59+
} finally {
60+
restoreContext(oldName)
61+
}
62+
}
63+
64+
@PublishedApi
65+
internal fun updateContext(context: CoroutineContext): String? {
66+
if (!DEBUG) return null
67+
val newId = context[CoroutineId] ?: return null
68+
val currentThread = Thread.currentThread()
69+
val oldName = currentThread.name
70+
val coroutineName = context[CoroutineName]?.name ?: "coroutine"
71+
currentThread.name = buildString(oldName.length + coroutineName.length + 10) {
72+
append(oldName)
73+
append(" @")
74+
append(coroutineName)
75+
append('#')
76+
append(newId.id)
77+
}
78+
return oldName
79+
}
80+
81+
@PublishedApi
82+
internal fun restoreContext(oldName: String?) {
83+
if (oldName != null) Thread.currentThread().name = oldName
84+
}
85+
86+
private class CoroutineId(val id: Long) : AbstractCoroutineContextElement(CoroutineId) {
87+
companion object Key : CoroutineContext.Key<CoroutineId>
88+
override fun toString(): String = "CoroutineId($id)"
89+
}

kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/CoroutineDispatcher.kt

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,9 @@ import kotlin.coroutines.CoroutineContext
1717
* a common pool of shared background threads.
1818
* This is an appropriate choice for compute-intensive coroutines that consume a lot of CPU resources.
1919
* * Private thread pools can be created with [newSingleThreadContext] and [newFixedThreadPoolContext].
20-
* * [currentCoroutineContext] -- inherits the context of the parent coroutine,
21-
* but throws [IllegalStateException] if used outside of coroutine. Use [currentCoroutineContextOrDefault]
22-
* if a default is needed when outside of coroutine.
23-
* This is an appropriate choice for libraries that need to inherit parent coroutine context.
24-
* * There are context implementations for UI libraries like `Swing` and `JavaFx` in separate modules.
20+
* * An arbitrary [Executor][java.util.concurrent.Executor] can be converted to dispatcher with [toCoroutineDispatcher] extension function.
2521
*
26-
* This class ensures that [currentCoroutineContext] is correctly transferred to a new thread and that
27-
* debugging facilities in [newCoroutineContext] function work properly.
22+
* This class ensures that debugging facilities in [newCoroutineContext] function work properly.
2823
*/
2924
public abstract class CoroutineDispatcher :
3025
AbstractCoroutineContextElement(ContinuationInterceptor), ContinuationInterceptor {
@@ -50,12 +45,12 @@ private class DispatchedContinuation<T>(
5045
val context = continuation.context
5146
if (dispatcher.isDispatchNeeded(context))
5247
dispatcher.dispatch(context, Runnable {
53-
withDefaultCoroutineContext(context) {
48+
withCoroutineContext(context) {
5449
continuation.resume(value)
5550
}
5651
})
5752
else
58-
withDefaultCoroutineContext(context) {
53+
withCoroutineContext(context) {
5954
continuation.resume(value)
6055
}
6156
}
@@ -64,12 +59,12 @@ private class DispatchedContinuation<T>(
6459
val context = continuation.context
6560
if (dispatcher.isDispatchNeeded(context))
6661
dispatcher.dispatch(context, Runnable {
67-
withDefaultCoroutineContext(context) {
62+
withCoroutineContext(context) {
6863
continuation.resumeWithException(exception)
6964
}
7065
})
7166
else
72-
withDefaultCoroutineContext(context) {
67+
withCoroutineContext(context) {
7368
continuation.resumeWithException(exception)
7469
}
7570
}

kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/CurrentCoroutineContext.kt

Lines changed: 0 additions & 131 deletions
This file was deleted.

kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/Deferred.kt

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,12 @@ public interface Deferred<out T> : Job {
2727

2828
/**
2929
* Starts new coroutine and returns its result as an implementation of [Deferred].
30-
* The running coroutine is cancelled when the resulting job is cancelled.
31-
* The [context] for the new coroutine must be explicitly specified and must include [CoroutineDispatcher] element.
30+
* The running coroutine is cancelled when the resulting object is [cancelled][Job.cancel].
31+
*
32+
* The [context] for the new coroutine must be explicitly specified.
3233
* See [CoroutineDispatcher] for the standard [context] implementations that are provided by `kotlinx.coroutines`.
33-
* The specified context is added to the context of the parent running coroutine (if any) inside which this function
34-
* is invoked. The [Job] of the resulting coroutine is a child of the job of the parent coroutine (if any).
34+
* The [context][CoroutineScope.context] of the parent coroutine from its [scope][CoroutineScope] may be used,
35+
* in which case the [Job] of the resulting coroutine is a child of the job of the parent coroutine.
3536
*/
3637
public fun <T> defer(context: CoroutineContext, block: suspend CoroutineScope.() -> T) : Deferred<T> =
3738
DeferredCoroutine<T>(newCoroutineContext(context)).also { block.startCoroutine(it, it) }

kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/Job.kt

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,14 @@ import kotlin.coroutines.CoroutineContext
1212

1313
/**
1414
* A background job. It has two states: _active_ (initial state) and _completed_ (final state).
15-
* It can be _cancelled_ at any time with [cancel] function that forces it to become completed immediately.
16-
* A job in the coroutine context represents the coroutine itself.
15+
*
16+
* A job can be _cancelled_ at any time with [cancel] function that forces it to become completed immediately.
17+
* A job in the coroutine [context][CoroutineScope.context] represents the coroutine itself.
1718
* A job is active while the coroutine is working and job's cancellation aborts the coroutine when
1819
* the coroutine is suspended on a _cancellable_ suspension point by throwing [CancellationException]
1920
* inside the coroutine.
2021
*
21-
* Jobs can have a _parent_. A job with a parent is cancelled when its parent completes.
22+
* A job can have a _parent_. A job with a parent is cancelled when its parent completes.
2223
*
2324
* All functions on this interface are thread-safe.
2425
*/

0 commit comments

Comments
 (0)