SafeCoroutine is one of the first things you’ll come across when exploring Kotlin’s coroutine principle. In this article, we’ll take a closer look at it.
SafeCoroutine role
SafeCoroutine is created in suspendCoroutine and serves two main purposes.
- Ensure that the suspension point of the suspendCoroutine (that is, the continuation argument passed into the lambda) is resumed only once
- Ensure that the suspend lambda argument resumes directly without suspending the thread
SuspendCoroutine indirectly invoked the suspendCoroutineUninterceptedOrReturn, By setting the transfer to the lambda suspendCoroutineUninterceptedOrReturn return value to set whether the current thread hanging
SuspendCoroutineUninterceptedOrReturn three steps
Step 1
You can see that the SafeContinuation constructor passes in c.I. Tercepted, the Continuation wrapped by the interceptor, where SafeContinuation is used as a proxy.
For example
C.ntercepted () is actually MyInterceptor, which simply gets the ContinuationInterceptor from a Continuation context.
[Note] The initial value of result is UNDECIDED, which will be used later.
Step 2
The first argument to the lambda passed in using the suspendCoroutine is actually a SafeContinuation, When it calls resume it calls resumeWith of SafeContinuation and then indirectly calls the continuation of its proxy.
If resume is called directly in the lambda passed in to suspendCoroutine (without switching threads)
For example
If you press Resume, SafeCoroutine’s resumeWith will be called
The RESULT field in SafeCoroutine is synchronized to the value passed in when RESULT is set to resume.
Step 3
Note the return value of this function, which directly determines whether the thread is suspended.
As you can see, the process will go here and the thread will not be suspended if COROUTINE_SUSPENDED is not returned.
If the context is switched instead of resume being called in the lambda passed to suspendCoroutine, result will be set to COROUTINE_SUSPENDED
For example
At this time, resume is not called in step 2, so result is still UNDECIDED, and will be set to COROUTINE_SUSPENDED in step 3. If the child thread resumes, it will still have SafeCoroutine resumeWith.
If result is set to COROUTINE_SUSPENDED, the proxied continuation is invoked indirectly.
Custom suspendCoroutineUninterceptedOrReturn
So you can see we can bypass suspendCoroutine direct call suspendCoroutineUninterceptedOrReturn expand coroutines ability. If direct call suspendCoroutineUninterceptedOrReturn can repeat calls resume.
The output
MyInterceptorContinuation@77b989be Start Done Start Done Start Done
Note that the return value is COROUTINE_SUSPENDED, otherwise the thread will not be suspended, and will resume automatically. In general, it will resume one more time, and there will be 4 pairs of Start and Done.