8
8
9
9
package kotlinx.coroutines
10
10
11
- import java.util.concurrent.locks.*
12
11
import kotlin.contracts.*
13
12
import kotlin.coroutines.*
14
13
15
14
/* *
16
- * Runs a new coroutine and **blocks** the current thread _interruptibly_ until its completion.
17
- * This function should not be used from a coroutine. It is designed to bridge regular blocking code
18
- * to libraries that are written in suspending style, to be used in `main` functions and in tests.
15
+ * Creates a new coroutine and **blocks** the current thread _interruptibly_ to immediately execute
16
+ * it.
19
17
*
20
- * The default [CoroutineDispatcher] for this builder is an internal implementation of event loop that processes continuations
21
- * in this blocked thread until the completion of this coroutine.
22
- * See [CoroutineDispatcher] for the other implementations that are provided by `kotlinx.coroutines`.
18
+ * [runBlocking] allows regular blocking code to call libraries that are written using coroutines.
19
+ * [runBlocking] should be called by a test case or in a program's `main()` to "boostrap" into
20
+ * coroutines.
21
+ *
22
+ * [runBlocking] should never be called from _in_ a coroutine. Blocking a thread is unnecessary
23
+ * and inefficient. When a function is a `suspend` function, call [coroutineScope] rather than
24
+ * [runBlocking] in order to introduce parallelism.
25
+ *
26
+ * [runBlocking] uses its input [context] as though it were the [CoroutineContext] that
27
+ * constructed the blocking coroutine. Unlike a child coroutine built with [launch] or [async],
28
+ * a [runBlocking] coroutine gets its [CoroutineStartInterceptor] and [ContinuationInterceptor]
29
+ * from its parameter, rather than from the running context.
23
30
*
24
31
* When [CoroutineDispatcher] is explicitly specified in the [context], then the new coroutine runs in the context of
25
32
* the specified dispatcher while the current thread is blocked. If the specified dispatcher is an event loop of another `runBlocking`,
26
33
* then this invocation uses the outer event loop.
27
34
*
28
- * If this blocked thread is interrupted (see [Thread.interrupt]), then the coroutine job is cancelled and
29
- * this `runBlocking` invocation throws [InterruptedException].
35
+ * If [context] does not contain a [CoroutineDispatcher], [runBlocking] will include add an event
36
+ * loop [CoroutineDispatcher] to the [CoroutineContext], and execute continuations using the
37
+ * blocked thread until [block] returns.
38
+ *
39
+ * When a [CoroutineStartInterceptor] is explicitly specified in the [context], it intercepts the
40
+ * construction of _this_ coroutine. This is a special case that allows the thread calling
41
+ * `runBlocking` to intercept the coroutine start before it blocks the thread.
42
+ *
43
+ * If the blocked thread is interrupted (see [Thread.interrupt]), this coroutine's job will be
44
+ * cancelled. If the cancellation by interrupt succeeds, the running [runBlocking] function call
45
+ * will complete by throwing [InterruptedException].
30
46
*
31
- * See [newCoroutineContext][CoroutineScope.newCoroutineContext] for a description of debugging facilities that are available
32
- * for a newly created coroutine.
47
+ * See [newCoroutineContext][CoroutineScope.newCoroutineContext] for a description of debugging
48
+ * facilities that are available for a newly created coroutine.
33
49
*
34
50
* @param context the context of the coroutine. The default value is an event loop on the current thread.
35
51
* @param block the coroutine code.
@@ -40,19 +56,26 @@ public actual fun <T> runBlocking(context: CoroutineContext, block: suspend Coro
40
56
callsInPlace(block, InvocationKind .EXACTLY_ONCE )
41
57
}
42
58
val currentThread = Thread .currentThread()
43
- val contextInterceptor = context[ContinuationInterceptor ]
59
+ val continuationInterceptor = context[ContinuationInterceptor ]
60
+ val coroutineStartInterceptor = context[CoroutineStartInterceptor ]
44
61
val eventLoop: EventLoop ?
45
62
val newContext: CoroutineContext
46
- if (contextInterceptor == null ) {
63
+ if (continuationInterceptor == null ) {
47
64
// create or use private event loop if no dispatcher is specified
48
65
eventLoop = ThreadLocalEventLoop .eventLoop
49
- newContext = GlobalScope .newCoroutineContext(context + eventLoop)
66
+ newContext = newCoroutineContext(
67
+ callingContext = context + eventLoop,
68
+ addedContext = EmptyCoroutineContext
69
+ )
50
70
} else {
51
71
// See if context's interceptor is an event loop that we shall use (to support TestContext)
52
72
// or take an existing thread-local event loop if present to avoid blocking it (but don't create one)
53
- eventLoop = (contextInterceptor as ? EventLoop )?.takeIf { it.shouldBeProcessedFromContext() }
73
+ eventLoop = (continuationInterceptor as ? EventLoop )?.takeIf { it.shouldBeProcessedFromContext() }
54
74
? : ThreadLocalEventLoop .currentOrNull()
55
- newContext = GlobalScope .newCoroutineContext(context)
75
+ newContext = newCoroutineContext(
76
+ callingContext = context,
77
+ addedContext = EmptyCoroutineContext
78
+ )
56
79
}
57
80
val coroutine = BlockingCoroutine <T >(newContext, currentThread, eventLoop)
58
81
coroutine.start(CoroutineStart .DEFAULT , coroutine, block)
0 commit comments