@@ -70,7 +70,28 @@ internal open class CancellableContinuationImpl<in T>(
70
70
*/
71
71
private val _state = atomic<Any ?>(Active )
72
72
73
- private var parentHandle: DisposableHandle ? = null
73
+ /*
74
+ * This field has a concurrent rendezvous in the following scenario:
75
+ *
76
+ * - installParentHandle publishes this instance on T1
77
+ *
78
+ * T1 writes:
79
+ * * handle = installed; right after the installation
80
+ * * Shortly after: if (isComplete) handle = NonDisposableHandle
81
+ *
82
+ * Any other T writes if the parent job is cancelled in detachChild:
83
+ * * handle = NonDisposableHandle
84
+ *
85
+ * We want to preserve a strict invariant on parentHandle transition, allowing only three of them:
86
+ * null -> anyHandle
87
+ * anyHandle -> NonDisposableHandle
88
+ * null -> NonDisposableHandle
89
+ *
90
+ * With a guarantee that after disposal the only state handle may end up in is NonDisposableHandle
91
+ */
92
+ private val _parentHandle = atomic<DisposableHandle ?>(null )
93
+ private val parentHandle: DisposableHandle ?
94
+ get() = _parentHandle .value
74
95
75
96
internal val state: Any? get() = _state .value
76
97
@@ -101,7 +122,7 @@ internal open class CancellableContinuationImpl<in T>(
101
122
if (isCompleted) {
102
123
// Can be invoked concurrently in 'parentCancelled', no problems here
103
124
handle.dispose()
104
- parentHandle = NonDisposableHandle
125
+ _parentHandle .value = NonDisposableHandle
105
126
}
106
127
}
107
128
@@ -307,7 +328,7 @@ internal open class CancellableContinuationImpl<in T>(
307
328
onCancelling = true ,
308
329
handler = ChildContinuation (this ).asHandler
309
330
)
310
- parentHandle = handle
331
+ _parentHandle .compareAndSet( null , handle)
311
332
return handle
312
333
}
313
334
@@ -492,7 +513,7 @@ internal open class CancellableContinuationImpl<in T>(
492
513
internal fun detachChild () {
493
514
val handle = parentHandle ? : return
494
515
handle.dispose()
495
- parentHandle = NonDisposableHandle
516
+ _parentHandle .value = NonDisposableHandle
496
517
}
497
518
498
519
// Note: Always returns RESUME_TOKEN | null
0 commit comments