How Coroutine Works Internally (Continutation) (Part-2.2)

This is the third article in the series on how Kotlin coroutines work internally. If you haven’t read the first article, you can find it…

How Coroutine Works Internally (Continutation) (Part-2.2)

This is the third article in the series on how Kotlin coroutines work internally. If you haven’t read the first article, you can find it here and the second one here. In this article, I will delve into the Continuation, which is implemented within the Kotlin Standard Library.

Continuation

The Continuation interface is defined in the Kotlin Standard Library. It is a straightforward generic interface that includes one public property, context, of type CoroutineContext, and a single public function, resumeWith, which takes a parameter of type Result<T> and returns Unit.

Implementation of Continuation

interface Continuation<T> { 
 
    public val context: CoroutineContext 
 
    public fun resumeWith(result: Result<T>) 
 
}

There are some helper function defined on Continuation interface. Let’s look at it:-

public inline fun <T> Continuation<T>.resume(value: T): Unit = 
    resumeWith(Result.success(value)) 
 
public inline fun <T> Continuation<T>.resumeWithException(exception: Throwable): Unit = 
    resumeWith(Result.failure(exception)) 
 
public inline fun <T> Continuation( 
    context:CoroutineContext, 
    crossinline resumeWith:(Result<T>) -> Unit 
) :Continuation<T> = object :Continuation<T> { 
    override val context: CoroutineContext 
        get() = context 
 
    override fun resumeWith(result: Result<T>) = resumeWith(result) 
}

These helper functions are designed to streamline the use of continuations, offering a more convenient and intuitive interface for coroutine management while abstracting away much of the underlying complexity.

  • resume Extension: This extension invokes the resumeWith function, handling the successful completion of a continuation.
  • resumeWithException Extension: This extension calls the resumeWith function, but for the failure case, managing exceptions within the continuation.
  • Continuation Function: This function accepts context and resumeWith parameters, creating a continuation object with the provided values and returning it.

SafeContinuation Class

This internal class implements the Continuation interface and is an expect class, meaning it has platform-specific implementations for different targets. Let's examine its implementation details.

internal expect class SafeContinuation<in T> : Continuation<T> { 
    internal constructor(delegate: Continuation<T>, initialResult: Any?) 
 
    @PublishedApi 
    internal constructor(delegate: Continuation<T>) 
 
    @PublishedApi 
    internal fun getOrThrow(): Any? 
 
    override val context: CoroutineContext 
    override fun resumeWith(result: Result<T>): Unit 
}

The actual implementation details of SafeContinuation vary across different Kotlin targets. I won't go into those specifics here, but if you're interested, you can explore the implementation details here.

Usage of Continuation

The Continuation object is crucial in managing the state of a coroutine. When a coroutine is suspended, all its state and local variables are preserved within the Continuation object. As a result, the coroutine's frame is removed from the call stack, freeing up the thread for other operations. However, the Continuation object itself remains in memory, allowing the coroutine to be resumed later with its state intact. I will explore the mechanisms behind this state preservation in upcoming articles.

The SafeContinuation class supports interceptors, which enable operations like thread switching within coroutines. This functionality is facilitated through the CoroutineInterceptor and is integrated within SafeContinuation. (I will discuss this working mechanism in later articles.)

CPS(Continuation Passing Style)

Although we do not explicitly pass the Continuation parameter in suspend functions, the Kotlin compiler automatically transforms each suspend function by adding a parameter of type Continuation, which is subsequently passed down to every suspend function call.

Conclusion

Typically, when utilizing Kotlin coroutines, developers do not directly interact with the Continuation object; however, library creators aiming to develop upon the Kotlin coroutine library must possess a comprehensive understanding of its underlying mechanisms.

In subsequent discussions, we will explore the Kotlin compiler’s support for coroutines, where I will illustrate the practical usage of Continuation. However, this article does not delve into those specifics, as there are several other foundational topics that need to be addressed beforehand.In the next part of this series, we will continue exploring the Kotlin standard library for coroutines.

If you have any questions, feel free to ask by replying! If you enjoyed the article, please give it a clap! Don’t forget to follow me on LinkedIn and Twitter for more updates!

You can read the fourth article here.