What does the Kotlin compiler actually do to a `suspend` function under the hood? Describe the Continuation parameter it adds.
answer
- Hidden Continuation<T> parameter appended
- resumeWith(Result) delivers value or exception
- Return type becomes Any?
- COROUTINE_SUSPENDED sentinel
- suspend calls suspend because continuation is threaded down
basics
~20 sThe compiler rewrites a suspend function to take a hidden extra parameter representing 'what to do next'. When the function pauses, it hands work back to that callback and resumes later instead of blocking a thread.
solid answer
~40 sKotlin compiles `suspend` functions using CPS (Continuation-Passing Style). The compiler adds one hidden parameter of type `Continuation<T>` to the signature: a `suspend fun foo(): String` effectively becomes `fun foo(c: Continuation<String>): Any?`. Instead of returning normally, the function delivers its result by calling `continuation.resumeWith(Result.success(value))`. If it must wait (a suspension point), it returns the special marker `COROUTINE_SUSPENDED` and is later resumed via the same continuation. This is why suspend functions can only be called from other suspend functions or a coroutine builder: the caller supplies the continuation. No new thread is created — the call simply returns control to the caller until the awaited work completes.
code
kotlin · 12 lines// Source
suspend fun greet(name: String): String {
delay(100)
return "Hi $name"
}
// Conceptual compiled shape
fun greet(name: String, completion: Continuation<String>): Any? {
// ... may return COROUTINE_SUSPENDED, or eventually:
// completion.resumeWith(Result.success("Hi $name"))
return COROUTINE_SUSPENDED
}go deeper
Knows suspend doesn't block a thread and that the compiler adds a hidden continuation parameter.
Can name Continuation, resumeWith, and that return type becomes Any? returning COROUTINE_SUSPENDED when waiting.
Explains the fast path (no suspension) vs slow path and why suspend can only be called from suspend, tying it to the threaded-down continuation.
Frames CPS as a compile-time-only transform with zero runtime framework, and can compare it to async/await desugaring in other languages.
## What CPS means **CPS = Continuation-Passing Style.** Normally a function *returns* a value. In CPS, a function instead receives an extra argument — a *continuation* — describing 'the rest of the computation', and it delivers its result by **calling** that continuation rather than returning. Kotlin uses CPS to implement `suspend`. The compiler transforms every `suspend` function's signature by appending one hidden parameter of type `Continuation<T>`: ```kotlin // You write: suspend fun fetch(id: Int): String // Compiler emits (conceptually): fun fetch(id: Int, completion: Continuation<String>): Any? ``` ## The Continuation interface ```kotlin public interface Continuation<in T> { public val context: CoroutineContext public fun resumeWith(result: Result<T>) } ``` - `resumeWith(Result.success(v))` delivers a successful value. - `resumeWith(Result.failure(e))` delivers an exception. - `context` carries the `CoroutineContext` (dispatcher, job, etc.). The helper extensions `resume(value)` and `resumeWithException(e)` wrap `resumeWith`. ## Return type becomes `Any?` The rewritten function returns `Any?` because it has two possible outcomes: 1. It finishes synchronously and returns the actual value (which fits in `Any?`). 2. It must wait, so it returns the sentinel **`COROUTINE_SUSPENDED`** (from `kotlin.coroutines.intrinsics`). The caller sees this marker and knows 'I'm suspended; don't expect a value yet — I'll be resumed later via my continuation.' ## Why suspend can only call suspend A `suspend` function needs a continuation to resume into. That continuation is threaded down from a **coroutine builder** (`launch`, `async`, `runBlocking`) which creates the root continuation. Ordinary functions have no continuation to pass, so they cannot call suspend functions directly. ## Key takeaways - No thread is blocked at a suspension point; control returns to the caller. - The transformation is purely a compile-time rewrite — there is no magic runtime. - `Continuation`, `resumeWith`, `COROUTINE_SUSPENDED`, and the appended parameter are the four pieces to name.
- Why can't you call a suspend function from a regular (non-suspend) function?Because the suspend function needs a Continuation to resume into, and only suspend contexts or coroutine builders can supply one. A regular function has no continuation to pass.
- What is the runtime cost of the CPS transform when a suspend function does not actually suspend?Almost none: it runs straight through and returns its value like a normal call; the only overhead is the extra parameter and a fast-path check, no allocation of a suspended state.
Like leaving a self-addressed envelope (the continuation) with a clerk: instead of waiting at the counter you walk away, and they mail you the result when it's ready.
saying these in an interview costs you the question
- Claims a suspend function spawns or blocks a thread
- Thinks suspend is just syntactic sugar for a callback you write yourself
- Doesn't know about the hidden Continuation parameter
- Says the return type stays the same (ignores Any?/COROUTINE_SUSPENDED)