For those who do not wish to spend time reading all thread here is a small recap:

First point:

Coroutine API suspendCoroutine function can’t be resumed immediately after calling its continuation.resume method up until it finishes execution of its body.

Second point:

ContinuationInterceptor doesn’t intercept a continuation of a suspension that has been resumed in the thread it had been started.

The original question was the following:

In the following sample suspendCoroutine function (Suspension) in exec2 sequentially executes its body code within some Coroutineexec1. But at some moment before Suspension execution is finished other code from another thread calls this Suspension continuation.resume. What’s expected is the Suspension to be resumed as soon as its continuation.resume is called. Thereby resuming the Coroutine at the same moment. Although what’s observed is the Suspension continues execution of its body and resumes Coroutine only after it’s done.

Why? Because a code within a suspendCoroutine function can be another long-running Coroutine which can decide by itself whether to run its code in current or parallel thread. And as long as it’s the long-running code it may be liable to cancellation at some point before it’s finished. Although with such Suspension behavior it’s impossible to actually cancel its body execution when it’s running in the thread it had been started in.
May be authors have a strong reason for that, which i’d like to know. Otherwise why not make a Suspension resumable at the moment of resumption not the moment it’s done executing its body.

The second point:

In the following sample i’m trying to track current Coroutine by intercepting it. Interceptor intercepts resumption after suspension only when Interceptor jumps to another thread (runner is not null). Otherwise (runner is null) if coroutine is going to be resumed after suspension in the same thread, Interceptor does not intercept resumption. Which make Interceptor useful only for jumping to threads but useless as the the general tool for actual interception of resumptions.

suspendCoroutine is a low-level primitive that is designed for conversion of callbacks into suspending functions. You should not use suspendCoroutine in your high-level code, precisely because invocation of suspendCoroutine exposes coroutine implementation details (a Continuation object) and can break the illusion of sequential execution in the way you’ve shown.

In particular, you should not mix your business-logic with suspendCoroutine invocations. Your high-level code should never work with Continuation objects directly. Continuation is exposed in Kotlin via suspendCoroutine only for the purpose of implementing low-level suspending functions.

Let us rewrite exec2 from your first example with this principle in mind:

Now exec2 does not use suspendCoroutine and it indeed executes sequentially just as it is supposed to. In particular, because exec2Cancel suspending function asynchronously completes with exception, the invocation of exec2 also completes with exception, never reaching “exec2 finished” line.

Because a code within a suspendCoroutine function can be another long-running Coroutine which can decide by itself whether to run its code in current or parallel thread

No. Code in suspendCoroutine cannot be a coroutine itself. The block of code that suspendCoroutine accepts is not a suspending function. Basically, the invocation of suspendCoroutine breaks out of coroutine into a regular non-suspending code where you are completely on your own in managing your asynchrony and concurrency via call-backs and other means.

Sorry, but I did not quite got what you were trying to achieve with your second example. Can you elaborate, please?

I have read your answer attentively and seems our thoughts do not sound yet. So let me be more specific.
Suppose i’m developing a lib based on Coroutines API. And i’m implementing a cancellable coroutine of my own.
Now let’s look at the code sample below. Here are functions runCoroutine and runSuspend that accept suspend code as parameter. Besides i want function runSuspend to be cancellable since it can execute long-running code. Let’s put aside the way it’s implemented here, and focus on the logic of the Coroutine API itself. The canceller is supposed to cancel current runSuspend body in the way the code in runCoroutine can continue execution after runSuspend. I expect it to be done as soon as canceller.cancel() is called. The executor instance (or its null value) here controls whether a continuation runs in the same or new thread.

Which is not desired behavior since [Coroutine 0] continues and completes at the 1.0 second, after [Coroutine 1] completion but not right after cancellation. That is in fact because of the logic of implementation of the suspendCoroutine{ continuation -> ...} API function which in spite of calling its continuation.resume(...) holds the actual resumption up to the moment it’s done executing its body.
At that point i expect you may start convincing me to implement my code in other way, but again the question is what is the logic behind this implementation of the Coroutine API. And if there is no principle design contradiction why not review it. It seems sensible to be able to resume continuation of suspendCoroutine{ continuation -> ...} right at the moment its continuation.resume is called. Why wait its body to finish?

Now about the second point.

In this slightly modified sample Canceller tries to track coroutine/suspension with help of Interceptor in order to cancel exactly that coroutine which is currently executing its code. In this case it should cancel [Coroutine 0] when it’s sleeping. Each time Interceptor intercepts continuation it passes the coroutine it belongs to to Canceller. So i expect the Canceller when its cancel() method is called, to cancel current coroutine (last intercepted).

Notice the absence of line 6 from prev printing. It’s because interceptor did not intercepted continuation of [Coroutine 0]. Consequently in line 6 of later printing actual coroutine is [Coroutine 0] but [Coroutine 1] is shown as current. And that’s why Canceller is trying to cancel [Coroutine 1] (see line 7, 8).
This shows that ContinuationInterceptor intercepts continuation only in case if Thread.currentThread isn’t the same that the ongoing suspension has been started from. This eliminates using Interceptor as the general tool for intercepting continuations, making it more like ThreadJumper but not actual Interceptor. So if there is no strong design consideration behind this decision why not review this logic? It seems sensible that Interceptor would intercept every continuation and decided itself whether to run it in the same thread or jump to another.

the question is what is the logic behind this implementation of the Coroutine API.

The short answer is: that is the only conceivable way it could be implemented. There is simply no way to abort the execution of the body of suspendCoroutine at a moment when resume is invoked. Attempting to do so whould lead to all sorts of constency problems.

This shows that ContinuationInterceptor intercepts continuation only in case if Thread.currentThread isn’t the same that the ongoing suspension has been started from.

That is not the case. Interception logic has nothing to do with the current thread. You can have all your coroutines executed in the single thread and interceptor would still work.

The ContinuationInterceptor intercepts all suspending functions that do suspend execution by design. Suspending function suspends coroutine when the invocation of suspendCoroutine returns without invoking resume. Suspending functions that did not suspend the execution (that is, they invoke resume before returning) are not intercepted.

Thank you for intelligible answer. Although it’s a pity to know there is no way to implement suspendCoroutine in other way in regard to abortion of its body, at least the reason seems compelling. In regard to the Interceptor issue i still have a question.
The name of the class ContinuationInterceptor as well as, i hope, its concept is supposed to literally mean intercepting Continuations. I.e. any Continuation that it comes across. Than why this selectivity in whether suspendCoroutine returns with or without invoking resume? Why not just follow the logic?
Let’s look at the following sample. It’s the simplest illustration of the issue. The function runCoroutine starts coroutine with interceptor that is meant to intercept its continuations.

By the logic you’ve presented it should skip suspension after the point 1 and just intercept suspension after the point 2. Which it does according to the printing above. But according to more general logic both suspensions involve continuations to resume the main coroutine. The decompiled coroutine (below is the simplified one) confirms that each suspension is the case of continuation. Ergo it’s the subject for ContinuationInterceptor since it is the continuation interceptor and not the JustSuspensionResumedFromOtherThreadContinuationInterceptor. Doesn’t it seem reasonable?
And in cases like one i’ve presented in the previous response it is really essential.

PS:
Correct me if i’m wrong but it seems the logic of the current implementation is grounded on the fact that it’s easier for a user to implement ContinuationInterceptor not carrying about the case when coroutine is resumed in the same thread as it had been suspended in. And because in this case there is no need to switch to another thread it may seem simpler just not to involve interceptor at all. In other words it is for the reason a user code would look like this:

It is a similar story with ContinuationInterceptor. There is no way to implement in any other way, since interceptor wraps around the execution it has to do so at the point where execution stack is unrolled, but execution stack is unrolled only when coroutine does suspend its execution, so we can only intercept at the actual suspension points.

This is not a bug, but a feature, because interceptors are designed to aid in managing threads for coroutines and a coroutine can only resume in another thread if it did suspend in its current thread, so the way interceptors are implemented perfectly aligns with the corresponding use-case.

Okay than. Although behavior (limitations) you’ve described above wouldn’t be redundant in the Coroutine API documentation. So please add it for clarity.
Especially misleading looks this example of Continuation interceptor from Informal description of Revision 3.2.

Continuation interceptor has an option to intercept and wrap the continuation that corresponds to the execution of initialCode, block1, and block2 from their resumption to the subsequent suspension points.

Since interceptor actually doesn’t have this option to interceptblock1, and block2. Particularly in case if f1 or/and f2 have already finished their execution at the point of await call.

Thanks for feedback. I’ve clarified the wording in the coroutines design document to ensure that the distinction between potentiation suspension points (suspending function invocation) and the actual suspensions is more clear. See the last commit. I hope it helps.