The idea of coroutines in kotlin was to abstract the notion of suspension and callbacks and write simple sequential code. You never need to worry if the coroutine is suspended or not, similar to threads.
What is the purpose of suspendCoroutineOrReturn and COROUTINE_SUSPENDED and in what case would you use them?
The
suspendCoroutineOrReturnandCOROUTINE_SUSPENDEDintrinsics were introduced very recently in 1.1 to address the particular stack overflow problem. Here is an example:Where
awaitsimply waits for completion:Let's have a look at the case when
workdoesn't really suspend, but returns the result immediately (for example, cached). The state machine, which is what coroutines are compiled into in Kotlin, is going to make the following calls:problem$stateMachine,await,CompletableFuture.whenComplete,await$lambda,ContinuationImpl.resume,problem$stateMachine,await, ...In essence, nothing is ever suspended and the state machine invokes itself within the same execution thread again and again, which ends up with
StackOverflowError.A suggested solution is to allow
awaitreturn a special token (COROUTINE_SUSPENDED) to distinguish whether the coroutine actually did suspend or not, so that the state machine could avoid stack overflow. Next,suspendCoroutineOrReturnis there to control coroutine execution. Here is its declaration:Note that it receives a block that is provided with a continuation. Basically it is a way to access the
Continuationinstance, which is normally hidden away and appears only during the compilation. The block is also allowed to return any value orCOROUTINE_SUSPENDED.Since this all looks rather complicated, Kotlin tries to hide it away and recommends to use just
suspendCoroutinefunction, which internally does all the stuff mentioned above for you. Here's the correctawaitimplementation which avoidsStackOverflowError(side note:awaitis shipped in Kotlin lib, and it's actually an extension function, but it's not that important for this discussion)But if you ever want to take over fine-graned control over coroutine continuation, you should call
suspendCoroutineOrReturnand returnCOROUTINE_SUSPENDEDwhenever an external call is made.