16
16
17
17
package kotlinx.coroutines.experimental
18
18
19
+ import kotlinx.coroutines.experimental.internal.*
19
20
import kotlin.coroutines.experimental.AbstractCoroutineContextElement
20
21
import kotlin.coroutines.experimental.Continuation
21
22
import kotlin.coroutines.experimental.ContinuationInterceptor
@@ -109,70 +110,65 @@ public actual abstract class CoroutineDispatcher actual constructor() :
109
110
*/
110
111
public actual typealias Runnable = java.lang.Runnable
111
112
112
- // named class for ease of debugging, better stack-traces and optimize the number of anonymous classes
113
- // note that CancellableContinuationImpl directly works as DispatchTask
114
- internal class DispatchTask <in T >(
115
- private val continuation : Continuation <T >,
116
- private val value : Any? , // T | Throwable
117
- private val exception : Boolean ,
118
- private val cancellable : Boolean
119
- ) : Runnable {
120
- @Suppress(" UNCHECKED_CAST" )
121
- override fun run () {
122
- try {
123
- val context = continuation.context
124
- val job = if (cancellable) context[Job ] else null
125
- withCoroutineContext(context) {
126
- when {
127
- job != null && ! job.isActive -> continuation.resumeWithException(job.getCancellationException())
128
- exception -> continuation.resumeWithException(value as Throwable )
129
- else -> continuation.resume(value as T )
130
- }
131
- }
132
- } catch (e: Throwable ) {
133
- throw RuntimeException (" Unexpected exception running $this " , e)
134
- }
135
- }
136
-
137
- override fun toString (): String =
138
- " DispatchTask[${continuation.toDebugString()} , cancellable=$cancellable , value=${value.toSafeString()} ]"
139
- }
113
+ @Suppress(" PrivatePropertyName" )
114
+ private val UNDEFINED = Symbol (" UNDEFINED" )
140
115
141
116
internal class DispatchedContinuation <in T >(
142
117
@JvmField val dispatcher : CoroutineDispatcher ,
143
118
@JvmField val continuation : Continuation <T >
144
- ): Continuation<T> by continuation {
119
+ ): Continuation<T> by continuation, DispatchedTask<T> {
120
+ private var _state : Any? = UNDEFINED
121
+ public override var resumeMode: Int = 0
122
+
123
+ override fun takeState (): Any? {
124
+ val state = _state
125
+ check(state != = UNDEFINED ) // fail-fast if repeatedly invoked
126
+ _state = UNDEFINED
127
+ return state
128
+ }
129
+
130
+ override val delegate: Continuation <T >
131
+ get() = this
132
+
145
133
override fun resume (value : T ) {
146
134
val context = continuation.context
147
- if (dispatcher.isDispatchNeeded(context))
148
- dispatcher.dispatch(context, DispatchTask (continuation, value, exception = false , cancellable = false ))
149
- else
135
+ if (dispatcher.isDispatchNeeded(context)) {
136
+ _state = value
137
+ resumeMode = MODE_ATOMIC_DEFAULT
138
+ dispatcher.dispatch(context, this )
139
+ } else
150
140
resumeUndispatched(value)
151
141
}
152
142
153
143
override fun resumeWithException (exception : Throwable ) {
154
144
val context = continuation.context
155
- if (dispatcher.isDispatchNeeded(context))
156
- dispatcher.dispatch(context, DispatchTask (continuation, exception, exception = true , cancellable = false ))
157
- else
145
+ if (dispatcher.isDispatchNeeded(context)) {
146
+ _state = CompletedExceptionally (exception)
147
+ resumeMode = MODE_ATOMIC_DEFAULT
148
+ dispatcher.dispatch(context, this )
149
+ } else
158
150
resumeUndispatchedWithException(exception)
159
151
}
160
152
161
153
@Suppress(" NOTHING_TO_INLINE" ) // we need it inline to save us an entry on the stack
162
154
inline fun resumeCancellable (value : T ) {
163
155
val context = continuation.context
164
- if (dispatcher.isDispatchNeeded(context))
165
- dispatcher.dispatch(context, DispatchTask (continuation, value, exception = false , cancellable = true ))
166
- else
156
+ if (dispatcher.isDispatchNeeded(context)) {
157
+ _state = value
158
+ resumeMode = MODE_CANCELLABLE
159
+ dispatcher.dispatch(context, this )
160
+ } else
167
161
resumeUndispatched(value)
168
162
}
169
163
170
164
@Suppress(" NOTHING_TO_INLINE" ) // we need it inline to save us an entry on the stack
171
165
inline fun resumeCancellableWithException (exception : Throwable ) {
172
166
val context = continuation.context
173
- if (dispatcher.isDispatchNeeded(context))
174
- dispatcher.dispatch(context, DispatchTask (continuation, exception, exception = true , cancellable = true ))
175
- else
167
+ if (dispatcher.isDispatchNeeded(context)) {
168
+ _state = CompletedExceptionally (exception)
169
+ resumeMode = MODE_CANCELLABLE
170
+ dispatcher.dispatch(context, this )
171
+ } else
176
172
resumeUndispatchedWithException(exception)
177
173
}
178
174
@@ -193,7 +189,9 @@ internal class DispatchedContinuation<in T>(
193
189
// used by "yield" implementation
194
190
internal fun dispatchYield (value : T ) {
195
191
val context = continuation.context
196
- dispatcher.dispatch(context, DispatchTask (continuation, value,false , true ))
192
+ _state = value
193
+ resumeMode = MODE_CANCELLABLE
194
+ dispatcher.dispatch(context, this )
197
195
}
198
196
199
197
override fun toString (): String =
@@ -219,3 +217,70 @@ internal fun <T> Continuation<T>.resumeDirectWithException(exception: Throwable)
219
217
is DispatchedContinuation -> continuation.resumeWithException(exception)
220
218
else -> resumeWithException(exception)
221
219
}
220
+
221
+ /* *
222
+ * @suppress **This is unstable API and it is subject to change.**
223
+ */
224
+ public interface DispatchedTask <in T > : Runnable {
225
+ public val delegate: Continuation <T >
226
+ public val resumeMode: Int get() = MODE_CANCELLABLE
227
+
228
+ public fun takeState (): Any?
229
+
230
+ @Suppress(" UNCHECKED_CAST" )
231
+ public fun <T > getSuccessfulResult (state : Any? ): T =
232
+ state as T
233
+
234
+ public fun getExceptionalResult (state : Any? ): Throwable ? =
235
+ (state as ? CompletedExceptionally )?.exception
236
+
237
+ public override fun run () {
238
+ try {
239
+ val delegate = delegate as DispatchedContinuation <T >
240
+ val continuation = delegate.continuation
241
+ val context = continuation.context
242
+ val job = if (resumeMode.isCancellableMode) context[Job ] else null
243
+ val state = takeState() // NOTE: Must take state in any case, even if cancelled
244
+ withCoroutineContext(context) {
245
+ if (job != null && ! job.isActive)
246
+ continuation.resumeWithException(job.getCancellationException())
247
+ else {
248
+ val exception = getExceptionalResult(state)
249
+ if (exception != null )
250
+ continuation.resumeWithException(exception)
251
+ else
252
+ continuation.resume(getSuccessfulResult(state))
253
+ }
254
+ }
255
+ } catch (e: Throwable ) {
256
+ throw RuntimeException (" Unexpected exception running $this " , e)
257
+ }
258
+ }
259
+ }
260
+
261
+ /* *
262
+ * @suppress **This is unstable API and it is subject to change.**
263
+ */
264
+ public fun <T > DispatchedTask<T>.dispatch (mode : Int = MODE_CANCELLABLE ) {
265
+ var useMode = mode
266
+ val delegate = this .delegate
267
+ if (mode.isDispatchedMode && delegate is DispatchedContinuation <* > && mode.isCancellableMode == resumeMode.isCancellableMode) {
268
+ // dispatch directly using this instance's Runnable implementation
269
+ val dispatcher = delegate.dispatcher
270
+ val context = delegate.context
271
+ if (dispatcher.isDispatchNeeded(context)) {
272
+ dispatcher.dispatch(context, this )
273
+ return // and that's it -- dispatched via fast-path
274
+ } else {
275
+ useMode = MODE_UNDISPATCHED
276
+ }
277
+ }
278
+ // slow-path - use delegate
279
+ val state = takeState()
280
+ val exception = getExceptionalResult(state)
281
+ if (exception != null ) {
282
+ delegate.resumeWithExceptionMode(exception, useMode)
283
+ } else {
284
+ delegate.resumeMode(getSuccessfulResult(state), useMode)
285
+ }
286
+ }
0 commit comments