@@ -35,35 +35,42 @@ import kotlin.coroutines.*
35
35
public fun <T > runBlocking (context : CoroutineContext = EmptyCoroutineContext , block : suspend CoroutineScope .() -> T ): T {
36
36
val currentThread = Thread .currentThread()
37
37
val contextInterceptor = context[ContinuationInterceptor ]
38
- val privateEventLoop = contextInterceptor == null // create private event loop if no dispatcher is specified
39
- val eventLoop = if (privateEventLoop) BlockingEventLoop (currentThread) else contextInterceptor as ? EventLoop
40
- val newContext = GlobalScope .newCoroutineContext(
41
- if (privateEventLoop) context + (eventLoop as ContinuationInterceptor ) else context
42
- )
43
- val coroutine = BlockingCoroutine <T >(newContext, currentThread, eventLoop, privateEventLoop)
38
+ val eventLoop: EventLoop ?
39
+ val topLevelEventLoop: Boolean
40
+ val newContext: CoroutineContext
41
+ if (contextInterceptor == null ) {
42
+ // create/use private event loop if no dispatcher is specified
43
+ val currentEventLoop: EventLoop ? = ThreadEventLoop .ref.get()
44
+ val usedEventEventLoop = currentEventLoop ? : BlockingEventLoop (currentThread).also {
45
+ ThreadEventLoop .ref.set(it)
46
+ }
47
+ eventLoop = usedEventEventLoop
48
+ topLevelEventLoop = currentEventLoop == null
49
+ newContext = GlobalScope .newCoroutineContext(context + usedEventEventLoop)
50
+ } else {
51
+ eventLoop = contextInterceptor as ? EventLoop
52
+ topLevelEventLoop = false
53
+ newContext = GlobalScope .newCoroutineContext(context)
54
+ }
55
+ val coroutine = BlockingCoroutine <T >(newContext, currentThread, eventLoop)
44
56
coroutine.start(CoroutineStart .DEFAULT , coroutine, block)
45
- return coroutine.joinBlocking()
57
+ return coroutine.joinBlocking(topLevelEventLoop )
46
58
}
47
59
48
60
private class BlockingCoroutine <T >(
49
61
parentContext : CoroutineContext ,
50
62
private val blockedThread : Thread ,
51
- private val eventLoop : EventLoop ? ,
52
- private val privateEventLoop : Boolean
63
+ private val eventLoop : EventLoop ?
53
64
) : AbstractCoroutine<T>(parentContext, true ) {
54
- init {
55
- if (privateEventLoop) require(eventLoop is BlockingEventLoop )
56
- }
57
-
58
65
override fun onCompletionInternal (state : Any? , mode : Int , suppressed : Boolean ) {
59
66
// wake up blocked thread
60
67
if (Thread .currentThread() != blockedThread)
61
68
LockSupport .unpark(blockedThread)
62
69
}
63
70
64
71
@Suppress(" UNCHECKED_CAST" )
65
- fun joinBlocking (): T {
66
- timeSource.registerTimeLoopThread()
72
+ fun joinBlocking (topLevelEventLoop : Boolean ): T {
73
+ if (topLevelEventLoop) timeSource.registerTimeLoopThread()
67
74
while (true ) {
68
75
@Suppress(" DEPRECATION" )
69
76
if (Thread .interrupted()) throw InterruptedException ().also { cancel(it) }
@@ -72,14 +79,18 @@ private class BlockingCoroutine<T>(
72
79
if (isCompleted) break
73
80
timeSource.parkNanos(this , parkNanos)
74
81
}
75
- // process queued events (that could have been added after last processNextEvent and before cancel
76
- if (privateEventLoop) (eventLoop as BlockingEventLoop ).apply {
77
- // We exit the "while" loop above when this coroutine's state "isCompleted",
78
- // Here we should signal that BlockingEventLoop should not accept any more tasks
79
- isCompleted = true
80
- shutdown()
82
+ if (topLevelEventLoop) {
83
+ // Clean up reference here -- this event loop is shutting down
84
+ ThreadEventLoop .ref.set(null )
85
+ // process queued events (that could have been added after last processNextEvent and before cancel
86
+ (eventLoop as BlockingEventLoop ).apply {
87
+ // We exit the "while" loop above when this coroutine's state "isCompleted",
88
+ // Here we should signal that BlockingEventLoop should not accept any more tasks
89
+ isCompleted = true
90
+ shutdown()
91
+ }
92
+ timeSource.unregisterTimeLoopThread()
81
93
}
82
- timeSource.unregisterTimeLoopThread()
83
94
// now return result
84
95
val state = this .state.unboxState()
85
96
(state as ? CompletedExceptionally )?.let { throw it.cause }
0 commit comments