@@ -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,19 +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
- if (onCancelling) {
513
- (handler as ? JobCancellingNode <* >)
514
- ?.takeIf { it.job == = this }
515
- ? : InvokeOnCancelling (this , handler)
510
+ private fun makeNode (handler : CompletionHandler , onCancelling : Boolean ): JobNode {
511
+ val node = if (onCancelling) {
512
+ (handler as ? JobCancellingNode )
513
+ ? : InvokeOnCancelling (handler)
516
514
} else {
517
- (handler as ? JobNode < * > )
515
+ (handler as ? JobNode )
518
516
?.also { assert { it !is JobCancellingNode } }
519
- ?.takeIf { it.job == = this }
520
- ? : InvokeOnCompletion (this , handler)
517
+ ? : InvokeOnCompletion (handler)
521
518
}
519
+ node.job = this
520
+ return node
521
+ }
522
522
523
- private fun addLastAtomic (expect : Any , list : NodeList , node : JobNode < * > ) =
523
+ private fun addLastAtomic (expect : Any , list : NodeList , node : JobNode ) =
524
524
list.addLastIf(node) { this .state == = expect }
525
525
526
526
private fun promoteEmptyToNodeList (state : Empty ) {
@@ -530,7 +530,7 @@ public open class JobSupport constructor(active: Boolean) : Job, ChildJob, Paren
530
530
_state .compareAndSet(state, update)
531
531
}
532
532
533
- private fun promoteSingleToNodeList (state : JobNode < * > ) {
533
+ private fun promoteSingleToNodeList (state : JobNode ) {
534
534
// try to promote it to list (SINGLE+ state)
535
535
state.addOneIfEmpty(NodeList ())
536
536
// it must be in SINGLE+ state or state has changed (node could have need removed from state)
@@ -556,7 +556,7 @@ public open class JobSupport constructor(active: Boolean) : Job, ChildJob, Paren
556
556
557
557
private suspend fun joinSuspend () = suspendCancellableCoroutine<Unit > { cont ->
558
558
// We have to invoke join() handler only on cancellation, on completion we will be resumed regularly without handlers
559
- cont.disposeOnCancellation(invokeOnCompletion(handler = ResumeOnCompletion (this , cont).asHandler))
559
+ cont.disposeOnCancellation(invokeOnCompletion(handler = ResumeOnCompletion (cont).asHandler))
560
560
}
561
561
562
562
public final override val onJoin: SelectClause0
@@ -576,7 +576,7 @@ public open class JobSupport constructor(active: Boolean) : Job, ChildJob, Paren
576
576
}
577
577
if (startInternal(state) == 0 ) {
578
578
// slow-path -- register waiter for completion
579
- select.disposeOnSelect(invokeOnCompletion(handler = SelectJoinOnCompletion (this , select, block).asHandler))
579
+ select.disposeOnSelect(invokeOnCompletion(handler = SelectJoinOnCompletion (select, block).asHandler))
580
580
return
581
581
}
582
582
}
@@ -585,11 +585,11 @@ public open class JobSupport constructor(active: Boolean) : Job, ChildJob, Paren
585
585
/* *
586
586
* @suppress **This is unstable API and it is subject to change.**
587
587
*/
588
- internal fun removeNode (node : JobNode < * > ) {
588
+ internal fun removeNode (node : JobNode ) {
589
589
// remove logic depends on the state of the job
590
590
loopOnState { state ->
591
591
when (state) {
592
- is JobNode < * > -> { // SINGE/SINGLE+ state -- one completion handler
592
+ is JobNode -> { // SINGE/SINGLE+ state -- one completion handler
593
593
if (state != = node) return // a different job node --> we were already removed
594
594
// try remove and revert back to empty state
595
595
if (_state .compareAndSet(state, EMPTY_ACTIVE )) return
@@ -773,7 +773,7 @@ public open class JobSupport constructor(active: Boolean) : Job, ChildJob, Paren
773
773
private fun getOrPromoteCancellingList (state : Incomplete ): NodeList ? = state.list ? :
774
774
when (state) {
775
775
is Empty -> NodeList () // we can allocate new empty list that'll get integrated into Cancelling state
776
- is JobNode < * > -> {
776
+ is JobNode -> {
777
777
// SINGLE/SINGLE+ must be promoted to NodeList first, because otherwise we cannot
778
778
// correctly capture a reference to it
779
779
promoteSingleToNodeList(state)
@@ -852,7 +852,7 @@ public open class JobSupport constructor(active: Boolean) : Job, ChildJob, Paren
852
852
* Otherwise, there can be a race between (completed state -> handled exception and newly attached child/join)
853
853
* which may miss unhandled exception.
854
854
*/
855
- 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 ) {
856
856
if (tryFinalizeSimpleState(state, proposedUpdate)) {
857
857
// Completed successfully on fast path -- return updated state
858
858
return proposedUpdate
@@ -967,7 +967,7 @@ public open class JobSupport constructor(active: Boolean) : Job, ChildJob, Paren
967
967
* If child is attached when the job is already being cancelled, such child will receive immediate notification on
968
968
* cancellation, but parent *will* wait for that child before completion and will handle its exception.
969
969
*/
970
- return invokeOnCompletion(onCancelling = true , handler = ChildHandleNode (this , child).asHandler) as ChildHandle
970
+ return invokeOnCompletion(onCancelling = true , handler = ChildHandleNode (child).asHandler) as ChildHandle
971
971
}
972
972
973
973
/* *
@@ -1150,7 +1150,7 @@ public open class JobSupport constructor(active: Boolean) : Job, ChildJob, Paren
1150
1150
private val state : Finishing ,
1151
1151
private val child : ChildHandleNode ,
1152
1152
private val proposedUpdate : Any?
1153
- ) : JobNode<Job>(child.childJob ) {
1153
+ ) : JobNode( ) {
1154
1154
override fun invoke (cause : Throwable ? ) {
1155
1155
parent.continueCompleting(state, child, proposedUpdate)
1156
1156
}
@@ -1228,7 +1228,7 @@ public open class JobSupport constructor(active: Boolean) : Job, ChildJob, Paren
1228
1228
* thrown and not a JobCancellationException.
1229
1229
*/
1230
1230
val cont = AwaitContinuation (uCont.intercepted(), this )
1231
- cont.disposeOnCancellation(invokeOnCompletion(ResumeAwaitOnCompletion (this , cont).asHandler))
1231
+ cont.disposeOnCancellation(invokeOnCompletion(ResumeAwaitOnCompletion (cont).asHandler))
1232
1232
cont.getResult()
1233
1233
}
1234
1234
@@ -1255,7 +1255,7 @@ public open class JobSupport constructor(active: Boolean) : Job, ChildJob, Paren
1255
1255
}
1256
1256
if (startInternal(state) == 0 ) {
1257
1257
// slow-path -- register waiter for completion
1258
- select.disposeOnSelect(invokeOnCompletion(handler = SelectAwaitOnCompletion (this , select, block).asHandler))
1258
+ select.disposeOnSelect(invokeOnCompletion(handler = SelectAwaitOnCompletion (select, block).asHandler))
1259
1259
return
1260
1260
}
1261
1261
}
@@ -1345,12 +1345,14 @@ internal interface Incomplete {
1345
1345
val list: NodeList ? // is null only for Empty and JobNode incomplete state objects
1346
1346
}
1347
1347
1348
- internal abstract class JobNode <out J : Job >(
1349
- @JvmField val job : J
1350
- ) : CompletionHandlerBase(), DisposableHandle, Incomplete {
1348
+ internal abstract class JobNode : CompletionHandlerBase (), DisposableHandle, Incomplete {
1349
+ /* *
1350
+ * Initialized by [JobSupport.makeNode].
1351
+ */
1352
+ lateinit var job: JobSupport
1351
1353
override val isActive: Boolean get() = true
1352
1354
override val list: NodeList ? get() = null
1353
- override fun dispose () = ( job as JobSupport ) .removeNode(this )
1355
+ override fun dispose () = job.removeNode(this )
1354
1356
override fun toString () = " $classSimpleName @$hexAddress [job@${job.hexAddress} ]"
1355
1357
}
1356
1358
@@ -1363,7 +1365,7 @@ internal class NodeList : LockFreeLinkedListHead(), Incomplete {
1363
1365
append(state)
1364
1366
append(" }[" )
1365
1367
var first = true
1366
- this @NodeList.forEach<JobNode < * > > { node ->
1368
+ this @NodeList.forEach<JobNode > { node ->
1367
1369
if (first) first = false else append(" , " )
1368
1370
append(node)
1369
1371
}
@@ -1382,23 +1384,20 @@ internal class InactiveNodeList(
1382
1384
}
1383
1385
1384
1386
private class InvokeOnCompletion (
1385
- job : Job ,
1386
1387
private val handler : CompletionHandler
1387
- ) : JobNode<Job>(job ) {
1388
+ ) : JobNode( ) {
1388
1389
override fun invoke (cause : Throwable ? ) = handler.invoke(cause)
1389
1390
}
1390
1391
1391
1392
private class ResumeOnCompletion (
1392
- job : Job ,
1393
1393
private val continuation : Continuation <Unit >
1394
- ) : JobNode<Job>(job) {
1394
+ ) : JobNode() {
1395
1395
override fun invoke (cause : Throwable ? ) = continuation.resume(Unit )
1396
1396
}
1397
1397
1398
1398
private class ResumeAwaitOnCompletion <T >(
1399
- job : JobSupport ,
1400
1399
private val continuation : CancellableContinuationImpl <T >
1401
- ) : JobNode<JobSupport>(job ) {
1400
+ ) : JobNode( ) {
1402
1401
override fun invoke (cause : Throwable ? ) {
1403
1402
val state = job.state
1404
1403
assert { state !is Incomplete }
@@ -1414,28 +1413,25 @@ private class ResumeAwaitOnCompletion<T>(
1414
1413
}
1415
1414
1416
1415
internal class DisposeOnCompletion (
1417
- job : Job ,
1418
1416
private val handle : DisposableHandle
1419
- ) : JobNode<Job>(job ) {
1417
+ ) : JobNode( ) {
1420
1418
override fun invoke (cause : Throwable ? ) = handle.dispose()
1421
1419
}
1422
1420
1423
1421
private class SelectJoinOnCompletion <R >(
1424
- job : JobSupport ,
1425
1422
private val select : SelectInstance <R >,
1426
1423
private val block : suspend () -> R
1427
- ) : JobNode<JobSupport>(job ) {
1424
+ ) : JobNode( ) {
1428
1425
override fun invoke (cause : Throwable ? ) {
1429
1426
if (select.trySelect())
1430
1427
block.startCoroutineCancellable(select.completion)
1431
1428
}
1432
1429
}
1433
1430
1434
1431
private class SelectAwaitOnCompletion <T , R >(
1435
- job : JobSupport ,
1436
1432
private val select : SelectInstance <R >,
1437
1433
private val block : suspend (T ) -> R
1438
- ) : JobNode<JobSupport>(job ) {
1434
+ ) : JobNode( ) {
1439
1435
override fun invoke (cause : Throwable ? ) {
1440
1436
if (select.trySelect())
1441
1437
job.selectAwaitCompletion(select, block)
@@ -1448,12 +1444,11 @@ private class SelectAwaitOnCompletion<T, R>(
1448
1444
* Marker for node that shall be invoked on in _cancelling_ state.
1449
1445
* **Note: may be invoked multiple times.**
1450
1446
*/
1451
- internal abstract class JobCancellingNode < out J : Job >( job : J ) : JobNode<J>(job )
1447
+ internal abstract class JobCancellingNode : JobNode ( )
1452
1448
1453
1449
private class InvokeOnCancelling (
1454
- job : Job ,
1455
1450
private val handler : CompletionHandler
1456
- ) : JobCancellingNode<Job>(job ) {
1451
+ ) : JobCancellingNode( ) {
1457
1452
// delegate handler shall be invoked at most once, so here is an additional flag
1458
1453
private val _invoked = atomic(0 ) // todo: replace with atomic boolean after migration to recent atomicFu
1459
1454
override fun invoke (cause : Throwable ? ) {
@@ -1462,18 +1457,16 @@ private class InvokeOnCancelling(
1462
1457
}
1463
1458
1464
1459
internal class ChildHandleNode (
1465
- parent : JobSupport ,
1466
1460
@JvmField val childJob : ChildJob
1467
- ) : JobCancellingNode<JobSupport>(parent ), ChildHandle {
1461
+ ) : JobCancellingNode( ), ChildHandle {
1468
1462
override fun invoke (cause : Throwable ? ) = childJob.parentCancelled(job)
1469
1463
override fun childCancelled (cause : Throwable ): Boolean = job.childCancelled(cause)
1470
1464
}
1471
1465
1472
1466
// Same as ChildHandleNode, but for cancellable continuation
1473
1467
internal class ChildContinuation (
1474
- parent : Job ,
1475
1468
@JvmField val child : CancellableContinuationImpl <* >
1476
- ) : JobCancellingNode<Job>(parent ) {
1469
+ ) : JobCancellingNode( ) {
1477
1470
override fun invoke (cause : Throwable ? ) {
1478
1471
child.parentCancelled(child.getContinuationCancellationCause(job))
1479
1472
}
0 commit comments