@@ -9,13 +9,95 @@ import kotlin.coroutines.*
9
9
import kotlin.jvm.*
10
10
11
11
@Suppress(" PrivatePropertyName" )
12
- private val UNDEFINED = Symbol (" UNDEFINED" )
12
+ @JvmField
13
+ internal val UNDEFINED = Symbol (" UNDEFINED" )
14
+
15
+ @NativeThreadLocal
16
+ internal object UndispatchedEventLoop {
17
+ data class State (
18
+ @JvmField var isActive : Boolean = false ,
19
+ @JvmField val threadLocalQueue : ArrayList <Runnable > = ArrayList ()
20
+ )
21
+
22
+ @JvmField
23
+ internal val state = CommonThreadLocal { State () }
24
+
25
+ fun dispatch (block : Runnable ) {
26
+ val state = state.get()
27
+ if (state.isActive) {
28
+ state.threadLocalQueue.add(block)
29
+ return
30
+ }
31
+
32
+ try {
33
+ state.isActive = true
34
+ block.run ()
35
+ while (! state.threadLocalQueue.isEmpty()) {
36
+ val element = state.threadLocalQueue.removeAt(state.threadLocalQueue.lastIndex)
37
+ element.run ()
38
+ }
39
+ } catch (e: Throwable ) {
40
+ /*
41
+ * This exception doesn't happen normally, only if user either submitted throwing runnable
42
+ * or if we have a bug in implementation. Anyway, reset state of the dispatcher to the initial.
43
+ */
44
+ state.threadLocalQueue.clear()
45
+ throw DispatchException (" Unexpected exception in undispatched event loop, clearing pending tasks" , e)
46
+ } finally {
47
+ state.isActive = false
48
+ }
49
+ }
50
+
51
+ inline fun execute (continuation : DispatchedContinuation <* >, contState : Any? , mode : Int , block : () -> Unit ) {
52
+ val state = state.get()
53
+ if (state.isActive) {
54
+ continuation._state = contState
55
+ continuation.resumeMode = mode
56
+ state.threadLocalQueue.add(continuation)
57
+ return
58
+ }
59
+
60
+ runLoop(state, block)
61
+ }
62
+
63
+ inline fun execute (task : DispatchedTask <* >, block : () -> Unit ) {
64
+ val state = state.get()
65
+ if (state.isActive) {
66
+ state.threadLocalQueue.add(task)
67
+ return
68
+ }
69
+
70
+ runLoop(state, block)
71
+ }
72
+
73
+ inline fun runLoop (state : State , block : () -> Unit ) {
74
+ try {
75
+ state.isActive = true
76
+ block()
77
+ while (! state.threadLocalQueue.isEmpty()) {
78
+ val element = state.threadLocalQueue.removeAt(state.threadLocalQueue.lastIndex)
79
+ element.run ()
80
+ }
81
+ } catch (e: Throwable ) {
82
+ /*
83
+ * This exception doesn't happen normally, only if user either submitted throwing runnable
84
+ * or if we have a bug in implementation. Anyway, reset state of the dispatcher to the initial.
85
+ */
86
+ state.threadLocalQueue.clear()
87
+ throw DispatchException (" Unexpected exception in undispatched event loop, clearing pending tasks" , e)
88
+ } finally {
89
+ state.isActive = false
90
+ }
91
+ }
92
+ }
13
93
14
94
internal class DispatchedContinuation <in T >(
15
95
@JvmField val dispatcher : CoroutineDispatcher ,
16
96
@JvmField val continuation : Continuation <T >
17
97
) : Continuation<T> by continuation, DispatchedTask<T> {
18
- private var _state : Any? = UNDEFINED
98
+ @JvmField
99
+ @Suppress(" PropertyName" )
100
+ internal var _state : Any? = UNDEFINED
19
101
public override var resumeMode: Int = 0
20
102
21
103
override fun takeState (): Any? {
@@ -30,39 +112,48 @@ internal class DispatchedContinuation<in T>(
30
112
31
113
override fun resumeWith (result : Result <T >) {
32
114
val context = continuation.context
115
+ val state = result.toState()
33
116
if (dispatcher.isDispatchNeeded(context)) {
34
- _state = result.toState()
117
+ _state = state
35
118
resumeMode = MODE_ATOMIC_DEFAULT
36
119
dispatcher.dispatch(context, this )
37
120
} else {
38
- resumeUndispatchedWith(result)
121
+ UndispatchedEventLoop .execute(this , state, MODE_ATOMIC_DEFAULT ) {
122
+ withCoroutineContext(this .context) {
123
+ continuation.resumeWith(result)
124
+ }
125
+ }
39
126
}
40
127
}
41
128
42
129
@Suppress(" NOTHING_TO_INLINE" ) // we need it inline to save us an entry on the stack
43
130
inline fun resumeCancellable (value : T ) {
44
- val context = continuation.context
45
131
if (dispatcher.isDispatchNeeded(context)) {
46
132
_state = value
47
133
resumeMode = MODE_CANCELLABLE
48
134
dispatcher.dispatch(context, this )
49
135
} else {
50
- if (! resumeCancelled()) {
51
- resumeUndispatched(value)
136
+ UndispatchedEventLoop .execute(this , value, MODE_CANCELLABLE ) {
137
+ if (! resumeCancelled()) {
138
+ resumeUndispatched(value)
139
+ }
52
140
}
53
141
}
54
142
}
55
143
56
144
@Suppress(" NOTHING_TO_INLINE" ) // we need it inline to save us an entry on the stack
57
145
inline fun resumeCancellableWithException (exception : Throwable ) {
58
146
val context = continuation.context
147
+ val state = CompletedExceptionally (exception)
59
148
if (dispatcher.isDispatchNeeded(context)) {
60
149
_state = CompletedExceptionally (exception)
61
150
resumeMode = MODE_CANCELLABLE
62
151
dispatcher.dispatch(context, this )
63
152
} else {
64
- if (! resumeCancelled()) {
65
- resumeUndispatchedWithException(exception)
153
+ UndispatchedEventLoop .execute(this , state, MODE_CANCELLABLE ) {
154
+ if (! resumeCancelled()) {
155
+ resumeUndispatchedWithException(exception)
156
+ }
66
157
}
67
158
}
68
159
}
@@ -78,13 +169,6 @@ internal class DispatchedContinuation<in T>(
78
169
return false
79
170
}
80
171
81
- @Suppress(" NOTHING_TO_INLINE" ) // we need it inline to save us an entry on the stack
82
- inline fun resumeUndispatchedWith (result : Result <T >) {
83
- withCoroutineContext(context) {
84
- continuation.resumeWith(result)
85
- }
86
- }
87
-
88
172
@Suppress(" NOTHING_TO_INLINE" ) // we need it inline to save us an entry on the stack
89
173
inline fun resumeUndispatched (value : T ) {
90
174
withCoroutineContext(context) {
@@ -182,12 +266,15 @@ internal fun <T> DispatchedTask<T>.dispatch(mode: Int = MODE_CANCELLABLE) {
182
266
useMode = MODE_UNDISPATCHED
183
267
}
184
268
}
185
- // slow-path - use delegate
186
- val state = takeState()
187
- val exception = getExceptionalResult(state)
188
- if (exception != null ) {
189
- delegate.resumeWithExceptionMode(exception, useMode)
190
- } else {
191
- delegate.resumeMode(getSuccessfulResult(state), useMode)
269
+
270
+ UndispatchedEventLoop .execute(this ) {
271
+ // slow-path - use delegate
272
+ val state = takeState()
273
+ val exception = getExceptionalResult(state)
274
+ if (exception != null ) {
275
+ delegate.resumeWithExceptionMode(exception, useMode)
276
+ } else {
277
+ delegate.resumeMode(getSuccessfulResult(state), useMode)
278
+ }
192
279
}
193
280
}
0 commit comments