Coroutine挂起
从编译代码看挂起
用Kotlin简单写一个Coroutine的小例子,代码如下:
@InternalCoroutinesApi
fun main() {
println("main")
runBlocking {
println("main launch")
test1()
}
}
suspend fun test1() {
println("test1")
delay(100)
println("test1-end")
}
以上两个函数一个有suspend关键字修饰,一个没有,起对应编译后代码也有所不同,首先看一个main()函数编译后的源码:
public static final void main() {
String var0 = "main";
boolean var1 = false;
System.out.println(var0);
//Builders.kt中没有default()函数,该处调用逻辑不清楚,但是不影响主流程分析
BuildersKt.runBlocking$default((CoroutineContext)null, (Function2)(new Function2((Continuation)null) {
private CoroutineScope p$;
Object L$0;
int label;
@Nullable
public final Object invokeSuspend(@NotNull Object $result) {
//COROUTINE_SUSPENDED标志标示函数挂起,在源码中会判断该标志
Object var5 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
CoroutineScope $this$runBlocking;
switch(this.label) {
case 0:
ResultKt.throwOnFailure($result);
$this$runBlocking = this.p$;
String var3 = "main launch";
boolean var4 = false;
System.out.println(var3);
this.L$0 = $this$runBlocking;
this.label = 1;
if (TestCorKt.test1(this) == var5) {
return var5;
}
break;
case 1:
$this$runBlocking = (CoroutineScope)this.L$0;
ResultKt.throwOnFailure($result);
break;
default:
throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
}
return Unit.INSTANCE;
}
@NotNull
public final Continuation create(@Nullable Object value, @NotNull Continuation completion) {
Intrinsics.checkParameterIsNotNull(completion, "completion");
Function2 var3 = new <anonymous constructor>(completion);
var3.p$ = (CoroutineScope)value;
return var3;
}
public final Object invoke(Object var1, Object var2) {
return ((<undefinedtype>)this.create(var1, (Continuation)var2)).invokeSuspend(Unit.INSTANCE);
}
}), 1, (Object)null);
}
可以看到代码经过编译器编译后依然是调用Builders.runBlocking()函数,先不关注该函数的函数体,看一下该函数的参数定义:
public fun <T> runBlocking(
context: CoroutineContext = EmptyCoroutineContext,
block: suspend CoroutineScope.() -> T
): T {}
可以看到编译代码中new了一个Function2函数对象做为lambda表达式block参数的值,invoke()是Function2的入口函数。invoke()函数首先调用create()函数创建一个Continuation对象,然后再调用该对象的invokeSuspend()方法。下面看一下test1()方法是怎么样构造一个Continuation对象的:
@Nullable
public static final Object test1(@NotNull Continuation $completion) {
Object $continuation;
label20: {
if ($completion instanceof <undefinedtype>) {
$continuation = (<undefinedtype>)$completion;
if ((((<undefinedtype>)$continuation).label & Integer.MIN_VALUE) != 0) {
((<undefinedtype>)$continuation).label -= Integer.MIN_VALUE;
break label20;
}
}
$continuation = new ContinuationImpl($completion) {
// $FF: synthetic field
Object result;
int label;
@Nullable
public final Object invokeSuspend(@NotNull Object $result) {
this.result = $result;
this.label |= Integer.MIN_VALUE;
return TestCorKt.test1(this);
}
};
}
Object $result = ((<undefinedtype>)$continuation).result;
Object var5 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
String var1;
boolean var2;
switch(((<undefinedtype>)$continuation).label) {
case 0:
ResultKt.throwOnFailure($result);
var1 = "test1";
var2 = false;
System.out.println(var1);
((<undefinedtype>)$continuation).label = 1;
if (DelayKt.delay(100L, (Continuation)$continuation) == var5) {
return var5;
}
break;
case 1:
ResultKt.throwOnFailure($result);
break;
default:
throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
}
var1 = "test1-end";
var2 = false;
System.out.println(var1);
return Unit.INSTANCE;
}
可以看到编译后的代码中每次调用test1都会传入一个Continuation对象$completion,第一次调用test1()时ContinuationImpl.label=0,因此label & Integer.MIN_VALUE != 0是false,因此会执行new ContinuationImpl()逻辑。当执行了invokeSuspend()函数时会令 this.label |= Integer.MIN_VALUE,因此label & Integer.MIN_VALUE != 0为true,会break label20。
从源码看挂起
一个简单的代码断 runBlock {launch {}}, launch()函数可以新建一个谐程并start,这一过程的源码:
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
}
launch()函数返回一个Job对象,Coroutine类是Job类的子类,LazyStandaloneCoroutine类继承于StandaloneCoroutine类,StandaloneCoroutine类又继承于AbstractCoroutine类,最终会调用AbstractCoroutine类的start()方法:
public fun <R> start(start: CoroutineStart, receiver: R, block: suspend R.() -> T) {
initParentJob()
start(block, receiver, this)
}
start参数是一个函数类型对象CoroutineStart,因此这里调用start()方法即是调用CoroutineStart类中对应参数的invoke()方法:
public operator fun <R, T> invoke(block: suspend R.() -> T, receiver: R, completion: Continuation<T>) =
when (this) {
CoroutineStart.DEFAULT -> block.startCoroutineCancellable(receiver, completion)
CoroutineStart.ATOMIC -> block.startCoroutine(receiver, completion)
CoroutineStart.UNDISPATCHED -> block.startCoroutineUndispatched(receiver, completion)
CoroutineStart.LAZY -> Unit // will start lazily
}
一般情况下这里的类型为DEFAULT,几条路径都会调用createCoroutineUnintercepted()函数:
internal fun <R, T> (suspend (R) -> T).startCoroutineCancellable(receiver: R, completion: Continuation<T>) =
runSafely(completion) {
createCoroutineUnintercepted(receiver, completion).intercepted().resumeCancellableWith(Result.success(Unit))
}
createCoroutineUnintercepted()函数对应的源码是没有函数体的,估计也是由编译器生成相应代码来创建一个Continuation对象,intercepted()方法与线程调度有关,最后会调用resumeCancellableWith()方法:
public fun <T> Continuation<T>.resumeCancellableWith(result: Result<T>) = when (this) {
is DispatchedContinuation -> resumeCancellableWith(result)
else -> resumeWith(result)
}
这里的this is DispatchedContinuation为true:
//DispatchedContinuation.kt
inline fun resumeCancellableWith(result: Result<T>) {
val state = result.toState()
if (dispatcher.isDispatchNeeded(context)) {
_state = state
resumeMode = MODE_CANCELLABLE
dispatcher.dispatch(context, this)
} else {
executeUnconfined(state, MODE_CANCELLABLE) {
if (!resumeCancelled()) {
resumeUndispatchedWith(result)
}
}
}
}
//class CoroutineDispatcher.kt
public open fun isDispatchNeeded(context: CoroutineContext): Boolean = true
这里dispatcher被申明为CoroutineDispatcher,这里它的实际类型为BlockingEventLoop,因此会调用dispatcher.dispatch(context, this),这里的this是一个DispatchedContinueContinuation对象,其实现了Runnable接口,其run()方法在其父类DispatchedTask中进行重写,主要是恢复续体:
//DispatchedTask.kt
public final override fun run() {
val taskContext = this.taskContext
var fatalException: Throwable? = null
try {
val delegate = delegate as DispatchedContinuation<T>
val continuation = delegate.continuation
val context = continuation.context
val state = takeState() // NOTE: Must take state in any case, even if cancelled
withCoroutineContext(context, delegate.countOrElement) {
val exception = getExceptionalResult(state)
val job = if (resumeMode.isCancellableMode) context[Job] else null
/*
* Check whether continuation was originally resumed with an exception.
* If so, it dominates cancellation, otherwise the original exception
* will be silently lost.
*/
if (exception == null && job != null && !job.isActive) {
val cause = job.getCancellationException()
cancelResult(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 {
val result = runCatching { taskContext.afterTask() }
handleFatalException(fatalException, result.exceptionOrNull())
}
}
下面看一下dispatch分发过程:
//EventLoopImplBase.kt
public final override fun dispatch(context: CoroutineContext, block: Runnable) = enqueue(block)
public fun enqueue(task: Runnable) {
if (enqueueImpl(task)) {
// todo: we should unpark only when this delayed task became first in the queue
unpark()
} else {
DefaultExecutor.enqueue(task)
}
}
private fun enqueueImpl(task: Runnable): Boolean {
_queue.loop { queue ->
if (isCompleted) return false // fail fast if already completed, may still add, but queues will close
when (queue) {
null -> if (_queue.compareAndSet(null, task)) return true
is Queue<*> -> {
when ((queue as Queue<Runnable>).addLast(task)) {
Queue.ADD_SUCCESS -> return true
Queue.ADD_CLOSED -> return false
Queue.ADD_FROZEN -> _queue.compareAndSet(queue, queue.next())
}
}
else -> when {
queue === CLOSED_EMPTY -> return false
else -> {
// update to full-blown queue to add one more
val newQueue = Queue<Runnable>(Queue.INITIAL_CAPACITY, singleConsumer = true)
newQueue.addLast(queue as Runnable)
newQueue.addLast(task)
if (_queue.compareAndSet(queue, newQueue)) return true
}
}
}
}
}
可以看到这要是调用enqueueImpl()方法将一个DispatchedContinuation对象添加到EventLoopImplBase中的_queue队列尾部。
当runblock()方法中执行eventLoop?.processNextEvent()方法时,会从eventLoop中拿出该task执行,运行其run()方法,最终会执行continuation.resume(getSuccessfulResult(state))恢复续体。
还记得上一节编译协程后的代码中会多一个Continuation对象吗,Continuation是一个虚类,参考[ Kotlin协程源码分析-3 调用挂起函数.]可知其实现类如上图所示,此处调用Continuation对象的resumeWith()方法,即会调用实现类BaseContinuationImpl对象的方法:
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
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)
//与上一节的IntrinsicsKt.getCOROUTINE_SUSPENDED()对应
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) {
//BaseContinuationImpl对象在循环中的作用即是执行协程的运算逻辑
// unrolling recursion via loop
current = completion
param = outcome
} else {
//断点发现此处completion是一个DeferredCoroutine实例,会调用其父类AbstractCoroutine的方法,outcome即为计算结果
// top-level completion reached -- invoke and return
completion.resumeWith(outcome)
return
}
}
}
}
这段代码主要完成两项功能:
- 调用协程体中的计算逻辑,判断是否挂起
- 挂起的计算完成,则恢复挂起的协程
调用协程体中的计算逻辑是通过调用invokeSuspend()方法实现的,恢复协程则是通过AbstractCoroutine的resumeWith()方法实现。这里先不分析协议恢复过程,先通过一个小例子来自己创建一个协程:
class MyCoroutine() : Continuation<String> {
override fun resumeWith(result: Result<String>) {
println("MyCoroutine 回调resumeWith 返回的结果 " + result.getOrNull())
}
override val context: CoroutineContext
get() = kotlin.coroutines.EmptyCoroutineContext
}
//定义一个suspend方法,用于模拟计算过程
suspend fun mySuspendFun(): String {
return "hello1";
}
@InternalCoroutinesApi
fun testOne() {
val myCoroutineFun: suspend () -> String = {
//1.协程执行计算的地方
println("返回 hello结果")
mySuspendFun()
}
val myCoroutine = MyCoroutine()
//对应源码中 block.startCoroutineCancellable(receiver, completion)
val coroutine = myCoroutineFun.startCoroutineCancellable(myCoroutine)
}
@InternalCoroutinesApi
fun main() {
testOne()
}
运行结果为:
返回 hello结果
MyCoroutine 回调resumeWith 返回的结果 hello1
根据源码此处编写的lambda表达式myCoroutineFun即是对应launch()函数中block的代码,即协程中的计算逻辑。因此当调用startCoroutineCancellable()方法后,最终会走到Continuation对象的resumeWith()方法,调用其中的invokeSuspend()方法会触发myCoroutineFun逻辑的执行,接着会调用到completion.resumeWith(outcome)这句,这里的completion即是通过参数传入的myCoroutine对象,即会调用MyCoroutine中的resumeWith()方法。
协程的恢复
先看一个简单的例子,这个例子中用了delay()函数和await()函数:
fun main(args: Array<String>) =
runBlocking<Unit> { // 新建并启动 blocking 协程,运行在 main 线程上,等待所有子协程运行完成后才会结束
launch(Dispatchers.Unconfined) { // 新建并启动 launch 协程,没有指定所运行线程,一开始运行在调用者所在的 main 线程上
println("${Thread.currentThread().name} : launch start")
yield()
async(Dispatchers.Default) { // 新建并启动 async 协程,运行在 Dispatchers.Default 的线程池中
println("${Thread.currentThread().name} : async start")
delay(1000) // 挂起 async 协程 100 ms
println("${Thread.currentThread().name} : async end")
}.await() // 挂起 launch 协程,直到 async 协程结束
println("${Thread.currentThread().name} : launch end")
}
}
运行结果为:
main : launch start
DefaultDispatcher-worker-1 : async start
1.通过delay()看协程恢复与异步调用
delay()函数的定义:
//kotlinx.coroutines.Delay.kt
public suspend fun delay(timeMillis: Long) {
if (timeMillis <= 0) return // don't delay
return suspendCancellableCoroutine sc@ { cont: CancellableContinuation<Unit> ->
cont.context.delay.scheduleResumeAfterDelay(timeMillis, cont)
}
}
可以看到调用suspendCancellableCoroutine()函数,挂起了当前协程:
public suspend inline fun <T> suspendCancellableCoroutine(
crossinline block: (CancellableContinuation<T>) -> Unit
): T =
suspendCoroutineUninterceptedOrReturn { uCont ->
val cancellable = CancellableContinuationImpl(uCont.intercepted(), resumeMode = MODE_CANCELLABLE)
// NOTE: Before version 1.1.0 the following invocation was inlined here, so invocation of this
// method indicates that the code was compiled by kotlinx.coroutines < 1.1.0
// cancellable.initCancellability()
block(cancellable)
cancellable.getResult()
}
可以看到构造了一个CancellableContinuationImpl对象,最后通过其getResult()方法获取协程恢复时的结果,回想一下,这一过程是不是和上一节小例子中MyCoroutine有点类似,MyCoroutine类中重写的方法resumeWith(result:Resul<>)的作用就是将协程恢复时的结果回调出去。block lambda表达式执行的是DefaultExecutor类的父类EventLoopImplBase中的方法:
public override fun scheduleResumeAfterDelay(timeMillis: Long, continuation: CancellableContinuation<Unit>) {
val timeNanos = delayToNanos(timeMillis)
if (timeNanos < MAX_DELAY_NS) {
val now = nanoTime()
DelayedResumeTask(now + timeNanos, continuation).also { task ->
continuation.disposeOnCancellation(task)
schedule(now, task)
}
}
}
这里的timeMillis是需要延迟的时间,然后构造了一个DelayedResumeTask对象,disposeOnCancellation是与协程的取消有关,schedule()的定义为:
public fun schedule(now: Long, delayedTask: DelayedTask) {
when (scheduleImpl(now, delayedTask)) {
SCHEDULE_OK -> if (shouldUnpark(delayedTask)) unpark()
SCHEDULE_COMPLETED -> reschedule(now, delayedTask)
SCHEDULE_DISPOSED -> {} // do nothing -- task was already disposed
else -> error("unexpected result")
}
}
首先是调用scheduleImpl()方法进一步处理这个task:
private fun scheduleImpl(now: Long, delayedTask: DelayedTask): Int {
if (isCompleted) return SCHEDULE_COMPLETED
val delayedQueue = _delayed.value ?: run {
_delayed.compareAndSet(null, DelayedTaskQueue(now))
_delayed.value!!
}
return delayedTask.scheduleTask(now, delayedQueue, this)
}
先判断_delayed.value 是否为null,若为null则新构造一个DelayedTaskQueue对象,并赋值给delayedQueue,然后调用delayedTask的scheduleTask()方法,传入的参数是当前的时间、延迟队列和事件循环:
fun scheduleTask(now: Long, delayed: DelayedTaskQueue, eventLoop: EventLoopImplBase): Int {
if (_heap === DISPOSED_TASK) return SCHEDULE_DISPOSED // don't add -- was already disposed
delayed.addLastIf(this) { firstTask ->
if (eventLoop.isCompleted) return SCHEDULE_COMPLETED // non-local return from scheduleTask
if (firstTask == null) {
delayed.timeNow = now
} else {
val firstTime = firstTask.nanoTime
// compute min(now, firstTime) using a wrap-safe check
val minTime = if (firstTime - now >= 0) now else firstTime
// update timeNow only when going forward in time
if (minTime - delayed.timeNow > 0) delayed.timeNow = minTime
}
if (nanoTime - delayed.timeNow < 0) nanoTime = delayed.timeNow
true
}
return SCHEDULE_OK
}
这里的this是当前的DelayedResumeTask对象,并将其添加的延迟队列delayedQueue尾部,并返回SCHEDULE_OK。回到前面的when语句,可知SCHEDULE_OK对应的执行分支为判断shouldUnpark(delayedTask),若为true则调用unpark()唤醒当前线程。这里的线程即是DefaultExecutor类中的thread,其定义为:
override val thread: Thread
get() = _thread ?: createThreadSync()
private fun createThreadSync(): Thread {
return _thread ?: Thread(this, THREAD_NAME).apply {
_thread = this
isDaemon = true
start()
}
}
可以看到只要thread第一次被读,就会新建并运行一个名字为THREAD_NAME = "kotlinx.coroutines.DefaultExecutor"的线程,其target对象是DefaultExecutor,其实现了Runnable接口,其run()方法定义为:
override fun run() {
ThreadLocalEventLoop.setEventLoop(this)
registerTimeLoopThread()
try {
var shutdownNanos = Long.MAX_VALUE
if (!notifyStartup()) return
while (true) {
Thread.interrupted() // just reset interruption flag
var parkNanos = processNextEvent()
if (parkNanos == Long.MAX_VALUE) {
// nothing to do, initialize shutdown timeout
if (shutdownNanos == Long.MAX_VALUE) {
val now = nanoTime()
if (shutdownNanos == Long.MAX_VALUE) shutdownNanos = now + KEEP_ALIVE_NANOS
val tillShutdown = shutdownNanos - now
if (tillShutdown <= 0) return // shut thread down
parkNanos = parkNanos.coerceAtMost(tillShutdown)
} else
parkNanos = parkNanos.coerceAtMost(KEEP_ALIVE_NANOS) // limit wait time anyway
}
if (parkNanos > 0) {
// check if shutdown was requested and bail out in this case
if (isShutdownRequested) return
parkNanos(this, parkNanos)
}
}
} finally {
_thread = null // this thread is dead
acknowledgeShutdownIfNeeded()
unregisterTimeLoopThread()
// recheck if queues are empty after _thread reference was set to null (!!!)
if (!isEmpty) thread // recreate thread if it is needed
}
}
run()方法中主要运行了一个while循环,先调用processNextEvent()去处理下一个到期的task对象:
override fun processNextEvent(): Long {
// unconfined events take priority
if (processUnconfinedEvent()) return nextTime
// queue all delayed tasks that are due to be executed
val delayed = _delayed.value
if (delayed != null && !delayed.isEmpty) {
val now = nanoTime()
while (true) {
delayed.removeFirstIf {
if (it.timeToExecute(now)) {
enqueueImpl(it)
} else
false
} ?: break // quit loop when nothing more to remove or enqueueImpl returns false on "isComplete"
}
}
// then process one event from queue
dequeue()?.run()
return nextTime
}
该断代码也主要执行一个while循环,判断队列首原始是否耗完delay事件,若到达执行时间,则将其从delayed队列中移除,并加入到_queue队列中,再跳出循环,然后调用dequeue()从_queue队首移除一个task来执行,下面则会执行DelayedResumeTask的run()方法,最终会调用CancellableContinuationImp
private inner class DelayedResumeTask(
nanoTime: Long,
private val cont: CancellableContinuation<Unit>
) : DelayedTask(nanoTime) {
override fun run() { with(cont) { resumeUndispatched(Unit) } }
override fun toString(): String = super.toString() + cont.toString()
}