概述
接着上一章kotlin协程之间父子关系1-Job如何关联的,继续讲解协程的取消过程,顺便提到了异常对协程的影响。协程本来就很复杂,它的状态有多,一定要对照本章中的调试图片仔细观察。
示例
运行环境JDK11
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.9.0")
implementation("org.jetbrains.kotlin:kotlin-stdlib:2.0.21")
private fun logX(any: Any?) {
println("[Time:${LocalTime.now()} Thread:${Thread.currentThread().name}] $any ".trimIndent())
}
private fun wait(timeOut: Int, char: Char = '.') {
var time = 0L
val min = 150L
if (timeOut < min) return
while (time <= timeOut) {
Thread.sleep(min)
time += min
print(char)
}
println()
}
fun main() {
// 指定到单线程调度器
val dispatcher = Executors.newSingleThreadScheduledExecutor().asCoroutineDispatcher()
val parentJob = Job()
val scope = CoroutineScope(dispatcher + parentJob)
// 协程1
val coroutine1 = scope.launch {
logX("coroutine1 start")
// 协程2
val coroutine2 = launch {
logX("coroutine2 start")
// 让子job无法多等一会,观察父子job之间的关系
delay(Long.MAX_VALUE - 1)
logX("coroutine2 end")
}
// 协程3
val coroutine3 = launch {
logX("coroutine3 start")
// 让子job无法多等一会,观察父子job之间的关系
delay(Long.MAX_VALUE - 1)
logX("coroutine3 end")
}
// 这样协程1就不会执行完
coroutine3.join()
logX("coroutine1 end")
}
// 等子job
wait(10000)
// 取消
scope.cancel()
while (parentJob.isActive)
Thread.yield()
// 关闭线程池
dispatcher.close()
logX("main end")
}
输出
[Time:18:37:30.848391300 Thread:pool-1-thread-1] coroutine1 start
[Time:18:37:30.884485100 Thread:pool-1-thread-1] coroutine2 start
[Time:18:37:30.885488700 Thread:pool-1-thread-1] coroutine3 start
...................................................................
[Time:18:37:41.289701 Thread:main] main end
先看下取消前各个协程的状态。
parentJob的状态
parentJob
的state
值是ChildHandleNode
,这个表示parentJob
有一个子Job
,即协程1。ChildHandleNode
的onCancelling
值是true,表示这个ChildHandleNode
节点可以响应取消操作,如果是false,就不响应取消,什么意思呢?就是当这个协程发生取消操作时,会将这个取消操作通知给它的子协程,或者叫做协程的监听器,那么能不能处理父协程这个取消操作就看这个onCancelling
的值。不监听取消操作,那什么时候监听呢?其实取消只是一个中途的过程,协程最终的状态是完成状态。有的比如ResumeOnCompletion
、InvokeOnCompletion
、ResumeAwaitOnCompletion
等都是在协程完成时,给这些监听器发出通知的。
协程1的状态
协程1的state
指向的是NodeList
,这个双向列表中有3个节点,其中2个是ChildHandleNode
节点,即表示协程1内部有2个子协程(协程2和协程3),还有一个是ChildContinuation
,上一章kotlin协程之间父子关系1-Job如何关联的已经分析了这个节点是怎么来的了。这里简单说下,就是协程3使用了join
方法,那么协程1必须等协程3结束才能运行,万一协程1发生取消操作,该怎么联系协程3呢?就是通过这个ChildContinuation
来通知协程3的。那协程3结束了怎么让协程1知道呢?这个一会看一下协程3的状态就知道了。
协程2的状态
协程2的state值是ChildContinuation
类型,这个是协程2中使用了delay
搞出来的,使用delay
会导致协程给挂起,如果在挂起阶段发生了取消操作,这个ChildContinuation
就是用来通知delay的那个调度器删除任务。
协程3的状态
协程3的state
的值是NodeList
类型,协程3完成后,ResumeOnCompletion
是用来通知协程1可以恢复执行了,ResumeOnCompletion
是使用join
后添加到协程3的链表上的。
ChildContinuation
和协程2一样,是使用了delay搞出来的。
取消过程
parentJob的取消
parentJob
是示例中的顶层Job。它只有一个子协程,就是协程1。
public fun CoroutineScope.cancel(cause: CancellationException? = null) {
val job = coroutineContext[Job] ?: error("Scope cannot be cancelled because it does not have a job: $this")
job.cancel(cause)
}
job
就是示例中的parentJob
,接下来看JobImpl
的cancel
方法。
public override fun cancel(cause: CancellationException?) {
// 如果没有异常就使用默认的JobCancellationException
cancelInternal(cause ?: defaultCancellationException())
}
public open fun cancelInternal(cause: Throwable) {
cancelImpl(cause)
}
// 这个参数cause可以是Throwable;也可以是ParentJob
internal fun cancelImpl(cause: Any?): Boolean {
var finalState: Any? = COMPLETING_ALREADY
if (onCancelComplete) {
// make sure it is completing, if cancelMakeCompleting returns state it means it had make it
// completing and had recorded exception
// 此时cause的值就是JobCancellationException
finalState = cancelMakeCompleting(cause)
if (finalState === COMPLETING_WAITING_CHILDREN) return true
}
if (finalState === COMPLETING_ALREADY) {
finalState = makeCancelling(cause)
}
return when {
finalState === COMPLETING_ALREADY -> true
finalState === COMPLETING_WAITING_CHILDREN -> true
finalState === TOO_LATE_TO_CANCEL -> false
else -> {
afterCompletion(finalState)
true
}
}
}
cancelImpl
方法中的参数cause
类型是Any?
,可以是Throwable,即协程被取消的原因。还可以是ParentJob,表示父协程发生了取消操作,同时也会导致它的子协程发生取消操作。
JobImpl
的onCancelComplete
值为true。
cancelMakeCompleting
private fun cancelMakeCompleting(cause: Any?): Any? {
// 循环检查state
loopOnState { state ->
if (state !is Incomplete || state is Finishing && state.isCompleting) {
// already completed/completing, do not even create exception to propose update
return COMPLETING_ALREADY
}
// 此时cause的值就是JobCancellationException
val proposedUpdate = CompletedExceptionally(createCauseException(cause))
// proposedUpdate的值被包装成了CompletedExceptionally
val finalState = tryMakeCompleting(state, proposedUpdate)
if (finalState !== COMPLETING_RETRY) return finalState
}
}
JobImpl
发生了取消操作,所谓取消也是一种完成时的状态,state
的值是ChildHandleNode
,cause
既可以是Throwable
类型,也可以是ParentJob
,但是这里就是Throwable
类型,即JobCancellationException
,CompletedExceptionally
表示协程完成了,但是是因为异常导致的完成。
如果是子协程,cause
可以是ParentJob
,createCauseException
方法就表示获取异常的原因,即从父协程中获取异常的原因。
tryMakeCompleting
private fun tryMakeCompleting(state: Any?, proposedUpdate: Any?): Any? {
if (state !is Incomplete)
return COMPLETING_ALREADY
/*
* FAST PATH -- no children to wait for && simple state (no list) && not cancelling => can complete immediately
* Cancellation (failures) always have to go through Finishing state to serialize exception handling.
* Otherwise, there can be a race between (completed state -> handled exception and newly attached child/join)
* which may miss unhandled exception.
*/
if ((state is Empty || state is JobNode) && state !is ChildHandleNode && proposedUpdate !is CompletedExceptionally) {
if (tryFinalizeSimpleState(state, proposedUpdate)) {
// Completed successfully on fast path -- return updated state
return proposedUpdate
}
return COMPLETING_RETRY
}
// The separate slow-path function to simplify profiling
return tryMakeCompletingSlowPath(state, proposedUpdate)
}
tryMakeCompletingSlowPath
private fun tryMakeCompletingSlowPath(state: Incomplete, proposedUpdate: Any?): Any? {
// get state's list or else promote to list to correctly operate on child lists
val list = getOrPromoteCancellingList(state) ?: return COMPLETING_RETRY
// promote to Finishing state if we are not in it yet
// This promotion has to be atomic w.r.t to state change, so that a coroutine that is not active yet
// atomically transition to finishing & completing state
val finishing = state as? Finishing ?: Finishing(list, false, null)
// must synchronize updates to finishing state
val notifyRootCause: Throwable?
synchronized(finishing) {
// check if this state is already completing
if (finishing.isCompleting) return COMPLETING_ALREADY
// mark as completing
// 完成状态
finishing.isCompleting = true
// if we need to promote to finishing, then atomically do it here.
// We do it as early is possible while still holding the lock. This ensures that we cancelImpl asap
// (if somebody else is faster) and we synchronize all the threads on this finishing lock asap.
if (finishing !== state) {
if (!_state.compareAndSet(state, finishing)) return COMPLETING_RETRY
}
// ## IMPORTANT INVARIANT: Only one thread (that had set isCompleting) can go past this point
assert { !finishing.isSealed } // cannot be sealed
// add new proposed exception to the finishing state
// 此时finishing中还没有放入异常信息,所以isCancelling的值就是false
// 这个wasCancelling控制下面notifyCancelling方法的执行
val wasCancelling = finishing.isCancelling
// 从上面知道proposedUpdate的值就是CompletedExceptionally,它里面的cause值是JobCancellationException
// 这里把异常包装到finishing中,有了异常 finishing.isCancelling值就是true了
(proposedUpdate as? CompletedExceptionally)?.let { finishing.addExceptionLocked(it.cause) }
// If it just becomes cancelling --> must process cancelling notifications
// 刚初始化finishing,所以wasCancelling为false,那么notifyRootCause就不为null、
// 不为null就可以执行下面的notifyCancelling方法
notifyRootCause = finishing.rootCause.takeIf { !wasCancelling }
}
// process cancelling notification here -- it cancels all the children _before_ we start to wait them (sic!!!)
notifyRootCause?.let { notifyCancelling(list, it) }
// 有的协程都是并发的,如果还有子协程就往子协程的状态链表中插入一个ChildCompletion节点
// 当子协程完成时通知它的父协程
// now wait for children
// we can't close the list yet: while there are active children, adding new ones is still allowed.
val child = list.nextChild()
if (child != null && tryWaitForChild(finishing, child, proposedUpdate))
return COMPLETING_WAITING_CHILDREN
// turns out, there are no children to await, so we close the list.
list.close(LIST_CHILD_PERMISSION)
// some children could have sneaked into the list, so we try waiting for them again.
// it would be more correct to re-open the list (otherwise, we get non-linearizable behavior),
// but it's too difficult with the current lock-free list implementation.
val anotherChild = list.nextChild()
if (anotherChild != null && tryWaitForChild(finishing, anotherChild, proposedUpdate))
return COMPLETING_WAITING_CHILDREN
// otherwise -- we have not children left (all were already cancelled?)
return finalizeFinishingState(finishing, proposedUpdate)
}
先获取state
中list
的值,此时parentJob
中state
的值是ChildHandleNode
类型,getOrPromoteCancellingList
方法就是获取list
值的,如果不为null,就不会返回COMPLETING_RETRY
,如果为null,先创建一个NodeList
,然后把当前的ChildHandleNode
添加到这个链表中,最后修改parentJob
的state
的值为这个NodeList
。
因为后面的Finishing
需要这个NodeList
,然后创建一个Finishing
状态,表示正在结束,然后修改parentJob
的state
的值为这个Finishing
。
执行到notifyRootCause?.let { notifyCancelling(list, it) }
时,我们看看parentJob
的state
然后通知parentJob
的子协程,要被取消了。再通知之前先把自己的状态链表中添加一个ListClosed
节点。类型是LIST_CANCELLATION_PERMISSION
,后面只能添加ListClosed
节点,但不能再添加LIST_CANCELLATION_PERMISSION
的ListClosed
节点到这个链表中。
notifyCancelling
private fun notifyCancelling(list: NodeList, cause: Throwable) {
// first cancel our own children
onCancelling(cause)
// 往parentJob的state中的NodeList添加一个ListClosed节点
// 表示这个链表后面只能添加ListClosed节点了,但不能是LIST_CANCELLATION_PERMISSION类型的
list.close(LIST_CANCELLATION_PERMISSION)
// 开始通知当前协程的子协程
notifyHandlers(list, cause) { it.onCancelling }
// then cancel parent
// 当前协程发生取消操作通知它的父协程
cancelParent(cause) // tentative cancellation -- does not matter if there is no parent
}
当前协程发生取消操作使用cancelParent
通知它的父协程,看下父协程parentJob的状态。可是本来就是parentJob发生取消,然后通知它的子Job的,子Job怎么还要通知它的父协程。
notifyCancelling
方法可以通知它的子协程取消操作,也可以通知当前协程的父协程。当前协程发生取消操作,当前子协程可以自己发生取消,但父协程是不会管的。意思就是如果cause
的类型是CancellationException
,父协程是不会搭理这个异常的。除非子协程发生了除了CancellationException
之外的异常,就会影响父协程。下面简单看几个例子
比如:子协程自己取消
private fun logX(any: Any?) {
println("[Time:${LocalTime.now()} Thread:${Thread.currentThread().name}] $any ".trimIndent())
}
fun main() {
val parentJob = GlobalScope.launch {
logX("parentJob start")
val childJob1 = launch {
logX("childJob1 start")
delay(2000)
logX("childJob1 end")
}
delay(1000)
// 给childJob1取消
// 并不会影响parentJob和childJob2的执行
childJob1.cancel()
val childJob2 = launch {
logX("childJob2 start")
delay(2000)
logX("childJob2 end")
}
logX("parentJob end")
}
while (parentJob.isActive)
Thread.yield()
logX("main end")
}
输出
[Time:13:12:04.564789300 Thread:DefaultDispatcher-worker-2 @coroutine#1] parentJob start
[Time:13:12:04.593867700 Thread:DefaultDispatcher-worker-1 @coroutine#2] childJob1 start
[Time:13:12:05.603720800 Thread:DefaultDispatcher-worker-2 @coroutine#1] parentJob end
[Time:13:12:05.603720800 Thread:DefaultDispatcher-worker-2 @coroutine#3] childJob2 start
[Time:13:12:07.609540900 Thread:DefaultDispatcher-worker-3 @coroutine#3] childJob2 end
[Time:13:12:07.610543700 Thread:main] main end
比如:子协程发生除了CancellationException
之外的异常
private fun logX(any: Any?) {
println("[Time:${LocalTime.now()} Thread:${Thread.currentThread().name}] $any ".trimIndent())
}
fun main() {
val parentJob = GlobalScope.launch {
logX("parentJob start")
val childJob1 = launch {
logX("childJob1 start")
// 触发异常,所有协程都会发生取消操作
val error = 1/0
delay(2000)
logX("childJob1 end")
}
val childJob2 = launch {
logX("childJob2 start")
delay(2000)
logX("childJob2 end")
}
delay(2000)
logX("parentJob end")
}
while (parentJob.isActive)
Thread.yield()
logX("main end")
}
[Time:13:17:34.604578 Thread:DefaultDispatcher-worker-1 @coroutine#1] parentJob start
[Time:13:17:34.630647300 Thread:DefaultDispatcher-worker-2 @coroutine#2] childJob1 start
[Time:13:17:34.631649800 Thread:main] main end
[Time:13:17:34.631649800 Thread:DefaultDispatcher-worker-3 @coroutine#3] childJob2 start
3个协程都运行在不同的线程中。因为childJob1
发生异常,导致整个协程链都发生了取消操作。这个问题怎么处理呢?可以使用SupervisorJob
。
比如:使用SupervisorJob
,这样childJob1发生异常都不会影响到其他协程了。就是因为SupervisorJobImpl
重写了childCancelled
方法,里面没有处理异常逻辑了。
private class SupervisorJobImpl(parent: Job?) : JobImpl(parent) {
override fun childCancelled(cause: Throwable): Boolean = false
}
private fun logX(any: Any?) {
println("[Time:${LocalTime.now()} Thread:${Thread.currentThread().name}] $any ".trimIndent())
}
fun main() {
val parentJob = GlobalScope.launch {
logX("parentJob start")
// 这里使用了SupervisorJob()
val childJob1 = launch(SupervisorJob()) {
logX("childJob1 start")
try {
val error = 1 / 0
} catch (e: Exception) {
logX(e)
}
delay(2000)
logX("childJob1 end")
}
val childJob2 = launch {
logX("childJob2 start")
delay(2000)
logX("childJob2 end")
}
delay(2000)
logX("parentJob end")
}
while (parentJob.isActive)
Thread.yield()
logX("main end")
}
输出:
[Time:13:28:36.902478100 Thread:DefaultDispatcher-worker-1 @coroutine#1] parentJob start
[Time:13:28:36.929549400 Thread:DefaultDispatcher-worker-3 @coroutine#2] childJob1 start
[Time:13:28:36.929549400 Thread:DefaultDispatcher-worker-3 @coroutine#2] java.lang.ArithmeticException: / by zero
[Time:13:28:36.933559600 Thread:DefaultDispatcher-worker-2 @coroutine#3] childJob2 start
[Time:13:28:38.940832800 Thread:DefaultDispatcher-worker-1 @coroutine#1] parentJob end
[Time:13:28:38.940832800 Thread:DefaultDispatcher-worker-3 @coroutine#3] childJob2 end
[Time:13:28:38.940832800 Thread:DefaultDispatcher-worker-2 @coroutine#2] childJob1 end
[Time:13:28:38.941835100 Thread:main] main end
notifyHandlers
private inline fun notifyHandlers(list: NodeList, cause: Throwable?, predicate: (JobNode) -> Boolean) {
var exception: Throwable? = null
list.forEach { node ->
if (node is JobNode && predicate(node)) {
try {
node.invoke(cause)
} catch (ex: Throwable) {
exception?.apply { addSuppressed(ex) } ?: run {
exception = CompletionHandlerException("Exception in completion handler $node for $this", ex)
}
}
}
}
exception?.let { handleOnCompletionException(it) }
}
parentJob
中有ChildHandleNode
可以满足notifyHandlers
方法的条件
private class ChildHandleNode(
@JvmField val childJob: ChildJob
) : JobNode(), ChildHandle {
override val parent: Job get() = job
// 能否响应取消操作
override val onCancelling: Boolean get() = true、
// 子协程执行取消操作
override fun invoke(cause: Throwable?) = childJob.parentCancelled(job)
// 子协程发生异常,这个异常包括CancellationException和其他异常
// 父协程(job)是不处理子协程发来的CancellationException
// 但是父协程会响应除了CancellationException之外的异常
override fun childCancelled(cause: Throwable): Boolean = job.childCancelled(cause)
}
调用ChildHandleNode
的invoke
方法,childJob
即ChildHandleNode
代表的那个协程,即协程1,job
就是父Job,这里就是parentJob
协程1的取消
ChildHandleNode
中的ChildJob
类型就是StandaloneCoroutine
它的onCancelComplete
是false。
parentCancelled
// Parent is cancelling child
public final override fun parentCancelled(parentJob: ParentJob) {
cancelImpl(parentJob)
}
internal fun cancelImpl(cause: Any?): Boolean {
var finalState: Any? = COMPLETING_ALREADY
if (onCancelComplete) {
.....
}
if (finalState === COMPLETING_ALREADY) {
finalState = makeCancelling(cause)
}
return when {
finalState === COMPLETING_ALREADY -> true
finalState === COMPLETING_WAITING_CHILDREN -> true
finalState === TOO_LATE_TO_CANCEL -> false
else -> {
afterCompletion(finalState)
true
}
}
}
由于是父协成发生的取消操作,子协程也被迫取消,所以cancelImpl
方法中的cause
的值就是ParentJob
,即发生取消的原因,对于当前协程来说,要么是有异常发生了,要么是父协程通知子协程取消。
makeCancelling
private fun makeCancelling(cause: Any?): Any? {
var causeExceptionCache: Throwable? = null // lazily init result of createCauseException(cause)
loopOnState { state ->
when (state) {
is Finishing -> { // already finishing -- collect exceptions
......
}
is Incomplete -> {
// Not yet finishing -- try to make it cancelling
// 获取父Job异常的原因,即JobCancellationException
val causeException = causeExceptionCache ?: createCauseException(cause).also { causeExceptionCache = it }
if (state.isActive) {
// active state becomes cancelling
if (tryMakeCancelling(state, causeException)) return COMPLETING_ALREADY
} else {
.....
}
}
else -> return TOO_LATE_TO_CANCEL // already complete
}
}
}
StandaloneCoroutine
开始修改成取消状态,协程1的state
还是NodeList
,还是属于未完成状态(Incomplete)。
tryMakeCancelling
private fun tryMakeCancelling(state: Incomplete, rootCause: Throwable): Boolean {
.....
//
val list = getOrPromoteCancellingList(state) ?: return false
// Create cancelling state (with rootCause!)
// rootCause不为null,即Finishing中的isCancelling就是true
val cancelling = Finishing(list, false, rootCause)
if (!_state.compareAndSet(state, cancelling)) return false
// Notify listeners
// 继续通知它的子协程,即协程2和协程3
notifyCancelling(list, rootCause)
return true
}
tryMakeCancelling
方法返回false就会回到makeCancelling
方法中,makeCancelling
方法中会不停地循环检查state
,返回true表示已经修改成了Finishing
状态。
Finishing
可以表示正在完成和正在取消,如果rootCause
不为null就表示正在取消。
notifyCancelling
private fun notifyCancelling(list: NodeList, cause: Throwable) {
// first cancel our own children
onCancelling(cause)
// 往协程1的state中的NodeList添加一个ListClosed节点
// 表示这个链表后面只能添加ListClosed节点了,但不能是同种类型,即LIST_CANCELLATION_PERMISSION类型的
// 即不能再添加其他节点
list.close(LIST_CANCELLATION_PERMISSION)
// 开始通知当前协程的子协程,即协程2和协程3
notifyHandlers(list, cause) { it.onCancelling }
// then cancel parent
cancelParent(cause) // tentative cancellation -- does not matter if there is no parent
}
completeStateFinalization
当协程1等它子协程都结束后,开始通知parentJob
private fun completeStateFinalization(state: Incomplete, update: Any?) {
/*
* Now the job in THE FINAL state. We need to properly handle the resulting state.
* Order of various invocations here is important.
*
* 1) Unregister from parent job.
*/
parentHandle?.let {
it.dispose() // volatile read parentHandle _after_ state was updated
parentHandle = NonDisposableHandle // release it just in case, to aid GC
}
val cause = (update as? CompletedExceptionally)?.cause
/*
* 2) Invoke completion handlers: .join(), callbacks etc.
* It's important to invoke them only AFTER exception handling and everything else, see #208
*/
if (state is JobNode) { // SINGLE/SINGLE+ state -- one completion handler (common case)
try {
state.invoke(cause)
} catch (ex: Throwable) {
handleOnCompletionException(CompletionHandlerException("Exception in completion handler $state for $this", ex))
}
} else {
state.list?.notifyCompletion(cause)
}
}
在执行notifyCompletion
方法前,看看我们协程的状态
协程2的取消
协程2的state值是ChildContinuation
类型。调用ChildHandleNode
的invoke
方法,parentCancelled
方法和cancelImpl
方法可以看协程1的取消。我们直接到makeCancelling
方法。
makeCancelling
private fun makeCancelling(cause: Any?): Any? {
var causeExceptionCache: Throwable? = null // lazily init result of createCauseException(cause)
loopOnState { state ->
when (state) {
is Finishing -> { // already finishing -- collect exceptions
......
}
is Incomplete -> {
// Not yet finishing -- try to make it cancelling
// 获取父Job异常的原因,即JobCancellationException
val causeException = causeExceptionCache ?: createCauseException(cause).also { causeExceptionCache = it }
if (state.isActive) {
// active state becomes cancelling
if (tryMakeCancelling(state, causeException)) return COMPLETING_ALREADY
} else {
.....
}
}
else -> return TOO_LATE_TO_CANCEL // already complete
}
}
}
协程2的state值是ChildContinuation
类型,这个是delay搞出来的,目的就是为了当协程被取消时,删除调度器里的任务。
tryMakeCancelling
private fun tryMakeCancelling(state: Incomplete, rootCause: Throwable): Boolean {
......
// state.list为null会退出tryMakeCancelling方法
// 同时state会被修改成NodeList,然后又会重新进入tryMakeCancelling方法
val list = getOrPromoteCancellingList(state) ?: return false
// Create cancelling state (with rootCause!)
val cancelling = Finishing(list, false, rootCause)
if (!_state.compareAndSet(state, cancelling)) return false
// Notify listeners
notifyCancelling(list, rootCause)
return true
}
state
的值是ChildContinuation
类型,getOrPromoteCancellingList
方法返回null,直接返回到makeCancelling
方法,然后makeCancelling
方法又会循环检查state
,此时state
已将被修改成了NodeList
,
再次进入tryMakeCancelling
。state
会被修改成Finishing
。然后发出取消通知。
notifyCancelling
private fun notifyCancelling(list: NodeList, cause: Throwable) {
// first cancel our own children
onCancelling(cause)
// 往协程2的state中的NodeList添加一个ListClosed节点
// 表示这个链表后面只能添加ListClosed节点了,但不能同种的,即LIST_CANCELLATION_PERMISSION类型的
// 即不能再添加其他节点
list.close(LIST_CANCELLATION_PERMISSION)
//
notifyHandlers(list, cause) { it.onCancelling }
// then cancel parent
cancelParent(cause) // tentative cancellation -- does not matter if there is no parent
}
ChildContinuation
// Same as ChildHandleNode, but for cancellable continuation
private class ChildContinuation(
@JvmField val child: CancellableContinuationImpl<*>
) : JobNode() {
override val onCancelling get() = true
override fun invoke(cause: Throwable?) {
// 通知CancellableContinuationImpl取消delay任务
child.parentCancelled(child.getContinuationCancellationCause(job))
}
}
ChildContinuation
中的child
是CancellableContinuationImpl
类型。CancellableContinuationImpl
的state
的值是CancelFutureOnCancel
。
主要就是通知CancellableContinuationImpl取消delay任务
internal open class CancellableContinuationImpl<in T>(
final override val delegate: Continuation<T>,
resumeMode: Int
) : DispatchedTask<T>(resumeMode), CancellableContinuation<T>, CoroutineStackFrame, Waiter {
......
internal fun parentCancelled(cause: Throwable) {
if (cancelLater(cause)) return
cancel(cause)
// Even if cancellation has failed, we should detach child to avoid potential leak
detachChildIfNonResuable()
}
public override fun cancel(cause: Throwable?): Boolean {
_state.loop { state ->
if (state !is NotCompleted) return false // false if already complete or cancelling
// Active -- update to final state
val update = CancelledContinuation(this, cause, handled = state is CancelHandler || state is Segment<*>)
if (!_state.compareAndSet(state, update)) return@loop // retry on cas failure
// Invoke cancel handler if it was present
when (state) {
is CancelHandler -> callCancelHandler(state, cause) // 从调度器中删除delay的任务
is Segment<*> -> callSegmentOnCancellation(state, cause)
}
// Complete state update
//
detachChildIfNonResuable()
dispatchResume(resumeMode) // no need for additional cancellation checks
return true
}
}
......
}
CancellableContinuationImpl
的state
是CancelFutureOnCancel
类型,然后被修改成了CancelledContinuation
。
// Unregister from parent job
private fun detachChildIfNonResuable() {
// If instance is reusable, do not detach on every reuse, #releaseInterceptedContinuation will do it for us in the end
if (!isReusable()) detachChild()
}
internal fun detachChild() {
val handle = parentHandle ?: return
handle.dispose()
_parentHandle.value = NonDisposableHandle
}
parentHandle
就是ChildContinuation
,执行ChildContinuation
的dispose
方法,其实这个方法就是把自己从链表中删除。
如下图,ChildContinuation
已经从协程2的链表中删除了。只剩下ListClosed
节点了。
dispatchResume
private fun dispatchResume(mode: Int) {
if (tryResume()) return // completed before getResult invocation -- bail out
// otherwise, getResult has already commenced, i.e. completed later or in other thread
dispatch(mode)
}
internal fun <T> DispatchedTask<T>.dispatch(mode: Int) {
assert { mode != MODE_UNINITIALIZED } // invalid mode value for this method
val delegate = this.delegate
val undispatched = mode == MODE_UNDISPATCHED
if (!undispatched && delegate is DispatchedContinuation<*> && mode.isCancellableMode == resumeMode.isCancellableMode) {
// dispatch directly using this instance's Runnable implementation
val dispatcher = delegate.dispatcher
val context = delegate.context
if (dispatcher.isDispatchNeeded(context)) {
dispatcher.dispatch(context, this)
} else {
resumeUnconfined()
}
} else {
// delegate is coming from 3rd-party interceptor implementation (and does not support cancellation)
// or undispatched mode was requested
resume(delegate, undispatched)
}
}
经过dispatch
的调度,协程2在调度器(就是线程池)中重新运行,即执行run
方法
internal abstract class DispatchedTask<in T> internal constructor(
@JvmField var resumeMode: Int
) : SchedulerTask() {
.....
final override fun run() {
assert { resumeMode != MODE_UNINITIALIZED } // should have been set before dispatching
var fatalException: Throwable? = null
try {
val delegate = delegate as DispatchedContinuation<T>
val continuation = delegate.continuation
withContinuationContext(continuation, delegate.countOrElement) {
val context = continuation.context
val state = takeState() // NOTE: Must take state in any case, even if cancelled
val exception = getExceptionalResult(state)
/*
* Check whether continuation was originally resumed with an exception.
* If so, it dominates cancellation, otherwise the original exception
* will be silently lost.
*/
val job = if (exception == null && resumeMode.isCancellableMode) context[Job] else null
if (job != null && !job.isActive) {
// 取消
val cause = job.getCancellationException()
cancelCompletedResult(state, cause)
continuation.resumeWithStackTrace(cause)
} else {
if (exception != null) {
// 有异常
continuation.resumeWithException(exception)
} else {
// 正常
continuation.resume(getSuccessfulResult(state))
}
}
}
} catch (e: Throwable) {
// This instead of runCatching to have nicer stacktrace and debug experience
fatalException = e
} finally {
fatalException?.let { handleFatalException(it) }
}
}
.....
}
exception
不为null,走resumeWithException
。
public inline fun <T> Continuation<T>.resumeWithException(exception: Throwable): Unit =
resumeWith(Result.failure(exception))
我们在代码中写的协程代码块都是SuspendLambda
的子类,如下图SuspendLambda
的类关系。resumeWith
方法就在BaseContinuationImpl
类中。
internal abstract class BaseContinuationImpl(
// This is `public val` so that it is private on JVM and cannot be modified by untrusted code, yet
// it has a public getter (since even untrusted code is allowed to inspect its call stack).
public val completion: Continuation<Any?>?
) : Continuation<Any?>, CoroutineStackFrame, Serializable {
.....
public final override fun resumeWith(result: Result<Any?>) {
// This loop unrolls recursion in current.resumeWith(param) to make saner and shorter stack traces on resume
// 这个this就是协程2
var current = this
var param = result
while (true) {
// Invoke "resume" debug probe on every resumed continuation, so that a debugging library infrastructure
// can precisely track what part of suspended callstack was already resumed
probeCoroutineResumed(current)
with(current) {
val completion = completion!! // fail fast when trying to resume continuation without completion
val outcome: Result<Any?> =
try {
val outcome = invokeSuspend(param)
if (outcome === COROUTINE_SUSPENDED) return
Result.success(outcome)
} catch (exception: Throwable) {
Result.failure(exception)
}
releaseIntercepted() // this state machine instance is terminating
if (completion is BaseContinuationImpl) {
// unrolling recursion via loop
current = completion
param = outcome
} else {
// top-level completion reached -- invoke and return
completion.resumeWith(outcome)
return
}
}
}
}
......
}
调度器恢复协程2运行后,执行invokeSuspend
方法,进入状态基后,会检查result
是不是异常,如果是异常,就抛出异常,invokeSuspend
方法就会结束,outcome
就会是Result.failure(exception)
。
然后协程2的结果给completion
,它是StandaloneCoroutine
类型,它是AbstractCoroutine
类的子类。
resumeWith
public abstract class AbstractCoroutine<in T>(
parentContext: CoroutineContext,
initParentJob: Boolean,
active: Boolean
) : JobSupport(active), Job, Continuation<T>, CoroutineScope {
......
public final override fun resumeWith(result: Result<T>) {
val state = makeCompletingOnce(result.toState())
if (state === COMPLETING_WAITING_CHILDREN) return
afterResume(state)
}
......
}
result.toState()
将会是CompletedExceptionally
,表示异常完成。
makeCompletingOnce
internal fun makeCompletingOnce(proposedUpdate: Any?): Any? {
loopOnState { state ->
// state是Finishing
// proposedUpdate的值是CompletedExceptionally
val finalState = tryMakeCompleting(state, proposedUpdate)
when {
finalState === COMPLETING_ALREADY ->
throw IllegalStateException(
"Job $this is already complete or completing, " +
"but is being completed with $proposedUpdate", proposedUpdate.exceptionOrNull
)
finalState === COMPLETING_RETRY -> return@loopOnState
else -> return finalState // COMPLETING_WAITING_CHILDREN or final state
}
}
}
我们看下现在协程2的state的值如下图,是Finishing
状态。但是它的isCompleting
是false,isCancelling
的值是true。表示正在处于取消状态
tryMakeCompleting
private fun tryMakeCompleting(state: Any?, proposedUpdate: Any?): Any? {
if (state !is Incomplete)
return COMPLETING_ALREADY
/*
* FAST PATH -- no children to wait for && simple state (no list) && not cancelling => can complete immediately
* Cancellation (failures) always have to go through Finishing state to serialize exception handling.
* Otherwise, there can be a race between (completed state -> handled exception and newly attached child/join)
* which may miss unhandled exception.
*/
if ((state is Empty || state is JobNode) && state !is ChildHandleNode && proposedUpdate !is CompletedExceptionally) {
if (tryFinalizeSimpleState(state, proposedUpdate)) {
// Completed successfully on fast path -- return updated state
return proposedUpdate
}
return COMPLETING_RETRY
}
// The separate slow-path function to simplify profiling
return tryMakeCompletingSlowPath(state, proposedUpdate)
}
state
是Finishing
,走tryMakeCompletingSlowPath
。
tryMakeCompletingSlowPath
在执行tryMakeCompletingSlowPath
方法之前,我们再看下协程2的state的值,是Finishing
,但是它的isCompleting
是false。
private fun tryMakeCompletingSlowPath(state: Incomplete, proposedUpdate: Any?): Any? {
// get state's list or else promote to list to correctly operate on child lists
// state是Finishing,里面的list是有值的
val list = getOrPromoteCancellingList(state) ?: return COMPLETING_RETRY
// promote to Finishing state if we are not in it yet
// This promotion has to be atomic w.r.t to state change, so that a coroutine that is not active yet
// atomically transition to finishing & completing state
// 在tryMakeCancelling方法中创建了一个Finishing,但是它的isCompleting一直是false
val finishing = state as? Finishing ?: Finishing(list, false, null)
// must synchronize updates to finishing state
val notifyRootCause: Throwable?
synchronized(finishing) {
// check if this state is already completing
if (finishing.isCompleting) return COMPLETING_ALREADY
// mark as completing
// 到这里才会变成true
finishing.isCompleting = true
// if we need to promote to finishing, then atomically do it here.
// We do it as early is possible while still holding the lock. This ensures that we cancelImpl asap
// (if somebody else is faster) and we synchronize all the threads on this finishing lock asap.
if (finishing !== state) {
if (!_state.compareAndSet(state, finishing)) return COMPLETING_RETRY
}
// ## IMPORTANT INVARIANT: Only one thread (that had set isCompleting) can go past this point
assert { !finishing.isSealed } // cannot be sealed
// add new proposed exception to the finishing state
// isCancelling值是true
val wasCancelling = finishing.isCancelling
(proposedUpdate as? CompletedExceptionally)?.let { finishing.addExceptionLocked(it.cause) }
// If it just becomes cancelling --> must process cancelling notifications
// !wasCancelling值为false,所以notifyRootCause为null
notifyRootCause = finishing.rootCause.takeIf { !wasCancelling }
}
// process cancelling notification here -- it cancels all the children _before_ we start to wait them (sic!!!)
// notifyRootCause为null
notifyRootCause?.let { notifyCancelling(list, it) }
// now wait for children
// we can't close the list yet: while there are active children, adding new ones is still allowed.
val child = list.nextChild()
if (child != null && tryWaitForChild(finishing, child, proposedUpdate))
return COMPLETING_WAITING_CHILDREN
// turns out, there are no children to await, so we close the list.
list.close(LIST_CHILD_PERMISSION)
// some children could have sneaked into the list, so we try waiting for them again.
// it would be more correct to re-open the list (otherwise, we get non-linearizable behavior),
// but it's too difficult with the current lock-free list implementation.
val anotherChild = list.nextChild()
if (anotherChild != null && tryWaitForChild(finishing, anotherChild, proposedUpdate))
return COMPLETING_WAITING_CHILDREN
// otherwise -- we have not children left (all were already cancelled?)
return finalizeFinishingState(finishing, proposedUpdate)
}
在tryMakeCancelling
方法中创建了一个Finishing
,但是它的isCompleting
一直是false,到tryMakeCompletingSlowPath
方法中才会修改成true。Finishing
中的list只有一个ListClosed
节点了,不能再添加类型一样的ListClosed
节点了。这个时候相当于链表已经空了。nextChild
方法已经获取不到child了。
finalizeFinishingState
private fun finalizeFinishingState(state: Finishing, proposedUpdate: Any?): Any? {
....
// proposedUpdate的值是CompletedExceptionally
val proposedException = (proposedUpdate as? CompletedExceptionally)?.cause
// Create the final exception and seal the state so that no more exceptions can be added
val wasCancelling: Boolean
val finalException = synchronized(state) {
wasCancelling = state.isCancelling
val exceptions = state.sealLocked(proposedException)
val finalCause = getFinalRootCause(state, exceptions)
if (finalCause != null) addSuppressedExceptions(finalCause, exceptions)
finalCause
}
// Create the final state object
val finalState = when {
// was not cancelled (no exception) -> use proposed update value
finalException == null -> proposedUpdate
// small optimization when we can used proposeUpdate object as is on cancellation
finalException === proposedException -> proposedUpdate
// cancelled job final state
else -> CompletedExceptionally(finalException)
}
// Now handle the final exception
// finalException的值是类似 kotlinx.coroutines.JobCancellationException: Job was cancelled; job=JobImpl{Cancelling}@6d4ee1d8
if (finalException != null) {
//
val handled = cancelParent(finalException) || handleJobException(finalException)
if (handled) (finalState as CompletedExceptionally).makeHandled()
}
// Process state updates for the final state before the state of the Job is actually set to the final state
// to avoid races where outside observer may see the job in the final state, yet exception is not handled yet.
// wasCancelling为true
if (!wasCancelling) onCancelling(finalException)
onCompletionInternal(finalState)
// Then CAS to completed state -> it must succeed
// 从Finishing变成CompletedExceptionally
val casSuccess = _state.compareAndSet(state, finalState.boxIncomplete())
assert { casSuccess }
// And process all post-completion actions
completeStateFinalization(state, finalState)
return finalState
}
最后状态被修改成CompletedExceptionally
completeStateFinalization
private fun completeStateFinalization(state: Incomplete, update: Any?) {
parentHandle?.let {
// 把协程2从协程1的链表中删除
it.dispose() // volatile read parentHandle _after_ state was updated
parentHandle = NonDisposableHandle // release it just in case, to aid GC
}
val cause = (update as? CompletedExceptionally)?.cause
/*
* 2) Invoke completion handlers: .join(), callbacks etc.
* It's important to invoke them only AFTER exception handling and everything else, see #208
*/
// 之前还是 Finishing状态
if (state is JobNode) { // SINGLE/SINGLE+ state -- one completion handler (common case)
.....
} else {
// Finishing中只有ListClosed
state.list?.notifyCompletion(cause)
}
}
执行到这表示协程已经要完成了,同时dispose
方法就会使父协程删除这个子协程的关联。
private fun NodeList.notifyCompletion(cause: Throwable?) {
// 添加一个ListClosed(LIST_ON_COMPLETION_PERMISSION)
close(LIST_ON_COMPLETION_PERMISSION)
// 处理list中的ChildCompletion,但是现在没有,把示例中的join去掉就有了
notifyHandlers(this, cause) { true }
}
因为示例代码中我们使用了单线程的调度器执行协程,很多都是顺序执行的,其实真实的环境中都是多线程的,比如协程1可以运行在线程1中,协程2可以运行在线程2等,都是并行执行的。有一种可能就是协程1已经执行完我们写的代码,然后协程2才开始执行我们写的代码,这种时候协程1就会等协程2执行结束,怎么做到的?
在tryMakeCompletingSlowPath
方法中执行了tryWaitForChild
方法,协程1往协程2注册一个监听,它就是ChildCompletion
,它的onCancelling
值是false,表示它不监听取消状态。只有协程2状态变成完成时,才会触发这个监听执行,用来告诉父协程,我已经执行完成了,你可以继续恢复执行了。
private class ChildCompletion(
private val parent: JobSupport,
private val state: Finishing,
private val child: ChildHandleNode,
private val proposedUpdate: Any?
) : JobNode() {
override val onCancelling get() = false
override fun invoke(cause: Throwable?) {
parent.continueCompleting(state, child, proposedUpdate)
}
}
协程3的取消
协程3的state值是NodeList
类型。我们直接到makeCancelling
方法。
private fun makeCancelling(cause: Any?): Any? {
var causeExceptionCache: Throwable? = null // lazily init result of createCauseException(cause)
loopOnState { state ->
when (state) {
is Finishing -> { // already finishing -- collect exceptions
.....
}
is Incomplete -> {
// Not yet finishing -- try to make it cancelling
val causeException = causeExceptionCache ?: createCauseException(cause).also { causeExceptionCache = it }
if (state.isActive) {
// active state becomes cancelling
if (tryMakeCancelling(state, causeException)) return COMPLETING_ALREADY
} else {
.....
}
}
else -> return TOO_LATE_TO_CANCEL // already complete
}
}
}
tryMakeCancelling
private fun tryMakeCancelling(state: Incomplete, rootCause: Throwable): Boolean {
....
val list = getOrPromoteCancellingList(state) ?: return false
// Create cancelling state (with rootCause!)
val cancelling = Finishing(list, false, rootCause)
if (!_state.compareAndSet(state, cancelling)) return false
// Notify listeners
notifyCancelling(list, rootCause)
return true
}
notifyCancelling
private fun notifyCancelling(list: NodeList, cause: Throwable) {
// first cancel our own children
onCancelling(cause)
list.close(LIST_CANCELLATION_PERMISSION)
notifyHandlers(list, cause) { it.onCancelling }
// then cancel parent
cancelParent(cause) // tentative cancellation -- does not matter if there is no parent
}
ChildContinuation
private class ChildContinuation(
@JvmField val child: CancellableContinuationImpl<*>
) : JobNode() {
override val onCancelling get() = true
override fun invoke(cause: Throwable?) {
// 通知CancellableContinuationImpl
child.parentCancelled(child.getContinuationCancellationCause(job))
}
}
internal fun parentCancelled(cause: Throwable) {
// cause值就是JobCancellationException
if (cancelLater(cause)) return
cancel(cause)
// Even if cancellation has failed, we should detach child to avoid potential leak
detachChildIfNonResuable()
}
public override fun cancel(cause: Throwable?): Boolean {
_state.loop { state ->
if (state !is NotCompleted) return false // false if already complete or cancelling
// Active -- update to final state
val update = CancelledContinuation(this, cause, handled = state is CancelHandler || state is Segment<*>)
if (!_state.compareAndSet(state, update)) return@loop // retry on cas failure
// Invoke cancel handler if it was present
when (state) {
is CancelHandler -> callCancelHandler(state, cause) // 从调度器中删除delay的任务
is Segment<*> -> callSegmentOnCancellation(state, cause)
}
// Complete state update
detachChildIfNonResuable()
dispatchResume(resumeMode) // no need for additional cancellation checks
return true
}
}
具体分析看协程2的取消
ResumeOnCompletion
private class ResumeOnCompletion(
private val continuation: Continuation<Unit>
) : JobNode() {
override val onCancelling get() = false // 不响应取消,只在乎完成状态
override fun invoke(cause: Throwable?) = continuation.resume(Unit)
}
当协程3重新恢复调度后,它的链表中已经没有ChildContinuation
节点了,目前还剩下一个ResumeOnCompletion
节点,协程3最终会下执行到completeStateFinalization
方法中。
completeStateFinalization
private fun completeStateFinalization(state: Incomplete, update: Any?) {
parentHandle?.let {
// 把协程3从协程1的链表中删除
it.dispose() // volatile read parentHandle _after_ state was updated
parentHandle = NonDisposableHandle // release it just in case, to aid GC
}
val cause = (update as? CompletedExceptionally)?.cause
/*
* 2) Invoke completion handlers: .join(), callbacks etc.
* It's important to invoke them only AFTER exception handling and everything else, see #208
*/
// 之前还是 Finishing状态
if (state is JobNode) { // SINGLE/SINGLE+ state -- one completion handler (common case)
.....
} else {
// 还有一个ResumeOnCompletion
state.list?.notifyCompletion(cause)
}
}
private class ResumeOnCompletion(
private val continuation: Continuation<Unit>
) : JobNode() {
override val onCancelling get() = false
override fun invoke(cause: Throwable?) = continuation.resume(Unit)
}
private fun NodeList.notifyCompletion(cause: Throwable?) {
close(LIST_ON_COMPLETION_PERMISSION)
notifyHandlers(this, cause) { true }
}
最后finishing变成如下
ResumeOnCompletion中的continuation就是CancellableContinuationImpl,重新执行resume
。即重新恢复协程1的执行。协程1重新执行后,又会去检查状态,发现已经是Finishing取消状态了,协程1继续执行取消操作,向着完成状态前进。