As mentioned, the implementation of interceptContinuation() in CoroutineDispatcher is returning a DispatchedContinuation. This class is what pairs a CoroutineDispatcher with a Continuation<T>.
So let's take a look at it. First let's start with its constructor and the interfaces that it implements:
internal class DispatchedContinuation<in T>(
@JvmField val dispatcher: CoroutineDispatcher,
@JvmField val continuation: Continuation<T>
) : Continuation<T> by continuation, DispatchedTask<T> {
...
}
Notice that it takes a dispatcher and a continuation in the constructor, and it implements both Continuation<T> and DispatchedTask<T>. Its implementation of resume() and resumeWithException() is where the continuation and the dispatcher are connected together:
override fun resume(value: T) {
val context = continuation.context
if (dispatcher.isDispatchNeeded(context)) {
_state = value
resumeMode = MODE_ATOMIC_DEFAULT
dispatcher.dispatch(context, this)
} else
resumeUndispatched(value)
}
override fun resumeWithException(exception: Throwable) {
val context = continuation.context
if (dispatcher.isDispatchNeeded(context)) {
_state = CompletedExceptionally(exception)
resumeMode = MODE_ATOMIC_DEFAULT
dispatcher.dispatch(context, this)
} else
resumeUndispatchedWithException(exception)
}
As you can see, an important part of the implementation lies here. Whenever resume() or resumeWithException() are called, DispatchedContinuation will use the dispatcher if needed.