协程之间父子关系2-.Job取消过程

概述

接着上一章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的状态

parentJobstate值是ChildHandleNode,这个表示parentJob有一个子Job,即协程1。ChildHandleNodeonCancelling值是true,表示这个ChildHandleNode节点可以响应取消操作,如果是false,就不响应取消,什么意思呢?就是当这个协程发生取消操作时,会将这个取消操作通知给它的子协程,或者叫做协程的监听器,那么能不能处理父协程这个取消操作就看这个onCancelling的值。不监听取消操作,那什么时候监听呢?其实取消只是一个中途的过程,协程最终的状态是完成状态。有的比如ResumeOnCompletionInvokeOnCompletionResumeAwaitOnCompletion等都是在协程完成时,给这些监听器发出通知的。

在这里插入图片描述

协程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,接下来看JobImplcancel方法。

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,表示父协程发生了取消操作,同时也会导致它的子协程发生取消操作。

JobImplonCancelComplete值为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的值是ChildHandleNodecause既可以是Throwable类型,也可以是ParentJob,但是这里就是Throwable类型,即JobCancellationExceptionCompletedExceptionally表示协程完成了,但是是因为异常导致的完成。

如果是子协程,cause可以是ParentJobcreateCauseException方法就表示获取异常的原因,即从父协程中获取异常的原因。

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)
}

先获取statelist的值,此时parentJobstate的值是ChildHandleNode类型,getOrPromoteCancellingList方法就是获取list值的,如果不为null,就不会返回COMPLETING_RETRY,如果为null,先创建一个NodeList,然后把当前的ChildHandleNode添加到这个链表中,最后修改parentJobstate的值为这个NodeList

因为后面的Finishing需要这个NodeList,然后创建一个Finishing状态,表示正在结束,然后修改parentJobstate的值为这个Finishing

执行到notifyRootCause?.let { notifyCancelling(list, it) }时,我们看看parentJobstate

在这里插入图片描述

然后通知parentJob的子协程,要被取消了。再通知之前先把自己的状态链表中添加一个ListClosed节点。类型是LIST_CANCELLATION_PERMISSION,后面只能添加ListClosed节点,但不能再添加LIST_CANCELLATION_PERMISSIONListClosed节点到这个链表中。

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)
}

调用ChildHandleNodeinvoke方法,childJobChildHandleNode代表的那个协程,即协程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类型。调用ChildHandleNodeinvoke方法,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

再次进入tryMakeCancellingstate会被修改成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中的childCancellableContinuationImpl类型。CancellableContinuationImplstate的值是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
        }
    }
    
	......
}

CancellableContinuationImplstateCancelFutureOnCancel类型,然后被修改成了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,执行ChildContinuationdispose方法,其实这个方法就是把自己从链表中删除。

如下图,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)
}

stateFinishing,走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继续执行取消操作,向着完成状态前进。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值