@@ -287,7 +287,7 @@ public open class JobSupport constructor(active: Boolean) : Job, ChildJob, Paren
287
287
// fast-path method to finalize normally completed coroutines without children
288
288
// returns true if complete, and afterCompletion(update) shall be called
289
289
private fun tryFinalizeSimpleState (state : Incomplete , update : Any? ): Boolean {
290
- assert { state is Empty || state is JobNode < * > } // only simple state without lists where children can concurrently add
290
+ assert { state is Empty || state is JobNode } // only simple state without lists where children can concurrently add
291
291
assert { update !is CompletedExceptionally } // only for normal completion
292
292
if (! _state .compareAndSet(state, update.boxIncomplete())) return false
293
293
onCancelling(null ) // simple state is not a failure
@@ -313,7 +313,7 @@ public open class JobSupport constructor(active: Boolean) : Job, ChildJob, Paren
313
313
* 2) Invoke completion handlers: .join(), callbacks etc.
314
314
* It's important to invoke them only AFTER exception handling and everything else, see #208
315
315
*/
316
- if (state is JobNode < * > ) { // SINGLE/SINGLE+ state -- one completion handler (common case)
316
+ if (state is JobNode ) { // SINGLE/SINGLE+ state -- one completion handler (common case)
317
317
try {
318
318
state.invoke(cause)
319
319
} catch (ex: Throwable ) {
@@ -327,7 +327,7 @@ public open class JobSupport constructor(active: Boolean) : Job, ChildJob, Paren
327
327
private fun notifyCancelling (list : NodeList , cause : Throwable ) {
328
328
// first cancel our own children
329
329
onCancelling(cause)
330
- notifyHandlers<JobCancellingNode < * > >(list, cause)
330
+ notifyHandlers<JobCancellingNode >(list, cause)
331
331
// then cancel parent
332
332
cancelParent(cause) // tentative cancellation -- does not matter if there is no parent
333
333
}
@@ -359,9 +359,9 @@ public open class JobSupport constructor(active: Boolean) : Job, ChildJob, Paren
359
359
}
360
360
361
361
private fun NodeList.notifyCompletion (cause : Throwable ? ) =
362
- notifyHandlers<JobNode < * > >(this , cause)
362
+ notifyHandlers<JobNode >(this , cause)
363
363
364
- private inline fun <reified T : JobNode < * > > notifyHandlers (list : NodeList , cause : Throwable ? ) {
364
+ private inline fun <reified T : JobNode > notifyHandlers (list : NodeList , cause : Throwable ? ) {
365
365
var exception: Throwable ? = null
366
366
list.forEach<T > { node ->
367
367
try {
@@ -453,21 +453,22 @@ public open class JobSupport constructor(active: Boolean) : Job, ChildJob, Paren
453
453
invokeImmediately : Boolean ,
454
454
handler : CompletionHandler
455
455
): DisposableHandle {
456
- var nodeCache: JobNode <* >? = null
456
+ // Create node upfront -- for common cases it just initializes JobNode.job field,
457
+ // for user-defined handlers it allocates a JobNode object that we might not need, but this is Ok.
458
+ val node: JobNode = makeNode(handler, onCancelling)
457
459
loopOnState { state ->
458
460
when (state) {
459
461
is Empty -> { // EMPTY_X state -- no completion handlers
460
462
if (state.isActive) {
461
463
// try move to SINGLE state
462
- val node = nodeCache ? : makeNode(handler, onCancelling).also { nodeCache = it }
463
464
if (_state .compareAndSet(state, node)) return node
464
465
} else
465
466
promoteEmptyToNodeList(state) // that way we can add listener for non-active coroutine
466
467
}
467
468
is Incomplete -> {
468
469
val list = state.list
469
470
if (list == null ) { // SINGLE/SINGLE+
470
- promoteSingleToNodeList(state as JobNode < * > )
471
+ promoteSingleToNodeList(state as JobNode )
471
472
} else {
472
473
var rootCause: Throwable ? = null
473
474
var handle: DisposableHandle = NonDisposableHandle
@@ -479,7 +480,6 @@ public open class JobSupport constructor(active: Boolean) : Job, ChildJob, Paren
479
480
// or we are adding a child to a coroutine that is not completing yet
480
481
if (rootCause == null || handler.isHandlerOf<ChildHandleNode >() && ! state.isCompleting) {
481
482
// Note: add node the list while holding lock on state (make sure it cannot change)
482
- val node = nodeCache ? : makeNode(handler, onCancelling).also { nodeCache = it }
483
483
if (! addLastAtomic(state, list, node)) return @loopOnState // retry
484
484
// just return node if we don't have to invoke handler (not cancelling yet)
485
485
if (rootCause == null ) return node
@@ -493,7 +493,6 @@ public open class JobSupport constructor(active: Boolean) : Job, ChildJob, Paren
493
493
if (invokeImmediately) handler.invokeIt(rootCause)
494
494
return handle
495
495
} else {
496
- val node = nodeCache ? : makeNode(handler, onCancelling).also { nodeCache = it }
497
496
if (addLastAtomic(state, list, node)) return node
498
497
}
499
498
}
@@ -508,16 +507,20 @@ public open class JobSupport constructor(active: Boolean) : Job, ChildJob, Paren
508
507
}
509
508
}
510
509
511
- private fun makeNode (handler : CompletionHandler , onCancelling : Boolean ): JobNode <* > {
512
- return if (onCancelling)
513
- (handler as ? JobCancellingNode <* >)?.also { assert { it.job == = this } }
514
- ? : InvokeOnCancelling (this , handler)
515
- else
516
- (handler as ? JobNode <* >)?.also { assert { it.job == = this && it !is JobCancellingNode } }
517
- ? : InvokeOnCompletion (this , handler)
510
+ private fun makeNode (handler : CompletionHandler , onCancelling : Boolean ): JobNode {
511
+ val node = if (onCancelling) {
512
+ (handler as ? JobCancellingNode )
513
+ ? : InvokeOnCancelling (handler)
514
+ } else {
515
+ (handler as ? JobNode )
516
+ ?.also { assert { it !is JobCancellingNode } }
517
+ ? : InvokeOnCompletion (handler)
518
+ }
519
+ node.job = this
520
+ return node
518
521
}
519
522
520
- private fun addLastAtomic (expect : Any , list : NodeList , node : JobNode < * > ) =
523
+ private fun addLastAtomic (expect : Any , list : NodeList , node : JobNode ) =
521
524
list.addLastIf(node) { this .state == = expect }
522
525
523
526
private fun promoteEmptyToNodeList (state : Empty ) {
@@ -527,7 +530,7 @@ public open class JobSupport constructor(active: Boolean) : Job, ChildJob, Paren
527
530
_state .compareAndSet(state, update)
528
531
}
529
532
530
- private fun promoteSingleToNodeList (state : JobNode < * > ) {
533
+ private fun promoteSingleToNodeList (state : JobNode ) {
531
534
// try to promote it to list (SINGLE+ state)
532
535
state.addOneIfEmpty(NodeList ())
533
536
// it must be in SINGLE+ state or state has changed (node could have need removed from state)
@@ -553,7 +556,7 @@ public open class JobSupport constructor(active: Boolean) : Job, ChildJob, Paren
553
556
554
557
private suspend fun joinSuspend () = suspendCancellableCoroutine<Unit > { cont ->
555
558
// We have to invoke join() handler only on cancellation, on completion we will be resumed regularly without handlers
556
- cont.disposeOnCancellation(invokeOnCompletion(handler = ResumeOnCompletion (this , cont).asHandler))
559
+ cont.disposeOnCancellation(invokeOnCompletion(handler = ResumeOnCompletion (cont).asHandler))
557
560
}
558
561
559
562
public final override val onJoin: SelectClause0
@@ -573,7 +576,7 @@ public open class JobSupport constructor(active: Boolean) : Job, ChildJob, Paren
573
576
}
574
577
if (startInternal(state) == 0 ) {
575
578
// slow-path -- register waiter for completion
576
- select.disposeOnSelect(invokeOnCompletion(handler = SelectJoinOnCompletion (this , select, block).asHandler))
579
+ select.disposeOnSelect(invokeOnCompletion(handler = SelectJoinOnCompletion (select, block).asHandler))
577
580
return
578
581
}
579
582
}
@@ -582,11 +585,11 @@ public open class JobSupport constructor(active: Boolean) : Job, ChildJob, Paren
582
585
/* *
583
586
* @suppress **This is unstable API and it is subject to change.**
584
587
*/
585
- internal fun removeNode (node : JobNode < * > ) {
588
+ internal fun removeNode (node : JobNode ) {
586
589
// remove logic depends on the state of the job
587
590
loopOnState { state ->
588
591
when (state) {
589
- is JobNode < * > -> { // SINGE/SINGLE+ state -- one completion handler
592
+ is JobNode -> { // SINGE/SINGLE+ state -- one completion handler
590
593
if (state != = node) return // a different job node --> we were already removed
591
594
// try remove and revert back to empty state
592
595
if (_state .compareAndSet(state, EMPTY_ACTIVE )) return
@@ -770,7 +773,7 @@ public open class JobSupport constructor(active: Boolean) : Job, ChildJob, Paren
770
773
private fun getOrPromoteCancellingList (state : Incomplete ): NodeList ? = state.list ? :
771
774
when (state) {
772
775
is Empty -> NodeList () // we can allocate new empty list that'll get integrated into Cancelling state
773
- is JobNode < * > -> {
776
+ is JobNode -> {
774
777
// SINGLE/SINGLE+ must be promoted to NodeList first, because otherwise we cannot
775
778
// correctly capture a reference to it
776
779
promoteSingleToNodeList(state)
@@ -849,7 +852,7 @@ public open class JobSupport constructor(active: Boolean) : Job, ChildJob, Paren
849
852
* Otherwise, there can be a race between (completed state -> handled exception and newly attached child/join)
850
853
* which may miss unhandled exception.
851
854
*/
852
- if ((state is Empty || state is JobNode < * > ) && state !is ChildHandleNode && proposedUpdate !is CompletedExceptionally ) {
855
+ if ((state is Empty || state is JobNode ) && state !is ChildHandleNode && proposedUpdate !is CompletedExceptionally ) {
853
856
if (tryFinalizeSimpleState(state, proposedUpdate)) {
854
857
// Completed successfully on fast path -- return updated state
855
858
return proposedUpdate
@@ -964,7 +967,7 @@ public open class JobSupport constructor(active: Boolean) : Job, ChildJob, Paren
964
967
* If child is attached when the job is already being cancelled, such child will receive immediate notification on
965
968
* cancellation, but parent *will* wait for that child before completion and will handle its exception.
966
969
*/
967
- return invokeOnCompletion(onCancelling = true , handler = ChildHandleNode (this , child).asHandler) as ChildHandle
970
+ return invokeOnCompletion(onCancelling = true , handler = ChildHandleNode (child).asHandler) as ChildHandle
968
971
}
969
972
970
973
/* *
@@ -1147,7 +1150,7 @@ public open class JobSupport constructor(active: Boolean) : Job, ChildJob, Paren
1147
1150
private val state : Finishing ,
1148
1151
private val child : ChildHandleNode ,
1149
1152
private val proposedUpdate : Any?
1150
- ) : JobNode<Job>(child.childJob ) {
1153
+ ) : JobNode( ) {
1151
1154
override fun invoke (cause : Throwable ? ) {
1152
1155
parent.continueCompleting(state, child, proposedUpdate)
1153
1156
}
@@ -1225,7 +1228,7 @@ public open class JobSupport constructor(active: Boolean) : Job, ChildJob, Paren
1225
1228
* thrown and not a JobCancellationException.
1226
1229
*/
1227
1230
val cont = AwaitContinuation (uCont.intercepted(), this )
1228
- cont.disposeOnCancellation(invokeOnCompletion(ResumeAwaitOnCompletion (this , cont).asHandler))
1231
+ cont.disposeOnCancellation(invokeOnCompletion(ResumeAwaitOnCompletion (cont).asHandler))
1229
1232
cont.getResult()
1230
1233
}
1231
1234
@@ -1252,7 +1255,7 @@ public open class JobSupport constructor(active: Boolean) : Job, ChildJob, Paren
1252
1255
}
1253
1256
if (startInternal(state) == 0 ) {
1254
1257
// slow-path -- register waiter for completion
1255
- select.disposeOnSelect(invokeOnCompletion(handler = SelectAwaitOnCompletion (this , select, block).asHandler))
1258
+ select.disposeOnSelect(invokeOnCompletion(handler = SelectAwaitOnCompletion (select, block).asHandler))
1256
1259
return
1257
1260
}
1258
1261
}
@@ -1342,12 +1345,14 @@ internal interface Incomplete {
1342
1345
val list: NodeList ? // is null only for Empty and JobNode incomplete state objects
1343
1346
}
1344
1347
1345
- internal abstract class JobNode <out J : Job >(
1346
- @JvmField val job : J
1347
- ) : CompletionHandlerBase(), DisposableHandle, Incomplete {
1348
+ internal abstract class JobNode : CompletionHandlerBase (), DisposableHandle, Incomplete {
1349
+ /* *
1350
+ * Initialized by [JobSupport.makeNode].
1351
+ */
1352
+ lateinit var job: JobSupport
1348
1353
override val isActive: Boolean get() = true
1349
1354
override val list: NodeList ? get() = null
1350
- override fun dispose () = ( job as JobSupport ) .removeNode(this )
1355
+ override fun dispose () = job.removeNode(this )
1351
1356
override fun toString () = " $classSimpleName @$hexAddress [job@${job.hexAddress} ]"
1352
1357
}
1353
1358
@@ -1360,7 +1365,7 @@ internal class NodeList : LockFreeLinkedListHead(), Incomplete {
1360
1365
append(state)
1361
1366
append(" }[" )
1362
1367
var first = true
1363
- this @NodeList.forEach<JobNode < * > > { node ->
1368
+ this @NodeList.forEach<JobNode > { node ->
1364
1369
if (first) first = false else append(" , " )
1365
1370
append(node)
1366
1371
}
@@ -1379,23 +1384,20 @@ internal class InactiveNodeList(
1379
1384
}
1380
1385
1381
1386
private class InvokeOnCompletion (
1382
- job : Job ,
1383
1387
private val handler : CompletionHandler
1384
- ) : JobNode<Job>(job ) {
1388
+ ) : JobNode( ) {
1385
1389
override fun invoke (cause : Throwable ? ) = handler.invoke(cause)
1386
1390
}
1387
1391
1388
1392
private class ResumeOnCompletion (
1389
- job : Job ,
1390
1393
private val continuation : Continuation <Unit >
1391
- ) : JobNode<Job>(job) {
1394
+ ) : JobNode() {
1392
1395
override fun invoke (cause : Throwable ? ) = continuation.resume(Unit )
1393
1396
}
1394
1397
1395
1398
private class ResumeAwaitOnCompletion <T >(
1396
- job : JobSupport ,
1397
1399
private val continuation : CancellableContinuationImpl <T >
1398
- ) : JobNode<JobSupport>(job ) {
1400
+ ) : JobNode( ) {
1399
1401
override fun invoke (cause : Throwable ? ) {
1400
1402
val state = job.state
1401
1403
assert { state !is Incomplete }
@@ -1411,28 +1413,25 @@ private class ResumeAwaitOnCompletion<T>(
1411
1413
}
1412
1414
1413
1415
internal class DisposeOnCompletion (
1414
- job : Job ,
1415
1416
private val handle : DisposableHandle
1416
- ) : JobNode<Job>(job ) {
1417
+ ) : JobNode( ) {
1417
1418
override fun invoke (cause : Throwable ? ) = handle.dispose()
1418
1419
}
1419
1420
1420
1421
private class SelectJoinOnCompletion <R >(
1421
- job : JobSupport ,
1422
1422
private val select : SelectInstance <R >,
1423
1423
private val block : suspend () -> R
1424
- ) : JobNode<JobSupport>(job ) {
1424
+ ) : JobNode( ) {
1425
1425
override fun invoke (cause : Throwable ? ) {
1426
1426
if (select.trySelect())
1427
1427
block.startCoroutineCancellable(select.completion)
1428
1428
}
1429
1429
}
1430
1430
1431
1431
private class SelectAwaitOnCompletion <T , R >(
1432
- job : JobSupport ,
1433
1432
private val select : SelectInstance <R >,
1434
1433
private val block : suspend (T ) -> R
1435
- ) : JobNode<JobSupport>(job ) {
1434
+ ) : JobNode( ) {
1436
1435
override fun invoke (cause : Throwable ? ) {
1437
1436
if (select.trySelect())
1438
1437
job.selectAwaitCompletion(select, block)
@@ -1445,12 +1444,11 @@ private class SelectAwaitOnCompletion<T, R>(
1445
1444
* Marker for node that shall be invoked on in _cancelling_ state.
1446
1445
* **Note: may be invoked multiple times.**
1447
1446
*/
1448
- internal abstract class JobCancellingNode < out J : Job >( job : J ) : JobNode<J>(job )
1447
+ internal abstract class JobCancellingNode : JobNode ( )
1449
1448
1450
1449
private class InvokeOnCancelling (
1451
- job : Job ,
1452
1450
private val handler : CompletionHandler
1453
- ) : JobCancellingNode<Job>(job ) {
1451
+ ) : JobCancellingNode( ) {
1454
1452
// delegate handler shall be invoked at most once, so here is an additional flag
1455
1453
private val _invoked = atomic(0 ) // todo: replace with atomic boolean after migration to recent atomicFu
1456
1454
override fun invoke (cause : Throwable ? ) {
@@ -1459,18 +1457,16 @@ private class InvokeOnCancelling(
1459
1457
}
1460
1458
1461
1459
internal class ChildHandleNode (
1462
- parent : JobSupport ,
1463
1460
@JvmField val childJob : ChildJob
1464
- ) : JobCancellingNode<JobSupport>(parent ), ChildHandle {
1461
+ ) : JobCancellingNode( ), ChildHandle {
1465
1462
override fun invoke (cause : Throwable ? ) = childJob.parentCancelled(job)
1466
1463
override fun childCancelled (cause : Throwable ): Boolean = job.childCancelled(cause)
1467
1464
}
1468
1465
1469
1466
// Same as ChildHandleNode, but for cancellable continuation
1470
1467
internal class ChildContinuation (
1471
- parent : Job ,
1472
1468
@JvmField val child : CancellableContinuationImpl <* >
1473
- ) : JobCancellingNode<Job>(parent ) {
1469
+ ) : JobCancellingNode( ) {
1474
1470
override fun invoke (cause : Throwable ? ) {
1475
1471
child.parentCancelled(child.getContinuationCancellationCause(job))
1476
1472
}
0 commit comments