@@ -11,10 +11,18 @@ import kotlin.coroutines.*
11
11
import kotlin.coroutines.intrinsics.*
12
12
import kotlin.jvm.*
13
13
14
+ private const val DECISION_SHIFT = 29
15
+ private const val INDEX_MASK = (1 shl DECISION_SHIFT ) - 1
16
+ private const val NO_INDEX = INDEX_MASK
14
17
private const val UNDECIDED = 0
15
18
private const val SUSPENDED = 1
16
19
private const val RESUMED = 2
17
20
21
+ private inline val Int .decision get() = this shr DECISION_SHIFT
22
+ private inline val Int .index get() = this and INDEX_MASK
23
+ @Suppress(" NOTHING_TO_INLINE" )
24
+ private inline fun construct (decision : Int , index : Int ) = (decision shl DECISION_SHIFT ) + index
25
+
18
26
@JvmField
19
27
internal val RESUME_TOKEN = Symbol (" RESUME_TOKEN" )
20
28
@@ -56,9 +64,9 @@ internal open class CancellableContinuationImpl<in T>(
56
64
| RESUMED |
57
65
+-----------+
58
66
59
- Note: both tryResume and trySuspend can be invoked at most once, first invocation wins
67
+ Note: both tryResume and trySuspend can be invoked at most once, first invocation wins.
60
68
*/
61
- private val _decision = atomic(UNDECIDED )
69
+ private val _decisionAndIndex = atomic(construct( UNDECIDED , NO_INDEX ) )
62
70
63
71
/*
64
72
=== Internal states ===
@@ -144,7 +152,7 @@ internal open class CancellableContinuationImpl<in T>(
144
152
detachChild()
145
153
return false
146
154
}
147
- _decision .value = UNDECIDED
155
+ _decisionAndIndex .value = construct( UNDECIDED , NO_INDEX )
148
156
_state .value = Active
149
157
return true
150
158
}
@@ -194,10 +202,11 @@ internal open class CancellableContinuationImpl<in T>(
194
202
_state .loop { state ->
195
203
if (state !is NotCompleted ) return false // false if already complete or cancelling
196
204
// Active -- update to final state
197
- val update = CancelledContinuation (this , cause, handled = state is CancelHandler )
205
+ val update = CancelledContinuation (this , cause, handled = state is CancelHandler || state is Segment < * > )
198
206
if (! _state .compareAndSet(state, update)) return @loop // retry on cas failure
199
207
// Invoke cancel handler if it was present
200
208
(state as ? CancelHandler )?.let { callCancelHandler(it, cause) }
209
+ (state as ? Segment <* >)?.let { callSegmentOnCancellation(it, cause) }
201
210
// Complete state update
202
211
detachChildIfNonResuable()
203
212
dispatchResume(resumeMode) // no need for additional cancellation checks
@@ -234,6 +243,13 @@ internal open class CancellableContinuationImpl<in T>(
234
243
fun callCancelHandler (handler : CancelHandler , cause : Throwable ? ) =
235
244
callCancelHandlerSafely { handler.invoke(cause) }
236
245
246
+ private fun callSegmentOnCancellation (segment : Segment <* >, cause : Throwable ? ) {
247
+ val index = _decisionAndIndex .value.index
248
+ check(index != NO_INDEX ) { " The index for segment.invokeOnCancellation(..) is broken" }
249
+ callCancelHandlerSafely { segment.invokeOnCancellation(index, cause) }
250
+ }
251
+
252
+
237
253
fun callOnCancellation (onCancellation : (cause: Throwable ) -> Unit , cause : Throwable ) {
238
254
try {
239
255
onCancellation.invoke(cause)
@@ -253,19 +269,19 @@ internal open class CancellableContinuationImpl<in T>(
253
269
parent.getCancellationException()
254
270
255
271
private fun trySuspend (): Boolean {
256
- _decision .loop { decision ->
257
- when (decision) {
258
- UNDECIDED -> if (this ._decision .compareAndSet(UNDECIDED , SUSPENDED )) return true
272
+ _decisionAndIndex .loop { cur ->
273
+ when (cur. decision) {
274
+ UNDECIDED -> if (this ._decisionAndIndex .compareAndSet(cur, construct( SUSPENDED , cur.index) )) return true
259
275
RESUMED -> return false
260
276
else -> error(" Already suspended" )
261
277
}
262
278
}
263
279
}
264
280
265
281
private fun tryResume (): Boolean {
266
- _decision .loop { decision ->
267
- when (decision) {
268
- UNDECIDED -> if (this ._decision .compareAndSet(UNDECIDED , RESUMED )) return true
282
+ _decisionAndIndex .loop { cur ->
283
+ when (cur. decision) {
284
+ UNDECIDED -> if (this ._decisionAndIndex .compareAndSet(cur, construct( RESUMED , cur.index) )) return true
269
285
SUSPENDED -> return false
270
286
else -> error(" Already resumed" )
271
287
}
@@ -350,14 +366,39 @@ internal open class CancellableContinuationImpl<in T>(
350
366
override fun resume (value : T , onCancellation : ((cause: Throwable ) -> Unit )? ) =
351
367
resumeImpl(value, resumeMode, onCancellation)
352
368
369
+ /* *
370
+ * An optimized version for the code below that does not allocate
371
+ * a cancellation handler object and efficiently stores the specified
372
+ * [segment] and [index] in this [CancellableContinuationImpl].
373
+ * ```
374
+ * invokeOnCancellation { cause ->
375
+ * segment.invokeOnCancellation(index, cause)
376
+ * }
377
+ * ```
378
+ */
379
+ internal fun invokeOnCancellation (segment : Segment <* >, index : Int ) {
380
+ _decisionAndIndex .update {
381
+ check(it.index == NO_INDEX ) {
382
+ " invokeOnCancellation should be invoked at most once"
383
+ }
384
+ construct(it.decision, index)
385
+ }
386
+ invokeOnCancellationImpl(segment)
387
+ }
388
+
353
389
public override fun invokeOnCancellation (handler : CompletionHandler ) {
354
390
val cancelHandler = makeCancelHandler(handler)
391
+ invokeOnCancellationImpl(cancelHandler)
392
+ }
393
+
394
+ private fun invokeOnCancellationImpl (handler : Any ) {
395
+ assert { handler is CancelHandler || handler is Segment <* > }
355
396
_state .loop { state ->
356
397
when (state) {
357
398
is Active -> {
358
- if (_state .compareAndSet(state, cancelHandler )) return // quit on cas success
399
+ if (_state .compareAndSet(state, handler )) return // quit on cas success
359
400
}
360
- is CancelHandler -> multipleHandlersError(handler, state)
401
+ is CancelHandler , is Segment < * > -> multipleHandlersError(handler, state)
361
402
is CompletedExceptionally -> {
362
403
/*
363
404
* Continuation was already cancelled or completed exceptionally.
@@ -371,7 +412,13 @@ internal open class CancellableContinuationImpl<in T>(
371
412
* because we play type tricks on Kotlin/JS and handler is not necessarily a function there
372
413
*/
373
414
if (state is CancelledContinuation ) {
374
- callCancelHandler(handler, (state as ? CompletedExceptionally )?.cause)
415
+ val cause: Throwable ? = (state as ? CompletedExceptionally )?.cause
416
+ if (handler is CancelHandler ) {
417
+ callCancelHandler(handler, cause)
418
+ } else {
419
+ val segment = handler as Segment <* >
420
+ callSegmentOnCancellation(segment, cause)
421
+ }
375
422
}
376
423
return
377
424
}
@@ -380,14 +427,16 @@ internal open class CancellableContinuationImpl<in T>(
380
427
* Continuation was already completed, and might already have cancel handler.
381
428
*/
382
429
if (state.cancelHandler != null ) multipleHandlersError(handler, state)
383
- // BeforeResumeCancelHandler does not need to be called on a completed continuation
384
- if (cancelHandler is BeforeResumeCancelHandler ) return
430
+ // BeforeResumeCancelHandler and Segment.invokeOnCancellation(..)
431
+ // do NOT need to be called on completed continuation.
432
+ if (handler is BeforeResumeCancelHandler || handler is Segment <* >) return
433
+ handler as CancelHandler
385
434
if (state.cancelled) {
386
435
// Was already cancelled while being dispatched -- invoke the handler directly
387
436
callCancelHandler(handler, state.cancelCause)
388
437
return
389
438
}
390
- val update = state.copy(cancelHandler = cancelHandler )
439
+ val update = state.copy(cancelHandler = handler )
391
440
if (_state .compareAndSet(state, update)) return // quit on cas success
392
441
}
393
442
else -> {
@@ -396,15 +445,16 @@ internal open class CancellableContinuationImpl<in T>(
396
445
* Change its state to CompletedContinuation, unless we have BeforeResumeCancelHandler which
397
446
* does not need to be called in this case.
398
447
*/
399
- if (cancelHandler is BeforeResumeCancelHandler ) return
400
- val update = CompletedContinuation (state, cancelHandler = cancelHandler)
448
+ if (handler is BeforeResumeCancelHandler || handler is Segment <* >) return
449
+ handler as CancelHandler
450
+ val update = CompletedContinuation (state, cancelHandler = handler)
401
451
if (_state .compareAndSet(state, update)) return // quit on cas success
402
452
}
403
453
}
404
454
}
405
455
}
406
456
407
- private fun multipleHandlersError (handler : CompletionHandler , state : Any? ) {
457
+ private fun multipleHandlersError (handler : Any , state : Any? ) {
408
458
error(" It's prohibited to register multiple handlers, tried to register $handler , already has $state " )
409
459
}
410
460
0 commit comments