Kotlin Coroutine 源码基于 1.5.2
读完这篇文章,你会知道什么?
- Job 相关类之间的作用和关系
- Job 的 定义状态 和 存储状态 的区别和联系
- ParentJob 和 ChildJob 的关联流程
- Job 的
cancel
流程,有完整的 结构划分 和 注释说明 - Job 的
join
流程 - Job 的异常分发流程
官方定义
-
一个 可取消 且 带生命周期 的任务;对应
Job#cancel
和Job#isActive
方法 -
Job 是具有 父节点 和 子节点 ,可以组成一个 多叉树 ;当 父节点 取消的时候,会递归取消自身所有的 子节点
-
当 子节点 出现了「除
CancellationException
之外」异常,将立即取消其 父节点 的任务,进而取消其 父节点下的所有子节点 ,可以通过SupervisorJob
定义此行为;同时这句话也有另外一个意思,即 子节点 产生了CancellationException
异常(比如Job#cancel
) ,不会影响 父节点 的运行 -
Job 一般分为 Corountine Job 和 CompletableJob 两种
-
Coroutine Job 一般通过
CoroutineScope#launch(block)
创建, 当 block 代码块执行完成,则代表当前 Job 执行完成「可能还需要等待 Child Job」 -
CompletableJob 一般顶层函数
Job()
创建,当调用CompletableJob#complete
时候代表完成,一般用于 CoroutineScope 的创建「因为当前 Job 的生命周期取决于 Scope ,由 Scope 管理 Job 的cancel
和complete
」
Kotlinpublic val LifecycleOwner.lifecycleScope: LifecycleCoroutineScope get() = lifecycle.coroutineScope public val Lifecycle.coroutineScope: LifecycleCoroutineScope get() { while (true) { val existing = mInternalScopeRef.get() as LifecycleCoroutineScopeImpl? if (existing != null) return existing /** * 这里使用 SupervisorJob 返回 CompletableJob 实例 */ val newScope = LifecycleCoroutineScopeImpl( this, SupervisorJob() + Dispatchers.Main.immediate ) if (mInternalScopeRef.compareAndSet(null, newScope)) { newScope.register() return newScope } } } public fun SupervisorJob(parent: Job? = null) : CompletableJob = SupervisorJobImpl(parent)
-
Job 相关的类
JobSupport
Kotlin
private class SupervisorJobImpl(parent: Job?) : JobImpl(parent) {
// ...
}
internal open class JobImpl(parent: Job?) : JobSupport(true), CompletableJob {
// ...
}
private class CompletableDeferredImpl<T>(
parent: Job?
) : JobSupport(true), CompletableDeferred<T>, SelectClause1<T> {
// ...
}
public interface CompletableDeferred<T> : Deferred<T> {
// ...
}
从 继承图 和 「Deferred<T>
或 CompletableJob
」 派生类可知,所有的 Job 继承实例都 直接或间接 继承了 JobSupport ,同时 JobSupport 实现 ParentJob 和 ChildJob , 不仅可以充当树上的 叶子节点 也可以充当 分支结点 ,从而组成一颗 多叉树 ;所以要想了解 Job 的内容,就要了解 JobSupport , 而要了解 JobSupport 的代码,需要理清下面「类之间的关系」,不然直接看源码就会 云里雾里
ParentJob 和 ChildJob
Kotlin
public interface ParentJob : Job {
@InternalCoroutinesApi
public fun getChildJobCancellationCause(): CancellationException
}
public interface ChildJob : Job {
@InternalCoroutinesApi
public fun parentCancelled(parentJob: ParentJob)
}
ParentJob 和 ChildJob 的接口是配合使用的
ParentJob#getChildJobCancellationCause
提供给 ChildJob 获取 ParentJob 自身cancel
的原因ChildJob#parentCancelled(parentJob: ParentJob)
提供 ParentJob 取消 自身ChildJob , 参数 ParentJob 就是让 ChildJob 调用ParentJob#getChildJobCancellationCause
的
Tips: 由 官方定义 可得,这里的返回值是 CancellationException
异常,CancellationException
只会从 树形结构 的 「父节点」 往 「子节点」 进行分发取消;因为 初始异常
早已经分发到父级,这里只需要 递归取消所有子节点 即可
CompletionHandler
Kotlin
public typealias CompletionHandler = (cause: Throwable?) -> Unit
internal actual abstract class CompletionHandlerBase actual constructor() : LockFreeLinkedListNode(), CompletionHandler {
/**
* 函数名为 invoke,函数类型为 (cause: Throwable?) -> Unit
*/
actual abstract override fun invoke(cause: Throwable?)
}
/**
* CompletionHandlerBase 是继承 CompletionHandler,返回自身即可
*/
internal actual inline val CompletionHandlerBase.asHandler: CompletionHandler get() = this
CompletionHandler 是 Job 完成的一个回调,同时也是一个函数类型, CompletionHandlerBase 继承 CompletionHandler 就会出现一个 abstract override fun invoke(cause: Throwable?)
方法需要重写,而 LockFreeLinkedListNode 是一个可在多线程操作的链表
ChildHandleNode
Kotlin
internal class ChildHandleNode(
@JvmField val childJob: ChildJob
) : JobCancellingNode(), ChildHandle {
override val parent: Job get() = job
/**
* CompletionHandler 实现
*/
override fun invoke(cause: Throwable?) = childJob.parentCancelled(job)
/**
* ChildHandle 的实现,用于取消 ParentJob 任务
*/
override fun childCancelled(cause: Throwable): Boolean = job.childCancelled(cause)
}
// JobCancellingNode 继承树
internal abstract class JobCancellingNode : JobNode()
internal abstract class JobNode : CompletionHandlerBase(), DisposableHandle, Incomplete {
/**
* 存储的是 ParentJob,在 makeNode 的时候进行赋值
*/
lateinit var job: JobSupport
/**
* Incomplete 的实现
*/
override val isActive: Boolean get() = true
override val list: NodeList? get() = null
/**
* DisposableHandle 的实现
*/
override fun dispose() = job.removeNode(this)
}
// ChildHandle 继承树
public interface ChildHandle : DisposableHandle {
public val parent: Job?
public fun childCancelled(cause: Throwable): Boolean
}
public interface DisposableHandle {
public fun dispose()
}
ChildHandleNode 是继承 ChildHandle 和 JobCancellingNode
ChildHandle 有三个功能
- parent 字段持有
ParentJob
childCancelled
用于 ChildJob 加入已完成 ParentJob 的时候,ChildJob 可以自动取消- DisposableHandle 是用于回收的,从上面看可知具体由 JobNode 实现
JobCancellingNode 相比于 JobNode 而言,可以在 「Cancelling 」 状态下就收到回调,而不用等到 完全取消 才收到回调,主要有三个功能
- 实现 CompletionHandlerBase ,可监听 Job 完成,主要用于监听 ParentJob
- 实现 DisposableHandle ,用于取消对 Job 完成的监听
- 实现 Incomplete , 是 Job 状态的子类,用于标记 Job 是否存活(
isActive
) 和 当前监听列表(NodeList
),其中 Job的状态 会在下面说明
Job 状态
定义状态
在 Job 的官方注释上状态分为 New 、Active 、Completing 、Cancelling 、Cancelled 和 Completed 状态, 而在对应状态上 Job#isActive
、Job#isCompleted
和 Job#isCancelled
值分别如表格所示
State | [isActive] | [isCompleted] | [isCancelled] |
---|---|---|---|
New (optional initial state) | false |
false |
false |
Active (default initial state) | true |
false |
false |
Completing (transient state) | true |
false |
false |
Cancelling (transient state) | false |
false |
true |
Cancelled (final state) | false |
true |
true |
Completed (final state) | false |
true |
false |
状态转移图
Kotlin
wait children
+-----+ start +--------+ complete +-------------+ finish +-----------+
| New | -----> | Active | ---------> | Completing | -------> | Completed |
+-----+ +--------+ +-------------+ +-----------+
| cancel / fail |
| +----------------+
| |
V V
+------------+ finish +-----------+
| Cancelling | --------------------------------> | Cancelled |
+------------+ +-----------+
wait children
New(初始状态) :设置了延迟执行, 如 launch(start = CoroutineStart.LAZY)
,需要调用方手动调用 Job#start
才会进入Active 状态
Active(初始状态) :如果没有设置 延迟执行 , 则在 launch
的时候就会处于这种状态;这里分为 两种 情况过渡到 Completing 状态
- CompletableJob 的时候,需要手动调用
CompletableJob#complete
进入 Completing(完成过渡状态),如转移图所述 - Coroutine Job 的时候,则会在 block 代码块 执行完成自动进入 Completing 状态
Completing/Cancelling(过渡状态) :当自身任务「已经完成」 或者 「已经取消」,等待所有的 ChildJob 「完成」 或者 「取消」
Completed/Cancelled(最终状态) :Job 及其 ChildJob 任务「已经完成」 或者 「已经取消」
存储状态
上面所述的是 定义状态 ,实际上 存储状态 是有点差异,主要差异在于 listeners 的数目,而这个 listeners 就是当前监听 Job 完成个数「从后面的插入代码和分发事件源码解析可知」
Kotlin
name state class public state description
------ ------------ ------------ -----------
EMPTY_N EmptyNew : New no listeners
EMPTY_A EmptyActive : Active no listeners
SINGLE JobNode : Active a single listener
SINGLE+ JobNode : Active a single listener + NodeList added as its next
LIST_N InactiveNodeList : New a list of listeners (promoted once, does not got back to EmptyNew)
LIST_A NodeList : Active a list of listeners (promoted once, does not got back to JobNode/EmptyActive)
COMPLETING Finishing : Completing has a list of listeners (promoted once from LIST_*)
CANCELLING Finishing : Cancelling -- " --
FINAL_C Cancelled : Cancelled Cancelled (final state)
FINAL_R <any> : Completed produced some result
New(初始状态) : 对应的存储状态为 EmptyNew 和 InactiveNodeList
- 从 listener 差异可知,EmptyNew 没有任何 listener , 而 InactiveNodeList 是有多个 listener;
- 从状态转移来说:一旦由 「EmptyNew」 推进到 「InactiveNodeList」 状态的时候,就不能回退「代码中移除 listener 的时候可以体现」
Kotlin
private val EMPTY_NEW = Empty(false)
private class Empty(override val isActive: Boolean) : Incomplete {
override val list: NodeList? get() = null
override fun toString(): String = "Empty{${if (isActive) "Active" else "New" }}"
}
internal class InactiveNodeList(override val list: NodeList) : Incomplete {
override val isActive: Boolean get() = false
}
Active(初始状态) :对应的存储状态为 EmptyActive 、JobNode 和 NodeList
- 从 listener 差异可知, EmptyActive 没有任何 listener , JobNode 只有一个 listener ,而 NodeList 是有多个 listener;
- 从状态转移来说:一旦由 「EmptyActive 或者 JobNode 」 推进到 「NodeList」 状态的时候,就不能回退;但 「EmptyActive」和 「JobNode」 互相切换;
- 这里比 New 状态多了个 JobNode ,因为在这里不仅是监听,同时也是链表节点,可以线程安全的插入进
Incomplete#list
中
Kotlin
private val EMPTY_ACTIVE = Empty(true)
internal abstract class JobNode : CompletionHandlerBase(), DisposableHandle, Incomplete {
lateinit var job: JobSupport
override val isActive: Boolean get() = true
override val list: NodeList? get() = null
override fun dispose() = job.removeNode(this)
}
internal class NodeList : LockFreeLinkedListHead(), Incomplete {
override val isActive: Boolean get() = true
override val list: NodeList get() = this
}
Completing/Cancelling(过渡状态) : 对应的存储状态为 Finishing , 使用 Finishing#isCancelling
来区分 Completing 还是 Cancelling
Kotlin
private class Finishing(
override val list: NodeList,
isCompleting: Boolean,
rootCause: Throwable?
) : SynchronizedObject(), Incomplete {
// ...
val isCancelling: Boolean get() = rootCause != null
// ...
}
Cancelled(最终状态) : 对应的存储状态为 CompletedExceptionally,主要「记录异常信息」和 「当前异常是否被处理」
Kotlin
internal open class CompletedExceptionally(
@JvmField public val cause: Throwable,
handled: Boolean = false
) {
private val _handled = atomic(handled)
val handled: Boolean get() = _handled.value
fun makeHandled(): Boolean = _handled.compareAndSet(false, true)
}
Completed(最终状态) : 对应的存储状态为 Any ,可以是任意类型,主要取决于 complete
传入的内容
Tips: 上面除 最终状态 外,其余都是 Incomplete 的子类,主要用于 「判断当前 Job 的状态」和 「存放监听列表」
Kotlin
internal interface Incomplete {
val isActive: Boolean
val list: NodeList? // 当为 Empty and JobNode 时候为空
}
状态转移图
Kotlin
New states Active states Inactive states
+---------+ +---------+ }
| EMPTY_N | ----> | EMPTY_A | ----+ } Empty states
+---------+ +---------+ | }
| | | ^ | +----------+
| | | | +--> | FINAL_* |
| | V | | +----------+
| | +---------+ | }
| | | SINGLE | ----+ } JobNode states
| | +---------+ | }
| | | | }
| | V | }
| | +---------+ | }
| +-------> | SINGLE+ | ----+ }
| +---------+ | }
| | |
V V |
+---------+ +---------+ | }
| LIST_N | ----> | LIST_A | ----+ } [Inactive]NodeList states
+---------+ +---------+ | }
| | | | |
| | +--------+ | |
| | | V |
| | | +------------+ | +------------+ }
| +-------> | COMPLETING | --+-- | CANCELLING | } Finishing states
| | +------------+ +------------+ }
| | | ^
| | | |
+--------+---------+--------------------+
从创建协程看父子关系的建立
前置知识已经准备好了,从日常使用的代码 lifecycleScope.launch
查看父子关系的建立
Kotlin
public fun CoroutineScope.launch(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> Unit
): Job {
val newContext = newCoroutineContext(context)
val coroutine = if (start.isLazy)
LazyStandaloneCoroutine(newContext, block) else
StandaloneCoroutine(newContext, active = true)
coroutine.start(start, coroutine, block)
return coroutine
}
private open class StandaloneCoroutine(
parentContext: CoroutineContext,
active: Boolean // 当前值为 true
) : AbstractCoroutine<Unit>(parentContext, initParentJob = true, active = active)
public abstract class AbstractCoroutine<in T>(
parentContext: CoroutineContext, // 父协程的上下文
initParentJob: Boolean, // 当前值为 true
active: Boolean // 当前值为 true
) : JobSupport(active), Job, Continuation<T>, CoroutineScope {
init {
// initParentJob 为 true,执行 JobSupport#initParentJob
if (initParentJob) initParentJob(parentContext[Job])
}
}
public open class JobSupport constructor(active: Boolean) : Job, ChildJob, ParentJob, SelectClause0 {
// 当前 this 为 ChildJob
protected fun initParentJob(parent: Job?) {
/**
* 如果当前没有父节点,就不用设置父节点的完成回调了
* NonDisposableHandle 为空白的对象,什么都不做
* 比如 GlobalScope.launch 的时候就没有 ParentJob
*/
if (parent == null) {
parentHandle = NonDisposableHandle
return
}
/**
* 确保 ParentJob 启动了
* 如果 ParentJob 是 Lazy 模式,也会同步启动,毕竟 ChildJob 已经启动 ParentJob 怎么能不启动
*/
parent.start()
// 注意这里是 ParentJob 关联 ChildJob
val handle = parent.attachChild(this)
parentHandle = handle
/**
* 如果在 attachChild 如果已经完成的话,会直接更改 ChildJob 的状态,所以重置当前状态
*/
if (isCompleted) {
handle.dispose() // 取消对 ParentJob 完成的监听
parentHandle = NonDisposableHandle // 重置 parentHandle 为空对象
}
}
// 当前 this 为 ParentJob
public final override fun attachChild(child: ChildJob): ChildHandle {
// ChildHandleNode(child).asHandler => CompletionHandler
return invokeOnCompletion(onCancelling = true, handler = ChildHandleNode(child).asHandler) as ChildHandle
}
/**
* 注意:当前 this 为 ParentJob
* onCancelling:当状态处于 「Cancelling」 的时候也会回调
* invokeImmediately: 当前已经处于对应可回调状态,则根据此值决定是否回调
* 这2个变量主要控制在特殊情况下「更快的回调」和「减少插入」
* 在 Incomplete 分支下都为 false 的时候,都是直接插入到 NodeList 中
*/
public final override fun invokeOnCompletion(
onCancelling: Boolean, // true
invokeImmediately: Boolean, // true
handler: CompletionHandler // ChildHandleNode(child)
): DisposableHandle {
/**
* makeNode方法做了二件事
* 1. 把 handler 包装 JobNode,但此处调用的 handler 为 ChildHandleNode,本身就是 JobNode,返回自身
* 2. JobNode#job 赋值为 ParentJob
*/
val node: JobNode = makeNode(handler, onCancelling)
// 循环读取 state 当前值,把当前生成的 node 线程安全的插入进对应的状态中
loopOnState { state ->
when (state) {
// 当前为没有监听的状态
is Empty -> {
if (state.isActive) {
// 对应存储状态转移:「Empty_Active」 -> 「JobNode」
if (_state.compareAndSet(state, node)) return node
} else {
// 对应存储状态转移:「EMPTY_NEW」 -> 「InactiveNodeList」,这里只是推进状态并未插入,下次循环在插入
promoteEmptyToNodeList(state)
}
}
// 除 Empty 之外的未完成状态
is Incomplete -> {
val list = state.list
if (list == null) {
// 对应存储状态转移:「JobNode」 -> 「NodeList」,这里只是推进状态并未插入,下次循环在插入
promoteSingleToNodeList(state as JobNode)
} else {
/**
* 能走到此分支的剩余 InactiveNodeList、NodeList 和 Finishing 状态
* 因为前面已经 ParentJob#start, 所以这里只剩下 NodeList 和 Finishing 状态
*/
var rootCause: Throwable? = null
var handle: DisposableHandle = NonDisposableHandle
// 当前 onCancelling 为 true 且 ParentJob 是处于 Finishing 状态
if (onCancelling && state is Finishing) {
synchronized(state) {
rootCause = state.rootCause
/**
* state.isCompleting = true 代表自身任务已经完成,在等待 ChildJob
* 如果当前已经有异常且自身Job完成,则在下面直接回调,不需要加入链表
* 而当前 Job 还没完成,则有概率出现其他异常,需要加入链表等待分发
*/
if (rootCause == null || handler.isHandlerOf<ChildHandleNode>() && !state.isCompleting) {
// 如果加入 list 失败则重试
if (!addLastAtomic(state, list, node)) return@loopOnState
if (rootCause == null) return node // Completing 直接返回
handle = node
}
}
}
if (rootCause != null) {
// 这里开始手动分发事件
if (invokeImmediately) handler.invokeIt(rootCause)
return handle
} else {
if (addLastAtomic(state, list, node)) return node
}
}
}
// 此分支是完成的状态
else -> {
/**
* 当前 ParentJob 已经完成,同时 invokeImmediately 为 true
* 直接调用 handler 回调给 ChildJob 告诉 ParentJob 已经完成,同时带上是异常结束还是正常结束的
*/
if (invokeImmediately) handler.invokeIt((state as? CompletedExceptionally)?.cause)
// 返回一个空的 Handle
return NonDisposableHandle
}
}
}
}
}
JobSupport#attachChild
在此方法中,Job#invokeOnCompletion
会有二种返回值
- NonDisposableHandle ,实现 DisposableHandle 和 ChildHandle
- ChildHandleNode ,实现 JobCancellingNode 和 ChildHandle 所以这里强转 ChildHandle 可正常运行
但对于一般 Job#invokeOnCompletion
调用来说,返回值取决于传入的 handler, 是另外二种返回值
- NonDisposableHandle ,实现 DisposableHandle 和 ChildHandle
- JobNode 及其子类,实现 CompletionHandlerBase 、DisposableHandle 和 Incomplete
总结:除 JobSupport#attachChild
外,其余都为 DisposableHandle 子类「主要用于 取消监听」
协程的取消
因为 Job 有 CompletableJob 和 Coroutine Job 两种, 所以 Job#cancel
也对应有2种;
在整个「取消 」过程除 cancel
入口外,分为两个步骤
- 第一步骤:
Job#tryMakeCompleting
和Job#tryMakeCompletingSlowPath
, 从 「InComplete 」 推进到 「Finishing 」 状态,主要等待 自身block 和 ChildJob 完成;同时正常结束也是相同步骤,只是 proposedUpdate 参数不为 CompletedExceptionally - 第二步骤:
Job#finalizeFinishingState
从 「Finishing 」 推进到 「终止状态」
cancel 入口
Kotlin
public open class JobSupport constructor(active: Boolean) : Job, ChildJob, ParentJob, SelectClause0 {
// ============================= cancel 的入口 =============================
public override fun cancel(cause: CancellationException?) {
cancelInternal(cause ?: defaultCancellationException())
}
public open fun cancelInternal(cause: Throwable) {
cancelImpl(cause)
}
// 返回值代表消费了此异常
internal fun cancelImpl(cause: Any?): Boolean {
var finalState: Any? = COMPLETING_ALREADY
/**
* onCancelComplete 为 true 则代表是 CompletableJob
*/
if (onCancelComplete) {
// 开始取消 Job 然后返回取消后的状态
finalState = cancelMakeCompleting(cause)
// 如果当前等待 ChildJob 完成,则什么都不做
if (finalState === COMPLETING_WAITING_CHILDREN) return true
}
/**
* 主要是处理 Coroutine Job 的情况
*/
if (finalState === COMPLETING_ALREADY) {
finalState = makeCancelling(cause)
}
return when {
finalState === COMPLETING_ALREADY -> true // 代表已经 Completing 过了
finalState === COMPLETING_WAITING_CHILDREN -> true // 等待 ChildJob 完成中
finalState === TOO_LATE_TO_CANCEL -> false // 已经完成,取消的太晚
else -> {
afterCompletion(finalState)
true
}
}
}
// ============================= CompletableJob 的 cancel =============================
/**
* CompletableJob 的 cancel,返回值包括三种
* 1. COMPLETING_ALREADY:代表已经 Completing 过了
* 2. COMPLETING_WAITING_CHILDREN: 等待 ChildJob 完成中
* 3. final state: 已经为最终状态
* tryMakeCompleting 方法 CAS 成功后状态至少为 「Finishing」
*/
private fun cancelMakeCompleting(cause: Any?): Any? {
// 循环获取 state 状态
loopOnState { state ->
// 如果当前已经完成 || 「已经是 Finishing 状态 且 Job block 已完成」
if (state !is Incomplete || state is Finishing && state.isCompleting) {
return COMPLETING_ALREADY // 代表已经 Completing 过了
}
// 「Cancelled」状态载体,其中内部会调用 ParentJob#getChildJobCancellationCause 方法
val proposedUpdate = CompletedExceptionally(createCauseException(cause))
// 尝试把当前「状态」推进到 「Cancelled」状态
val finalState = tryMakeCompleting(state, proposedUpdate)
// 推进成功返回状态
if (finalState !== COMPLETING_RETRY) return finalState
}
}
// ============================= Coroutine Job 的 cancel =============================
/**
* 虽然从上述代码流程会 CompletableJob 也会走进来
* 但是主要是处理 Coroutine Job 的取消
* 原因 CompletableJob 在 cancelMakeCompleting 方法中,状态至少为 「Finishing」甚至有可能为 「最终状态」,则
* 如果以 「Cancelling」状态进来,基本是跳过执行的
* 如果以 「Completing」 进来则变成 「Cancelling」
*
* Coroutine Job 和 CompletableJob 主要的区别在于 block 的执行,所以代码中根据 isActive 做处理
* 如果 isActive 为 true,则 block 正在执行,只需要变更状态等 block 执行完成后调用 complete 函数(下面会详细说明)
* 如果 isActive 为 false,就跟 CompletableJob 是一致的,直接调用 tryMakeCompleting 变更状态
*
* Coroutine Job 的 cancel,返回值包括四种
* 1. COMPLETING_ALREADY
* 2. TOO_LATE_TO_CANCEL
* 3. COMPLETING_WAITING_CHILDREN
* 4. finalState
*/
private fun makeCancelling(cause: Any?): Any? {
var causeExceptionCache: Throwable? = null
// 循环
loopOnState { state ->
when (state) {
is Finishing -> {
val notifyRootCause = synchronized(state) {
// Finishing 已经密封了不允许在添加异常了
if (state.isSealed) return TOO_LATE_TO_CANCEL
val wasCancelling = state.isCancelling
if (cause != null || !wasCancelling) {
val causeException = causeExceptionCache ?: createCauseException(cause).also { causeExceptionCache = it }
state.addExceptionLocked(causeException)
}
// 原先没有分发则返回 true 进行事件分发
state.rootCause.takeIf { !wasCancelling }
}
notifyRootCause?.let { notifyCancelling(state.list, it) }
return COMPLETING_ALREADY
}
// Finishing 之外的 Incomplete
is Incomplete -> {
// 得到异常
val causeException = causeExceptionCache ?: createCauseException(cause).also { causeExceptionCache = it }
if (state.isActive) {
// 转换为 Cancelling 等待 block 完成
if (tryMakeCancelling(state, causeException)) return COMPLETING_ALREADY
} else {
// block 未运行直接使用 tryMakeCompleting 过渡
val finalState = tryMakeCompleting(state, CompletedExceptionally(causeException))
when {
finalState === COMPLETING_ALREADY -> error("Cannot happen in $state")
finalState === COMPLETING_RETRY -> return@loopOnState
else -> return finalState
}
}
}
// 已经完成则返回
else -> return TOO_LATE_TO_CANCEL
}
}
}
}
Job#tryMakeCancelling
只需变更状态为 Cancelling 等待 block 执行完成后会自动下去,这里可能有人看不懂,而我们知道 CorountineScope.launch
的时候会得到 StandaloneCoroutine
Kotlin
private open class StandaloneCoroutine(
parentContext: CoroutineContext,
active: Boolean
) : AbstractCoroutine<Unit>(parentContext, initParentJob = true, active = active)
public abstract class AbstractCoroutine<in T>(
parentContext: CoroutineContext,
initParentJob: Boolean,
active: Boolean
) : JobSupport(active), Job, Continuation<T>, CoroutineScope {
public fun <R> start(start: CoroutineStart, receiver: R, block: suspend R.() -> T) {
// 这里会调用 CoroutineStart.invoke 方法
start(block, receiver, this)
}
/**
* start(block, receiver, this) 等价于 block.startCoroutineCancellable(receiver, completion)
* 而在 block 执行完成的时候就会调用 Continuation#resumeWith 方法,即当前方法
* 而具体为什么执行,这是 Kotlin Coroutine 相关知识了,有空再出一遍文章说明
*
* 可以看到调用了 makeCompletingOnce 函数,而 makeCompletingOnce 只有两个返回值
* 1. COMPLETING_WAITING_CHILDREN
* 2. 一个是最终状态
*/
public final override fun resumeWith(result: Result<T>) {
val state = makeCompletingOnce(result.toState())
if (state === COMPLETING_WAITING_CHILDREN) return
afterResume(state)
}
internal fun makeCompletingOnce(proposedUpdate: Any?): Any? {
loopOnState { state ->
/**
* 当前 block 完成,调用 tryMakeCompleting 过渡到最终状态了
*/
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
}
}
}
}
第一步骤
「Incomplete 」 和 proposedUpdate 过渡 「Finishing」状态
Kotlin
public open class JobSupport constructor(active: Boolean) : Job, ChildJob, ParentJob, SelectClause0 {
private fun tryMakeCompleting(state: Any?, proposedUpdate: Any?): Any? {
// 已经处于完成状态直接返回 COMPLETING_ALREADY
if (state !is Incomplete) return COMPLETING_ALREADY
/**
* 如果当前只有「0 或 1」个监听 && 不是 ChildJob 的监听 && 推进的不是「Cancelled」 状态
* 则可以快速转换为最终状态,原因
* 1. 不是 ChildJob 监听,可以不用等待 ChildJob 的完成
* 2. 「Cancelled」因为存在多线程,需要锁上分发才保证不会丢失,不适合快速转换
*/
if ((state is Empty || state is JobNode) && state !is ChildHandleNode && proposedUpdate !is CompletedExceptionally) {
// CAS 转换成功则直接返回最终状态
if (tryFinalizeSimpleState(state, proposedUpdate)) return proposedUpdate
// CAS 转换失败,重试
return COMPLETING_RETRY
}
return tryMakeCompletingSlowPath(state, proposedUpdate)
}
private fun tryMakeCompletingSlowPath(state: Incomplete, proposedUpdate: Any?): Any? {
// 从 「Incomplte」 获取 「NodeList」
val list = getOrPromoteCancellingList(state) ?: return COMPLETING_RETRY
// 「Finishing」 也属于 「Incomplete」,所以需要判断一下
val finishing = state as? Finishing ?: Finishing(list, false, null)
var notifyRootCause: Throwable? = null
synchronized(finishing) {
if (finishing.isCompleting) return COMPLETING_ALREADY
// 进入此方法代表 Job#block 已经执行完成
finishing.isCompleting = true
if (finishing !== state) {
// CAS 切换到 Finishing
if (!_state.compareAndSet(state, finishing)) return COMPLETING_RETRY
}
assert { !finishing.isSealed }
// 记录原先 isCancelling 状态
val wasCancelling = finishing.isCancelling
// 如果是推进到 「Cancelled」状态,则把当前异常添加进去
(proposedUpdate as? CompletedExceptionally)?.let { finishing.addExceptionLocked(it.cause) }
// finishing.rootCause != null && 之前不是取消异常 => 分发异常
notifyRootCause = finishing.rootCause.takeIf { !wasCancelling }
}
/**
* notifyCancelling 主要干了3件活
* 1. 自身取消
* 2. 仅通知完成回调的相关 JobCancellingNode 节点(invokeOnCompletion onCancelling 为 true),
* 而所有的 ChildJob 都是 JobCancellingNode 子类,所有的 ChildJob 都会接收到
* 3. 取消 ParentJob
*/
notifyRootCause?.let { notifyCancelling(list, it) }
// 查找 ChildHandleNode(即ChildJob),ChildJob 完成后会自动移除,在此列表都是为完成的
val child = firstChild(state)
// ChildJob 存在,则尝试等待 ChildJob 完成,返回等待 Child 状态
if (child != null && tryWaitForChild(finishing, child, proposedUpdate))
return COMPLETING_WAITING_CHILDREN
// 从 Finishing 推进到指定状态 「Cancelled」 或者 「Completed」状态
return finalizeFinishingState(finishing, proposedUpdate)
}
/**
* tailrec 是 Kotlin 的关键字,当自身函数是调用自身的话,会优化递归调用逻辑
* 返回值代表是否在等待 ChildJob
*/
private tailrec fun tryWaitForChild(state: Finishing, child: ChildHandleNode, proposedUpdate: Any?): Boolean {
// 对当前 ChildJob 进行完成监听,在完成的时候会调用下方 continueCompleting 方法
val handle = child.childJob.invokeOnCompletion(
invokeImmediately = false, // 已完成不需要直接执行
handler = ChildCompletion(this, state, child, proposedUpdate).asHandler
)
// 由建立父子关系可知,invokeOnCompletion 不为 NonDisposableHandle 代表监听 ChildJob 成功
if (handle !== NonDisposableHandle) return true
// 当前 ChildJob 已经完成查找下一个
val nextChild = child.nextChild() ?: return false
return tryWaitForChild(state, nextChild, proposedUpdate)
}
// 监听 ChildJob 任务完成后会调用此方法(由 ChildCompletion 调用)
private fun continueCompleting(state: Finishing, lastChild: ChildHandleNode, proposedUpdate: Any?) {
// 一致性检查
assert { this.state === state }
// 继续查找是否有 ChildJob 要等待
val waitChild = lastChild.nextChild()
if (waitChild != null && tryWaitForChild(state, waitChild, proposedUpdate)) return
// 没有任何 ChildJob 则调用 finalizeFinishingState 推进到最终状态
val finalState = finalizeFinishingState(state, proposedUpdate)
// 通知当前 Job 已经完成,一般用于恢复调用 withContext 或者 coroutineScope 所在的协程
afterCompletion(finalState)
}
}
第二步骤
从 「Finishing 」推进 proposedUpdate 「Completed 」或 「Cancelled」状态
Kotlin
public open class JobSupport constructor(active: Boolean) : Job, ChildJob, ParentJob, SelectClause0 {
private fun finalizeFinishingState(state: Finishing, proposedUpdate: Any?): Any? {
// 一致性检查
assert { this.state === state }
assert { !state.isSealed }
assert { state.isCompleting }
val proposedException = (proposedUpdate as? CompletedExceptionally)?.cause
var wasCancelling = false
// 得出最后异常
val finalException = synchronized(state) {
wasCancelling = state.isCancelling
val exceptions = state.sealLocked(proposedException)
val finalCause = getFinalRootCause(state, exceptions)
if (finalCause != null) addSuppressedExceptions(finalCause, exceptions)
finalCause
}
/**
* 根据条件计算最终转移状态
* 1. finalException 没有任何异常,则使用 proposedUpdate 的状态「any」
* 2. finalException 等于当前推进的异常,则使用 proposedUpdate
* 3. 有其他异常,则使用 CompletedExceptionally 包裹返回
*/
val finalState = when {
finalException == null -> proposedUpdate
finalException === proposedException -> proposedUpdate
else -> CompletedExceptionally(finalException)
}
// 处理异常[下文会交接]
if (finalException != null) {
val handled = cancelParent(finalException) || handleJobException(finalException)
if (handled) (finalState as CompletedExceptionally).makeHandled()
}
// 分发回调
if (!wasCancelling) onCancelling(finalException)
onCompletionInternal(finalState)
// 「Finishing」 推进到 「完成状态」
val casSuccess = _state.compareAndSet(state, finalState.boxIncomplete())
assert { casSuccess }
/**
* 收尾工作
* 1. 清理对 ParentJob 的监听
* 2. 分发监听当前 ParentJob 的完成事件,注意这里是 JobNode,所有的监听都是 JobNode 的子类,
* 所以分发给所有节点
*/
completeStateFinalization(state, finalState)
// 返回最后状态
return finalState
}
}
协程的等待
其实 Job#join
的挂起函数本质也是等待 Job 的完成,所以源码也是调用 Job#invokeOnCompletion
设置 Job 的完成回调,在收到回调之后恢复 Job#join
挂起函数的运行
Kotlin
public open class JobSupport constructor(active: Boolean) : Job, ChildJob, ParentJob, SelectClause0 {
public final override suspend fun join() {
// 检测 Job 环境是否为运行状态,如果不是则直接抛出 CancellationException 异常,不用挂起等待
if (!joinInternal()) {
/**
* 注意:这里 coroutineContext 为 suspend 函数上下文,而不是 JobSupport 的上下文
* 用于检查当前执行 suspend 函数的状态,如果已经是非 isActive 状态则抛出 CancelException
*/
coroutineContext.ensureActive()
return
}
// 当前在运行状态,则挂起等待完成
return joinSuspend()
}
private fun joinInternal(): Boolean {
// 循环读取状态
loopOnState { state ->
// 如果 state 不是 Incomplete 子类且「非Incomplete」已经是最终状态,无后继性,可以直接返回 false
if (state !is Incomplete) return false
// 判断是否处于已启动状态
if (startInternal(state) >= 0) return true
}
}
// 下方函数解析
private fun startInternal(state: Any?): Int {
when (state) {
is Empty -> {
if (state.isActive) return FALSE
// 对应「EMPTY_NEW」 -> 「EMPTY_ACTIVE」 状态转移
if (!_state.compareAndSet(state, EMPTY_ACTIVE)) return RETRY
onStartInternal()
return TRUE
}
is InactiveNodeList -> {
// 对应 「InactiveNodeList」 -> 「NodeList」状态转移
if (!_state.compareAndSet(state, state.list)) return RETRY // -1
onStartInternal()
return TRUE
}
else -> return FALSE
}
}
// 下方函数解析
private suspend fun joinSuspend() = suspendCancellableCoroutine<Unit> { cont ->
cont.disposeOnCancellation(invokeOnCompletion(handler = ResumeOnCompletion(cont).asHandler))
}
}
JobSupport#startInternal
返回值有3个
- RETRY 为 -1:代表 CAS 修改变量失败,需要重试
- FALSE 为 0:代表当前 Job 早已经启动
- TRUE 为 1: 代表 Job 启动成功
这里主要判断 Job 是否已经启动;注意 JobSupport#startInternal
在 高并发场景 下可能是以 最终状态 进入的,最后就会进入 JobSupport#joinSuspend
,但在 JobSupport#invokeOnCompletion
的时候会直接 调用完成回调 ;如上面 ChildJob 加入已完成的 ParentJob 直接取消是一致的
JobSupport#joinSuspend
主要流程解析:
-
ResumeOnCompletion 从下面可知,本质是在 Job 完成的时候恢复 cont 执行
Kotlinprivate class ResumeOnCompletion(private val continuation: Continuation<Unit>) : JobNode() { override fun invoke(cause: Throwable?) = continuation.resume(Unit) }
-
invokeOnCompletion
会返回一个 DisposableHandle ,然后传入cont.disposeOnCancellation
作为参数,这里的 DisposableHandle 主要是取消对 Job 的监听回调 -
使用 DisposeOnCancel 包装 DisposableHandle 然后作为参数传入
invokeOnCancellation
;本质的意思是在 suspend 函数cancel
的时候,能取消对 Job 的监听回调,这里的源码与 Job 基本是一致的Kotlinpublic fun CancellableContinuation<*>.disposeOnCancellation(handle: DisposableHandle): Unit = invokeOnCancellation(handler = DisposeOnCancel(handle).asHandler) private class DisposeOnCancel(private val handle: DisposableHandle) : CancelHandler() { override fun invoke(cause: Throwable?) = handle.dispose() }
协程异常捕获
当我们想捕获协程中的异常,经常在 launch
中 使用 CoroutineExceptionHandler 捕获,近似下面代码,此时会正常输出 catch throwable
Kotlin
fun main() {
val scope = CoroutineScope(SupervisorJob())
val job = scope.launch(CoroutineExceptionHandler { _, _ -> println("catch throwable") }) {
error("")
}
runBlocking {
job.join()
}
}
而当我们如果修改一下,把捕获改到内层,此时发现应用是崩溃的
Kotlin
fun main() {
val scope = CoroutineScope(SupervisorJob())
val job = scope.launch {
launch(CoroutineExceptionHandler { _, _ -> println("catch throwable")}) {
error("")
}
}
runBlocking {
job.join()
}
}
此时结合源码来分析下 异常分发 流程
Kotlin
public open class JobSupport constructor(active: Boolean) : Job, ChildJob, ParentJob, SelectClause0 {
// 从 「Finishing」 过渡到 「终止状态」
private fun finalizeFinishingState(state: Finishing, proposedUpdate: Any?): Any? {
// ...
if (finalException != null) {
val handled = cancelParent(finalException) || handleJobException(finalException)
if (handled) (finalState as CompletedExceptionally).makeHandled()
}
// ...
return finalState
}
// 当前为 ChildJob
private fun cancelParent(cause: Throwable): Boolean {
/**
* 如果当前为 coroutineScope 或者 withContext 等,则直接返回
* 原由这些是 suspend函数 转换 CoroutineScope 环境的
* 内部异常应由 suspend 所抛出,而不是走 Job 的事件分发
*/
if (isScopedCoroutine) return true
val isCancellation = cause is CancellationException
val parent = parentHandle
// 当前没有 ParentJob,则根据是否 CancellationException 返回对应异常是否被处理
if (parent === null || parent === NonDisposableHandle) {
return isCancellation
}
// 往父类分发 - 同时 CancellationException 永远返回 true 代表消耗
return parent.childCancelled(cause) || isCancellation
}
// 当前为 ParentJob
public open fun childCancelled(cause: Throwable): Boolean {
// 如果从子类过来的异常是 CancellationException 则直接返回,不取消
if (cause is CancellationException) return true
return cancelImpl(cause) && handlesException
}
}
从 Job#finalizeFinishingState
-> ChildJob#cancelParent
-> ParentJob#childCancelled
来看,只能看到异常分发流程,在回过去看 StandaloneCoroutine#handleJobException
对应的实现
Kotlin
private open class StandaloneCoroutine(
parentContext: CoroutineContext,
active: Boolean
) : AbstractCoroutine<Unit>(parentContext, active) {
override fun handleJobException(exception: Throwable): Boolean {
handleCoroutineException(context, exception)
return true
}
}
public fun handleCoroutineException(context: CoroutineContext, exception: Throwable) {
try {
context[CoroutineExceptionHandler]?.let {
it.handleException(context, exception)
return
}
} catch (t: Throwable) {
handleCoroutineExceptionImpl(context, handlerException(exception, t))
return
}
handleCoroutineExceptionImpl(context, exception)
}
从 context[CoroutineExceptionHandler]
可知开始使用外部注入的 CoroutineExceptionHandler , 可为什么还会异常呢;因为异常分发是先往 ParentJob 走,直到没有 ParentJob 为止或像 SupervisorJob 一样中断异常的分发,然后使用当前 Job 调用 handleCoroutineException
进行异常处理,所以只有最外层 ParentJob 或 SupervisorJob 才能使用 CoroutineExceptionHandler 处理异常
Kotlin
public fun SupervisorJob(parent: Job? = null) : CompletableJob = SupervisorJobImpl(parent)
private class SupervisorJobImpl(parent: Job?) : JobImpl(parent) {
// 中断往上的分发
override fun childCancelled(cause: Throwable): Boolean = false
}
刚刚我们说了 isScopedCoroutine 异常是由 suspend 函数抛出,我们来看下 coroutineScope
源码
Kotlin
public suspend fun <R> coroutineScope(block: suspend CoroutineScope.() -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return suspendCoroutineUninterceptedOrReturn { uCont ->
val coroutine = ScopeCoroutine(uCont.context, uCont)
coroutine.startUndispatchedOrReturn(coroutine, block)
}
}
internal open class ScopeCoroutine<in T>(
context: CoroutineContext,
@JvmField val uCont: Continuation<T> // unintercepted continuation
) : AbstractCoroutine<T>(context, true, true), CoroutineStackFrame {
override fun afterCompletion(state: Any?) {
// 恢复 suspend 函数运行
uCont.intercepted().resumeCancellableWith(recoverResult(state, uCont))
}
override fun afterResume(state: Any?) {
// 恢复 suspend 函数运行
uCont.resumeWith(recoverResult(state, uCont))
}
}
internal fun <T> recoverResult(state: Any?, uCont: Continuation<T>): Result<T> =
if (state is CompletedExceptionally)
Result.failure(recoverStackTrace(state.cause, uCont)) // 「Cancelled」返回异常
else
Result.success(state as T) // 「Completed」返回结果值
上述的 ScopeCoroutine 中, afterCompletion
和 afterResume
的区别
afterCompletion
和 afterResume
其实都是 Job#block 和 ChildJob 同时完成才调用,区别在于 afterCompletion
是 「after ChildJob Completion」即 ChildJob 后完成,而 afterResume
是「after Continuation resume」即 job#block 后完成;
对应 coroutineScope
来说
afterCompletion
代表 ChildJob 后完成,有可能改变了 CoroutineContext ,所以需要intercepted
切回原先的 CoroutineContext 在resume
afterResume
代表 Job#block 后完成,coroutineScope
不会修改 CoroutineContext ,所以可以直接resume
「Kotlin 和 Google 一样,在性能方面做到 极致」