@@ -10,10 +10,18 @@ import kotlin.coroutines.*
10
10
import kotlin.coroutines.intrinsics.*
11
11
import kotlin.jvm.*
12
12
13
+ private const val DECISION_SHIFT = 29
14
+ private const val INDEX_MASK = (1 shl DECISION_SHIFT ) - 1
15
+ private const val NO_INDEX = INDEX_MASK
13
16
private const val UNDECIDED = 0
14
17
private const val SUSPENDED = 1
15
18
private const val RESUMED = 2
16
19
20
+ private inline val Int .decision get() = this shr DECISION_SHIFT
21
+ private inline val Int .index get() = this and INDEX_MASK
22
+ @Suppress(" NOTHING_TO_INLINE" )
23
+ private inline fun construct (decision : Int , index : Int ) = (decision shl DECISION_SHIFT ) + index
24
+
17
25
@JvmField
18
26
internal val RESUME_TOKEN = Symbol (" RESUME_TOKEN" )
19
27
@@ -55,9 +63,9 @@ internal open class CancellableContinuationImpl<in T>(
55
63
| RESUMED |
56
64
+-----------+
57
65
58
- Note: both tryResume and trySuspend can be invoked at most once, first invocation wins
66
+ Note: both tryResume and trySuspend can be invoked at most once, first invocation wins.
59
67
*/
60
- private val _decision = atomic(UNDECIDED )
68
+ private val _decisionAndIndex = atomic(construct( UNDECIDED , NO_INDEX ) )
61
69
62
70
/*
63
71
=== Internal states ===
@@ -122,7 +130,7 @@ internal open class CancellableContinuationImpl<in T>(
122
130
detachChild()
123
131
return false
124
132
}
125
- _decision .value = UNDECIDED
133
+ _decisionAndIndex .value = construct( UNDECIDED , NO_INDEX )
126
134
_state .value = Active
127
135
return true
128
136
}
@@ -172,10 +180,11 @@ internal open class CancellableContinuationImpl<in T>(
172
180
_state .loop { state ->
173
181
if (state !is NotCompleted ) return false // false if already complete or cancelling
174
182
// Active -- update to final state
175
- val update = CancelledContinuation (this , cause, handled = state is CancelHandler )
183
+ val update = CancelledContinuation (this , cause, handled = state is CancelHandler || state is Segment < * > )
176
184
if (! _state .compareAndSet(state, update)) return @loop // retry on cas failure
177
185
// Invoke cancel handler if it was present
178
186
(state as ? CancelHandler )?.let { callCancelHandler(it, cause) }
187
+ (state as ? Segment <* >)?.let { callSegmentOnCancellation(it, cause) }
179
188
// Complete state update
180
189
detachChildIfNonResuable()
181
190
dispatchResume(resumeMode) // no need for additional cancellation checks
@@ -212,6 +221,13 @@ internal open class CancellableContinuationImpl<in T>(
212
221
fun callCancelHandler (handler : CancelHandler , cause : Throwable ? ) =
213
222
callCancelHandlerSafely { handler.invoke(cause) }
214
223
224
+ private fun callSegmentOnCancellation (segment : Segment <* >, cause : Throwable ? ) {
225
+ val index = _decisionAndIndex .value.index
226
+ check(index != NO_INDEX ) { " The index for segment.invokeOnCancellation(..) is broken" }
227
+ callCancelHandlerSafely { segment.invokeOnCancellation(index, cause) }
228
+ }
229
+
230
+
215
231
fun callOnCancellation (onCancellation : (cause: Throwable ) -> Unit , cause : Throwable ) {
216
232
try {
217
233
onCancellation.invoke(cause)
@@ -231,19 +247,19 @@ internal open class CancellableContinuationImpl<in T>(
231
247
parent.getCancellationException()
232
248
233
249
private fun trySuspend (): Boolean {
234
- _decision .loop { decision ->
235
- when (decision) {
236
- UNDECIDED -> if (this ._decision .compareAndSet(UNDECIDED , SUSPENDED )) return true
250
+ _decisionAndIndex .loop { cur ->
251
+ when (cur. decision) {
252
+ UNDECIDED -> if (this ._decisionAndIndex .compareAndSet(cur, construct( SUSPENDED , cur.index) )) return true
237
253
RESUMED -> return false
238
254
else -> error(" Already suspended" )
239
255
}
240
256
}
241
257
}
242
258
243
259
private fun tryResume (): Boolean {
244
- _decision .loop { decision ->
245
- when (decision) {
246
- UNDECIDED -> if (this ._decision .compareAndSet(UNDECIDED , RESUMED )) return true
260
+ _decisionAndIndex .loop { cur ->
261
+ when (cur. decision) {
262
+ UNDECIDED -> if (this ._decisionAndIndex .compareAndSet(cur, construct( RESUMED , cur.index) )) return true
247
263
SUSPENDED -> return false
248
264
else -> error(" Already resumed" )
249
265
}
@@ -328,14 +344,39 @@ internal open class CancellableContinuationImpl<in T>(
328
344
override fun resume (value : T , onCancellation : ((cause: Throwable ) -> Unit )? ) =
329
345
resumeImpl(value, resumeMode, onCancellation)
330
346
347
+ /* *
348
+ * An optimized version for the code below that does not allocate
349
+ * a cancellation handler object and efficiently stores the specified
350
+ * [segment] and [index] in this [CancellableContinuationImpl].
351
+ * ```
352
+ * invokeOnCancellation { cause ->
353
+ * segment.invokeOnCancellation(index, cause)
354
+ * }
355
+ * ```
356
+ */
357
+ internal fun invokeOnCancellation (segment : Segment <* >, index : Int ) {
358
+ _decisionAndIndex .update {
359
+ check(it.index == NO_INDEX ) {
360
+ " invokeOnCancellation should be invoked at most once"
361
+ }
362
+ construct(it.decision, index)
363
+ }
364
+ invokeOnCancellationImpl(segment)
365
+ }
366
+
331
367
public override fun invokeOnCancellation (handler : CompletionHandler ) {
332
368
val cancelHandler = makeCancelHandler(handler)
369
+ invokeOnCancellationImpl(cancelHandler)
370
+ }
371
+
372
+ private fun invokeOnCancellationImpl (handler : Any ) {
373
+ assert { handler is CancelHandler || handler is Segment <* > }
333
374
_state .loop { state ->
334
375
when (state) {
335
376
is Active -> {
336
- if (_state .compareAndSet(state, cancelHandler )) return // quit on cas success
377
+ if (_state .compareAndSet(state, handler )) return // quit on cas success
337
378
}
338
- is CancelHandler -> multipleHandlersError(handler, state)
379
+ is CancelHandler , is Segment < * > -> multipleHandlersError(handler, state)
339
380
is CompletedExceptionally -> {
340
381
/*
341
382
* Continuation was already cancelled or completed exceptionally.
@@ -349,7 +390,13 @@ internal open class CancellableContinuationImpl<in T>(
349
390
* because we play type tricks on Kotlin/JS and handler is not necessarily a function there
350
391
*/
351
392
if (state is CancelledContinuation ) {
352
- callCancelHandler(handler, (state as ? CompletedExceptionally )?.cause)
393
+ val cause: Throwable ? = (state as ? CompletedExceptionally )?.cause
394
+ if (handler is CancelHandler ) {
395
+ callCancelHandler(handler, cause)
396
+ } else {
397
+ val segment = handler as Segment <* >
398
+ callSegmentOnCancellation(segment, cause)
399
+ }
353
400
}
354
401
return
355
402
}
@@ -358,14 +405,16 @@ internal open class CancellableContinuationImpl<in T>(
358
405
* Continuation was already completed, and might already have cancel handler.
359
406
*/
360
407
if (state.cancelHandler != null ) multipleHandlersError(handler, state)
361
- // BeforeResumeCancelHandler does not need to be called on a completed continuation
362
- if (cancelHandler is BeforeResumeCancelHandler ) return
408
+ // BeforeResumeCancelHandler and Segment.invokeOnCancellation(..)
409
+ // do NOT need to be called on completed continuation.
410
+ if (handler is BeforeResumeCancelHandler || handler is Segment <* >) return
411
+ handler as CancelHandler
363
412
if (state.cancelled) {
364
413
// Was already cancelled while being dispatched -- invoke the handler directly
365
414
callCancelHandler(handler, state.cancelCause)
366
415
return
367
416
}
368
- val update = state.copy(cancelHandler = cancelHandler )
417
+ val update = state.copy(cancelHandler = handler )
369
418
if (_state .compareAndSet(state, update)) return // quit on cas success
370
419
}
371
420
else -> {
@@ -374,15 +423,16 @@ internal open class CancellableContinuationImpl<in T>(
374
423
* Change its state to CompletedContinuation, unless we have BeforeResumeCancelHandler which
375
424
* does not need to be called in this case.
376
425
*/
377
- if (cancelHandler is BeforeResumeCancelHandler ) return
378
- val update = CompletedContinuation (state, cancelHandler = cancelHandler)
426
+ if (handler is BeforeResumeCancelHandler || handler is Segment <* >) return
427
+ handler as CancelHandler
428
+ val update = CompletedContinuation (state, cancelHandler = handler)
379
429
if (_state .compareAndSet(state, update)) return // quit on cas success
380
430
}
381
431
}
382
432
}
383
433
}
384
434
385
- private fun multipleHandlersError (handler : CompletionHandler , state : Any? ) {
435
+ private fun multipleHandlersError (handler : Any , state : Any? ) {
386
436
error(" It's prohibited to register multiple handlers, tried to register $handler , already has $state " )
387
437
}
388
438
0 commit comments