kotlin协程中suspendCoroutineUninterceptedOrReturn()方法原理

4 阅读1分钟

kotlin协程中suspendCoroutineUninterceptedOrReturn()方法原理

public suspend inline fun <T> suspendCoroutineUninterceptedOrReturn(crossinline block: (Continuation<T>) -> Any?): T {
// ...
}

背景

当协程挂起以后,如果要恢复执行,需要拿到后续执行对应的续体。

作用

参考suspendCoroutineUninterceptedOrReturn()文档

原理

suspendCoroutineUninterceptedOrReturn()方法的调用处编译器会:

1. 将block中的代码inline到调用处
2. 将当前续体赋值给block中所有用到它的地方,此时block内的代码就拿到了续体,如果block中的代码返回COROUTINE_SUSPENDED挂起以后,后续可以通
过调用这个续体的resume()系列方法
来恢复后续的执行
3. 检查block返回值,如果是COROUTINE_SUSPENDED则调用:DebugProbesKt.probeCoroutineSuspended($continuation)

需要说明的是suspendCoroutineUninterceptedOrReturn()不同的调用位置拿到的续体类型也不一样:

1.在带suspend关键字的lambda中执行,则block中代码直接拿到这个lambda
2.如果在非inline方法末尾执行,则拿到编译器在编译时期插入到挂起方法末尾的续体
3.如果如果在非inline方法非末尾执行,这个挂起方法末尾有其他有效调用,编译器会在编译时期插在挂起方法内部插入一段生成ContinuationImpl续体的代码。
  在block中可以拿到这个续体。

例子

private suspend fun like() {
    suspendCoroutineUninterceptedOrReturn<String> {
        thread {
            it.resume("hah")
        }
        COROUTINE_SUSPENDED
    }
}
private static final Object like(final Continuation $completion) {
    int var2 = 0;
    ThreadsKt.thread$default(false, false, (ClassLoader) null, (String) null, 0, new Function0() {
        public final void invoke() {
            Result.Companion var10001 = Result.Companion;
            // suspendCoroutineUninterceptedOrReturn()在末尾调用,挂起like()方法内部不生成创建续体的代码
            // 直接使用传递给挂起方法的续体 
            $completion.resumeWith(Result.constructor - impl("hah"));
        }

        public Object invoke() {
            this.invoke();
            return Unit.INSTANCE;
        }
    }, 31, (Object) null);

    // 检查返回值
    Object var10000 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
    if (var10000 == IntrinsicsKt.getCOROUTINE_SUSPENDED()) {
        // 挂起则调用
        DebugProbesKt.probeCoroutineSuspended($completion);
    }

    return var10000 == IntrinsicsKt.getCOROUTINE_SUSPENDED() ? var10000 : Unit.INSTANCE;
}

当在挂起lambda内部调用suspendCoroutineUninterceptedOrReturn()时的情况类似。