@@ -104,7 +104,7 @@ internal open class JobSupport constructor(active: Boolean) : Job, SelectClause0
104
104
@Suppress(" DEPRECATION" )
105
105
val handle = parent.attachChild(this )
106
106
parentHandle = handle
107
- // now check our state _after_ registering (see updateState order of actions)
107
+ // now check our state _after_ registering (see tryFinalizeState order of actions)
108
108
if (isCompleted) {
109
109
handle.dispose()
110
110
parentHandle = NonDisposableHandle // release it just in case, to aid GC
@@ -148,62 +148,39 @@ internal open class JobSupport constructor(active: Boolean) : Job, SelectClause0
148
148
// ------------ state update ------------
149
149
150
150
/* *
151
- * Updates current [state] of this job. Returns `false` if current state is not equal to expected.
152
- * @suppress **This is unstable API and it is subject to change.**
151
+ * Updates current [state] of this job to the final state, invoking all necessary handlers
152
+ * and/or `on*` methods.
153
+ *
154
+ * Returns `false` if current state is not equal to expected.
155
+ * If this method succeeds, state of this job will never be changed again
153
156
*/
154
- private fun updateState (expect : Incomplete , proposedUpdate : Any? , mode : Int ): Boolean {
157
+ private fun tryFinalizeState (expect : Incomplete , proposedUpdate : Any? , mode : Int ): Boolean {
155
158
val update = coerceProposedUpdate(expect, proposedUpdate)
156
- if (! tryUpdateState (expect, update)) return false
157
- completeUpdateState (expect, update, mode)
159
+ if (! tryFinalizeState (expect, update)) return false
160
+ completeStateFinalization (expect, update, mode)
158
161
return true
159
162
}
160
163
161
- // when Job is in Cancelling state, it can only be promoted to Cancelled state,
162
- // so if the proposed Update is not an appropriate Cancelled (preserving the cancellation cause),
163
- // then the corresponding Cancelled state is constructed.
164
- private fun coerceProposedUpdate (expect : Incomplete , proposedUpdate : Any? ): Any? =
165
- if (expect is Finishing && expect.cancelled != null && ! isCorrespondinglyCancelled(expect.cancelled, proposedUpdate))
166
- createCancelled(expect.cancelled, proposedUpdate) else proposedUpdate
167
-
168
- private fun isCorrespondinglyCancelled (cancelled : Cancelled , proposedUpdate : Any? ): Boolean {
169
- if (proposedUpdate !is Cancelled ) return false
170
- // NOTE: equality comparison of causes is performed here by design, see equals of JobCancellationException
171
- return proposedUpdate.cause == cancelled.cause || proposedUpdate.cause is JobCancellationException
172
- }
173
-
174
- private fun createCancelled (cancelled : Cancelled , proposedUpdate : Any? ): Cancelled {
175
- if (proposedUpdate !is CompletedExceptionally ) return cancelled // not exception -- just use original cancelled
176
- val exception = proposedUpdate.cause
177
- if (cancelled.cause == exception) return cancelled // that is the cancelled we need already!
178
- // todo: We need to rework this logic to keep original cancellation cause in the state and suppress other exceptions
179
- // that could have occurred while coroutine is being cancelled.
180
- // Do not spam with JCE in suppressed exceptions
181
- if (cancelled.cause !is JobCancellationException ) {
182
- exception.addSuppressedThrowable(cancelled.cause)
183
- }
184
- return Cancelled (this , exception)
185
- }
186
-
187
164
/* *
188
- * Tries to initiate update of the current [state ] of this job.
189
- * @suppress **This is unstable API and it is subject to change.**
165
+ * Tries to update [_state ] of this job to the final state and, if
166
+ * succeeds, disposes parent handle (de-attaching child from parent)
190
167
*/
191
- private fun tryUpdateState (expect : Incomplete , update : Any? ): Boolean {
168
+ private fun tryFinalizeState (expect : Incomplete , update : Any? ): Boolean {
192
169
require(update !is Incomplete ) // only incomplete -> completed transition is allowed
193
170
if (! _state .compareAndSet(expect, update)) return false
194
171
// Unregister from parent job
195
172
parentHandle?.let {
196
173
it.dispose() // volatile read parentHandle _after_ state was updated
197
174
parentHandle = NonDisposableHandle // release it just in case, to aid GC
198
175
}
199
- return true // continues in completeUpdateState
176
+ return true // continues in completeStateFinalization
200
177
}
201
178
202
179
/* *
203
180
* Completes update of the current [state] of this job.
204
181
* @suppress **This is unstable API and it is subject to change.**
205
182
*/
206
- private fun completeUpdateState (expect : Incomplete , update : Any? , mode : Int ) {
183
+ private fun completeStateFinalization (expect : Incomplete , update : Any? , mode : Int ) {
207
184
val exceptionally = update as ? CompletedExceptionally
208
185
// Do overridable processing before completion handlers
209
186
@@ -228,11 +205,42 @@ internal open class JobSupport constructor(active: Boolean) : Job, SelectClause0
228
205
} else {
229
206
expect.list?.notifyCompletion(cause)
230
207
}
231
-
232
208
onCompletionInternal(update, mode)
233
209
}
234
210
235
- private inline fun <reified T : JobNode <* >> notifyHandlers (list : NodeList , cause : Throwable ? ) {
211
+ // when Job is in Cancelling state, it can only be promoted to Cancelled state,
212
+ // so if the proposed Update is not an appropriate Cancelled (preserving the cancellation cause),
213
+ // then the corresponding Cancelled state is constructed.
214
+ private fun coerceProposedUpdate (expect : Incomplete , proposedUpdate : Any? ): Any? =
215
+ if (expect is Finishing && expect.cancelled != null && ! isCorrespondinglyCancelled(expect.cancelled, proposedUpdate))
216
+ createCancelled(expect.cancelled, proposedUpdate) else proposedUpdate
217
+
218
+ private fun isCorrespondinglyCancelled (cancelled : Cancelled , proposedUpdate : Any? ): Boolean {
219
+ if (proposedUpdate !is Cancelled ) return false
220
+ // NOTE: equality comparison of causes is performed here by design, see equals of JobCancellationException
221
+ return proposedUpdate.cause == cancelled.cause || proposedUpdate.cause is JobCancellationException
222
+ }
223
+
224
+ private fun createCancelled (cancelled : Cancelled , proposedUpdate : Any? ): Cancelled {
225
+ if (proposedUpdate !is CompletedExceptionally ) return cancelled // not exception -- just use original cancelled
226
+ val exception = proposedUpdate.cause
227
+ if (cancelled.cause == exception) return cancelled // that is the cancelled we need already!
228
+ // todo: We need to rework this logic to keep original cancellation cause in the state and suppress other exceptions
229
+ // that could have occurred while coroutine is being cancelled.
230
+ // Do not spam with JCE in suppressed exceptions
231
+ if (cancelled.cause !is JobCancellationException ) {
232
+ exception.addSuppressedThrowable(cancelled.cause)
233
+ }
234
+ return Cancelled (this , exception)
235
+ }
236
+
237
+ private fun NodeList.notifyCompletion (cause : Throwable ? ) =
238
+ notifyHandlers<JobNode <* >>(this , cause)
239
+
240
+ private fun notifyCancellation (list : NodeList , cause : Throwable ? ) =
241
+ notifyHandlers<JobCancellationNode <* >>(list, cause)
242
+
243
+ private inline fun <reified T : JobNode <* >> notifyHandlers (list : NodeList , cause : Throwable ? ) {
236
244
var exception: Throwable ? = null
237
245
list.forEach<T > { node ->
238
246
try {
@@ -246,12 +254,6 @@ internal open class JobSupport constructor(active: Boolean) : Job, SelectClause0
246
254
exception?.let { handleException(it) }
247
255
}
248
256
249
- private fun NodeList.notifyCompletion (cause : Throwable ? ) =
250
- notifyHandlers<JobNode <* >>(this , cause)
251
-
252
- private fun notifyCancellation (list : NodeList , cause : Throwable ? ) =
253
- notifyHandlers<JobCancellationNode <* >>(list, cause)
254
-
255
257
public final override fun start (): Boolean {
256
258
loopOnState { state ->
257
259
when (startInternal(state)) {
@@ -472,14 +474,14 @@ internal open class JobSupport constructor(active: Boolean) : Job, SelectClause0
472
474
473
475
public override fun cancel (cause : Throwable ? ): Boolean = when (onCancelMode) {
474
476
ON_CANCEL_MAKE_CANCELLING -> makeCancelling(cause)
475
- ON_CANCEL_MAKE_COMPLETING -> makeCompletingOnCancel( cause)
477
+ ON_CANCEL_MAKE_COMPLETING -> makeCompleting( Cancelled ( this , cause) )
476
478
else -> error(" Invalid onCancelMode $onCancelMode " )
477
479
}
478
480
479
481
// we will be dispatching coroutine to process its cancellation exception, so there is no need for
480
482
// an extra check for Job status in MODE_CANCELLABLE
481
483
private fun updateStateCancelled (state : Incomplete , cause : Throwable ? ) =
482
- updateState (state, Cancelled (this , cause), mode = MODE_ATOMIC_DEFAULT )
484
+ tryFinalizeState (state, Cancelled (this , cause), mode = MODE_ATOMIC_DEFAULT )
483
485
484
486
// transitions to Cancelling state
485
487
private fun makeCancelling (cause : Throwable ? ): Boolean {
@@ -528,9 +530,6 @@ internal open class JobSupport constructor(active: Boolean) : Job, SelectClause0
528
530
return true
529
531
}
530
532
531
- private fun makeCompletingOnCancel (cause : Throwable ? ): Boolean =
532
- makeCompleting(Cancelled (this , cause))
533
-
534
533
/* *
535
534
* @suppress **This is unstable API and it is subject to change.**
536
535
*/
@@ -565,7 +564,7 @@ internal open class JobSupport constructor(active: Boolean) : Job, SelectClause0
565
564
val child: ChildJob ? = firstChild(state) ? : // or else complete immediately w/o children
566
565
when {
567
566
state !is Finishing && hasOnFinishingHandler(proposedUpdate) -> null // unless it has onFinishing handler
568
- updateState (state, proposedUpdate, mode) -> return COMPLETING_COMPLETED
567
+ tryFinalizeState (state, proposedUpdate, mode) -> return COMPLETING_COMPLETED
569
568
else -> return @loopOnState
570
569
}
571
570
val list = state.list ? : // must promote to list to correctly operate on child lists
@@ -590,7 +589,7 @@ internal open class JobSupport constructor(active: Boolean) : Job, SelectClause0
590
589
if (state !is Finishing ) onFinishingInternal(proposedUpdate)
591
590
if (child != null && tryWaitForChild(child, proposedUpdate))
592
591
return COMPLETING_WAITING_CHILDREN
593
- if (updateState (completing, proposedUpdate, mode = MODE_ATOMIC_DEFAULT ))
592
+ if (tryFinalizeState (completing, proposedUpdate, mode = MODE_ATOMIC_DEFAULT ))
594
593
return COMPLETING_COMPLETED
595
594
}
596
595
}
@@ -628,7 +627,7 @@ internal open class JobSupport constructor(active: Boolean) : Job, SelectClause0
628
627
// try wait for next child
629
628
if (waitChild != null && tryWaitForChild(waitChild, proposedUpdate)) return // waiting for next child
630
629
// no more children to wait -- try update state
631
- if (updateState (state, proposedUpdate, MODE_ATOMIC_DEFAULT )) return
630
+ if (tryFinalizeState (state, proposedUpdate, MODE_ATOMIC_DEFAULT )) return
632
631
}
633
632
}
634
633
0 commit comments