@@ -60,7 +60,7 @@ import kotlin.coroutines.experimental.intrinsics.suspendCoroutineOrReturn
60
60
* [CoroutineStart.LAZY]. Such a job can be made _active_ by invoking [start] or [join].
61
61
*
62
62
* A job can be _cancelled_ at any time with [cancel] function that forces it to transition to
63
- * _cancelling_ state immediately. Job that is not backed by a coroutine and does not have
63
+ * _cancelling_ state immediately. Job that is not backed by a coroutine (see `Job()` function) and does not have
64
64
* [children] becomes _cancelled_ on [cancel] immediately.
65
65
* Otherwise, job becomes _cancelled_ when it finishes executing its code and
66
66
* when all its children [complete][isCompleted].
@@ -841,7 +841,7 @@ public open class JobSupport(active: Boolean) : Job, SelectClause0, SelectClause
841
841
promoteSingleToNodeList(state as JobNode <* >)
842
842
} else {
843
843
if (state is Finishing && state.cancelled != null && onCancelling) {
844
- check(hasCancellingState ) // cannot be in this state unless were support cancelling state
844
+ check(onCancelMode != ON_CANCEL_MAKE_CANCELLED ) // cannot be in this state unless were support cancelling state
845
845
// installing cancellation handler on job that is being cancelled
846
846
if (invokeImmediately) handler(state.cancelled.cause)
847
847
return NonDisposableHandle
@@ -858,14 +858,15 @@ public open class JobSupport(active: Boolean) : Job, SelectClause0, SelectClause
858
858
}
859
859
}
860
860
861
- private fun makeNode (handler : CompletionHandler , onCancelling : Boolean ): JobNode <* > =
862
- if (onCancelling && hasCancellingState)
861
+ private fun makeNode (handler : CompletionHandler , onCancelling : Boolean ): JobNode <* > {
862
+ val hasCancellingState = onCancelMode != ON_CANCEL_MAKE_CANCELLED
863
+ return if (onCancelling && hasCancellingState)
863
864
(handler as ? JobCancellationNode <* >)?.also { require(it.job == = this ) }
864
865
? : InvokeOnCancellation (this , handler)
865
866
else
866
867
(handler as ? JobNode <* >)?.also { require(it.job == = this && (! hasCancellingState || it !is JobCancellationNode )) }
867
868
? : InvokeOnCompletion (this , handler)
868
-
869
+ }
869
870
870
871
private fun addLastAtomic (expect : Any , list : NodeList , node : JobNode <* >) =
871
872
list.addLastIf(node) { this .state == = expect }
@@ -948,12 +949,14 @@ public open class JobSupport(active: Boolean) : Job, SelectClause0, SelectClause
948
949
}
949
950
}
950
951
951
- protected open val hasCancellingState : Boolean get() = false
952
+ protected open val onCancelMode : Int get() = ON_CANCEL_MAKE_CANCELLING
952
953
953
- public override fun cancel (cause : Throwable ? ): Boolean =
954
- if (hasCancellingState)
955
- makeCancelling(cause) else
956
- makeCancelled(cause)
954
+ public override fun cancel (cause : Throwable ? ): Boolean = when (onCancelMode) {
955
+ ON_CANCEL_MAKE_CANCELLED -> makeCancelled(cause)
956
+ ON_CANCEL_MAKE_CANCELLING -> makeCancelling(cause)
957
+ ON_CANCEL_MAKE_COMPLETING -> makeCompletingOnCancel(cause)
958
+ else -> error(" Invalid onCancelMode $onCancelMode " )
959
+ }
957
960
958
961
// we will be dispatching coroutine to process its cancellation exception, so there is no need for
959
962
// an extra check for Job status in MODE_CANCELLABLE
@@ -1013,6 +1016,15 @@ public open class JobSupport(active: Boolean) : Job, SelectClause0, SelectClause
1013
1016
return true
1014
1017
}
1015
1018
1019
+ private fun makeCompletingOnCancel (cause : Throwable ? ): Boolean =
1020
+ makeCompleting(Cancelled (this , cause))
1021
+
1022
+ internal fun makeCompleting (proposedUpdate : Any? ): Boolean =
1023
+ when (makeCompletingInternal(proposedUpdate, mode = MODE_ATOMIC_DEFAULT )) {
1024
+ COMPLETING_ALREADY_COMPLETING -> false
1025
+ else -> true
1026
+ }
1027
+
1016
1028
/* *
1017
1029
* Returns:
1018
1030
* * `true` if state was updated to completed/cancelled;
@@ -1021,30 +1033,38 @@ public open class JobSupport(active: Boolean) : Job, SelectClause0, SelectClause
1021
1033
* @throws IllegalStateException if job is already complete or completing
1022
1034
* @suppress **This is unstable API and it is subject to change.**
1023
1035
*/
1024
- internal fun makeCompleting (proposedUpdate : Any? , mode : Int ): Boolean {
1036
+ internal fun makeCompletingOnce (proposedUpdate : Any? , mode : Int ): Boolean =
1037
+ when (makeCompletingInternal(proposedUpdate, mode)) {
1038
+ COMPLETING_COMPLETED -> true
1039
+ COMPLETING_WAITING_CHILDREN -> false
1040
+ else -> throw IllegalStateException (" Job $this is already complete or completing, " +
1041
+ " but is being completed with $proposedUpdate " , proposedUpdate.exceptionOrNull)
1042
+ }
1043
+
1044
+ private fun makeCompletingInternal (proposedUpdate : Any? , mode : Int ): Int {
1025
1045
loopOnState { state ->
1026
1046
if (state !is Incomplete )
1027
- throw IllegalStateException ( " Job $this is already complete, but is being completed with $proposedUpdate " , proposedUpdate.exceptionOrNull)
1047
+ return COMPLETING_ALREADY_COMPLETING
1028
1048
if (state is Finishing && state.completing)
1029
- throw IllegalStateException ( " Job $this is already completing, but is being completed with $proposedUpdate " , proposedUpdate.exceptionOrNull)
1049
+ return COMPLETING_ALREADY_COMPLETING
1030
1050
val child: Child = firstChild(state) ? : // or else complete immediately w/o children
1031
- if (updateState(state, proposedUpdate, mode)) return true else return @loopOnState
1051
+ if (updateState(state, proposedUpdate, mode)) return COMPLETING_COMPLETED else return @loopOnState
1032
1052
// must promote to list to correct operate on child lists
1033
1053
if (state is JobNode <* >) {
1034
1054
promoteSingleToNodeList(state)
1035
1055
return @loopOnState // retry
1036
1056
}
1037
1057
// cancel all children in list on exceptional completion
1038
- if (proposedUpdate is CompletedExceptionally
1039
- )
1058
+ if (proposedUpdate is CompletedExceptionally )
1040
1059
child.cancelChildrenInternal(proposedUpdate.exception)
1041
1060
// switch to completing state
1042
- val completing = Finishing (state.list!! , (state as ? Finishing )?.cancelled, true )
1061
+ val cancelled = (state as ? Finishing )?.cancelled ? : (proposedUpdate as ? Cancelled )
1062
+ val completing = Finishing (state.list!! , cancelled, true )
1043
1063
if (_state .compareAndSet(state, completing)) {
1044
1064
if (tryWaitForChild(child, proposedUpdate))
1045
- return false
1046
- if (updateState(completing, proposedUpdate, MODE_ATOMIC_DEFAULT ))
1047
- return true
1065
+ return COMPLETING_WAITING_CHILDREN
1066
+ if (updateState(completing, proposedUpdate, mode = MODE_ATOMIC_DEFAULT ))
1067
+ return COMPLETING_COMPLETED
1048
1068
}
1049
1069
}
1050
1070
}
@@ -1251,8 +1271,7 @@ public open class JobSupport(active: Boolean) : Job, SelectClause0, SelectClause
1251
1271
cont.disposeOnCompletion(invokeOnCompletion {
1252
1272
val state = this .state
1253
1273
check(state !is Incomplete )
1254
- if (state is CompletedExceptionally
1255
- )
1274
+ if (state is CompletedExceptionally )
1256
1275
cont.resumeWithException(state.exception)
1257
1276
else
1258
1277
cont.resume(state)
@@ -1267,8 +1286,7 @@ public open class JobSupport(active: Boolean) : Job, SelectClause0, SelectClause
1267
1286
if (state !is Incomplete ) {
1268
1287
// already complete -- select result
1269
1288
if (select.trySelect(null )) {
1270
- if (state is CompletedExceptionally
1271
- )
1289
+ if (state is CompletedExceptionally )
1272
1290
select.resumeSelectCancellableWithException(state.exception)
1273
1291
else
1274
1292
block.startCoroutineUndispatched(state, select.completion)
@@ -1286,14 +1304,21 @@ public open class JobSupport(active: Boolean) : Job, SelectClause0, SelectClause
1286
1304
internal fun <R > selectAwaitCompletion (select : SelectInstance <R >, block : suspend (Any? ) -> R ) {
1287
1305
val state = this .state
1288
1306
// Note: await is non-atomic (can be cancelled while dispatched)
1289
- if (state is CompletedExceptionally
1290
- )
1307
+ if (state is CompletedExceptionally )
1291
1308
select.resumeSelectCancellableWithException(state.exception)
1292
1309
else
1293
1310
block.startCoroutineCancellable(state, select.completion)
1294
1311
}
1295
1312
}
1296
1313
1314
+ internal const val ON_CANCEL_MAKE_CANCELLED = 0
1315
+ internal const val ON_CANCEL_MAKE_CANCELLING = 1
1316
+ internal const val ON_CANCEL_MAKE_COMPLETING = 2
1317
+
1318
+ private const val COMPLETING_ALREADY_COMPLETING = 0
1319
+ private const val COMPLETING_COMPLETED = 1
1320
+ private const val COMPLETING_WAITING_CHILDREN = 2
1321
+
1297
1322
private const val RETRY = - 1
1298
1323
private const val FALSE = 0
1299
1324
private const val TRUE = 1
@@ -1310,6 +1335,7 @@ private class Empty(override val isActive: Boolean) : JobSupport.Incomplete {
1310
1335
1311
1336
private class JobImpl (parent : Job ? = null ) : JobSupport(true ) {
1312
1337
init { initParentJob(parent) }
1338
+ override val onCancelMode: Int get() = ON_CANCEL_MAKE_COMPLETING
1313
1339
}
1314
1340
1315
1341
// -------- invokeOnCompletion nodes
0 commit comments