1
1
package kotlinx.coroutines.experimental
2
2
3
3
import java.util.concurrent.locks.LockSupport
4
- import kotlin.coroutines.Continuation
5
- import kotlin.coroutines.CoroutineContext
6
- import kotlin.coroutines.startCoroutine
7
- import kotlin.coroutines.suspendCoroutine
4
+ import kotlin.coroutines.*
8
5
9
6
// --------------- basic coroutine builders ---------------
10
7
@@ -21,29 +18,30 @@ import kotlin.coroutines.suspendCoroutine
21
18
*
22
19
* See [newCoroutineContext] for a description of debugging facilities that are available for newly created coroutine.
23
20
*/
24
- fun launch (context : CoroutineContext , block : suspend () -> Unit ): Job =
25
- StandaloneCoroutine (newCoroutineContext(context)).also { block.startCoroutine(it) }
21
+ fun launch (context : CoroutineContext , block : suspend CoroutineScope . () -> Unit ): Job =
22
+ StandaloneCoroutine (newCoroutineContext(context)).also { block.startCoroutine(it, it ) }
26
23
27
24
/* *
28
25
* Calls the specified suspending block with a given coroutine context, suspends until it completes, and returns
29
26
* the result. It immediately applies dispatcher from the new context, shifting execution of the block into the
30
27
* different thread inside the block, and back when it completes.
31
28
* The specified [context] is merged onto the current coroutine context.
32
29
*/
33
- public suspend fun <T > run (context : CoroutineContext , block : suspend () -> T ): T =
30
+ public suspend fun <T > run (context : CoroutineContext , block : suspend CoroutineScope . () -> T ): T =
34
31
suspendCoroutine { cont ->
35
- block.startCoroutine(object : Continuation <T > by cont {
36
- override val context: CoroutineContext = cont.context + context
37
- })
32
+ // new don't invoke `newCoroutineContext`, but consider this being the same coroutine in the new context
33
+ InnerCoroutine (cont.context + context, cont).also { block.startCoroutine(it, it) }
38
34
}
39
35
40
36
/* *
41
37
* Runs new coroutine and *blocks* current thread *interruptibly* until its completion.
42
- * This function should not be used from coroutine. It is designed to bridge regular code blocking code
43
- * to libraries that are written in suspending style.
44
- * The [context] for the new coroutine must be explicitly specified and must include [CoroutineDispatcher] element.
45
- * See [CoroutineDispatcher] for the standard [context] implementations that are provided by `kotlinx.coroutines`.
46
- * The specified context is added to the context of the parent running coroutine (if any) inside which this function
38
+ * This function should not be used from coroutine. It is designed to bridge regular blocking code
39
+ * to libraries that are written in suspending style, to be used in `main` functions and in tests.
40
+ *
41
+ * The default [CoroutineDispatcher] for this builder in an implementation of [EventLoop] that processes continuations
42
+ * in this blocked thread until the completion of this coroutine.
43
+ * 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
47
45
* is invoked. The [Job] of the resulting coroutine is a child of the job of the parent coroutine (if any).
48
46
*
49
47
* If this blocked thread is interrupted (see [Thread.interrupt]), then the coroutine job is cancelled and
@@ -52,26 +50,45 @@ public suspend fun <T> run(context: CoroutineContext, block: suspend () -> T): T
52
50
* See [newCoroutineContext] for a description of debugging facilities that are available for newly created coroutine.
53
51
*/
54
52
@Throws(InterruptedException ::class )
55
- public fun <T > runBlocking (context : CoroutineContext , block : suspend () -> T ): T =
56
- BlockingCoroutine <T >(newCoroutineContext(context)).also { block.startCoroutine(it) }.joinBlocking()
53
+ public fun <T > runBlocking (context : CoroutineContext = EmptyCoroutineContext , block : suspend CoroutineScope .() -> T ): T {
54
+ val currentThread = Thread .currentThread()
55
+ val privateEventLoop = if (context[ContinuationInterceptor ] as ? CoroutineDispatcher == null )
56
+ EventLoopImpl (currentThread) else null
57
+ val newContext = newCoroutineContext(context + (privateEventLoop ? : EmptyCoroutineContext ))
58
+ val coroutine = BlockingCoroutine <T >(newContext, currentThread, privateEventLoop != null )
59
+ privateEventLoop?.initParentJob(coroutine)
60
+ block.startCoroutine(coroutine, coroutine)
61
+ return coroutine.joinBlocking()
62
+ }
57
63
58
64
// --------------- implementation ---------------
59
65
60
66
private class StandaloneCoroutine (
61
- val parentContext : CoroutineContext
62
- ) : AbstractCoroutine<Unit>(parentContext ) {
63
- init { initParentJob(parentContext [Job ]) }
67
+ val newContext : CoroutineContext
68
+ ) : AbstractCoroutine<Unit>(newContext ) {
69
+ init { initParentJob(newContext [Job ]) }
64
70
65
71
override fun afterCompletion (state : Any? ) {
66
- // note the use of the parent context below!
67
- if (state is CompletedExceptionally ) handleCoroutineException(parentContext , state.cancelReason)
72
+ // note the use of the parent's job context below!
73
+ if (state is CompletedExceptionally ) handleCoroutineException(newContext , state.cancelReason)
68
74
}
69
75
}
70
76
71
- private class BlockingCoroutine <T >(parentContext : CoroutineContext ) : AbstractCoroutine<T>(parentContext) {
72
- val blockedThread: Thread = Thread .currentThread()
77
+ private class InnerCoroutine <T >(
78
+ override val context : CoroutineContext ,
79
+ continuation : Continuation <T >
80
+ ) : Continuation<T> by continuation, CoroutineScope {
81
+ override val isActive: Boolean = context[Job ]?.isActive ? : true
82
+ }
83
+
84
+ private class BlockingCoroutine <T >(
85
+ newContext : CoroutineContext ,
86
+ val blockedThread : Thread ,
87
+ val hasPrivateEventLoop : Boolean
88
+ ) : AbstractCoroutine<T>(newContext) {
89
+ val eventLoop: EventLoop ? = newContext[ContinuationInterceptor ] as ? EventLoop
73
90
74
- init { initParentJob(parentContext [Job ]) }
91
+ init { initParentJob(newContext [Job ]) }
75
92
76
93
override fun afterCompletion (state : Any? ) {
77
94
LockSupport .unpark(blockedThread)
@@ -81,8 +98,14 @@ private class BlockingCoroutine<T>(parentContext: CoroutineContext) : AbstractCo
81
98
fun joinBlocking (): T {
82
99
while (isActive) {
83
100
if (Thread .interrupted()) throw InterruptedException ().also { cancel(it) }
84
- LockSupport .park(this )
101
+ if (eventLoop == null || ! eventLoop.processNextEvent())
102
+ LockSupport .park(this )
103
+ }
104
+ // process remaining events (that could have been added after last processNextEvent and before cancel
105
+ if (hasPrivateEventLoop) {
106
+ while (eventLoop!! .processNextEvent()) { /* just spin */ }
85
107
}
108
+ // now return result
86
109
val state = getState()
87
110
(state as ? CompletedExceptionally )?.let { throw it.exception }
88
111
return state as T
0 commit comments