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.

  1. Ensure that the suspension point of the suspendCoroutine (that is, the continuation argument passed into the lambda) is resumed only once
  2. 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.