"In this notebook, I outline my current understand of why ProcessTest>>#testTerminateEverywhere fails."
ProcessTest>>#testTerminateEverywhere.

"Basically, this is a smoke test that asserts that the block can be terminated at any execution time correctly. However, in the current trunk, this test fails at step 332:"
block := [([[Notification signal]
ensure: []]
on: Notification do: [:ex | ex resume: 6])
* 7].
process := Process forContext: block asContext priority: Processor activePriority.
332 timesRepeat:
[process step].
self debugErrorWhile: [process terminate] in: process.

"So why is this? Let's investigate first how the process looked like before we sent it #terminate!"
process := Process forContext: block asContext priority: Processor activePriority.
332 timesRepeat:
[process step].
self debug: process.

"Okay, so we are just in the midst of unwinding the stack while doing 'ex resume: 6'. More precisely, what is the next unwind context we will run?"
process suspendedContext top.

"Okay, we are done with unwinding already. And what was the previous unwind block?"
ctx := process suspendedContext tempNamed: 'ctx'.

self inspect: (ctx tempAt: 1).

"That's the ensure: block from Context>>#handleSignal:, as expected."
"Now, let's investigate why #terminate can't terminate that process correctly."
terminator := self debug: [process terminate].
terminator := terminator copy stepInto; stepThroughUntil: [:ctx | ctx xnbWillSend: #resume].

"...the actual unwinding happens on the process itself, so we will debug that..."
terminator2 := self debug: terminator interruptedContext top.

terminator2 := terminator2 copy stepInto: #value.

terminator2 := terminator2 copy stepIntoUntil: [:ctx | ctx xnbWillSend: #runUntilReturnFrom:].

"Okay, now things get interesting. We are now going do complete the outermost halfway-unwound context:"
terminator2 := terminator2 copy stepInto; stepThroughUntil: [:ctx | ctx xnbWillSend: #jump].

"#runUntilReturnFrom: jumps into the halfway-unwound context to continue it and jumps back before leaving it through an inject guard context."
terminator2 := terminator2 copy stepInto; stepThroughUntil: [:ctx | ctx selector ~= #jump].

"...and here we are again in the unwinding method we saw above. Let's step one statement further..."
terminator2 := terminator2 copy stepInto.

"So now we should return to our guard context from #runUntilReturnFrom:, right? "
terminator2 interruptedContext tempNamed: 'ctx'.

"Oh no, we don't! We actually leave the unwinding method now, without ever returning to the guard context. But we still terminate thisContext to aContext, so we cut off the guard context completely."
terminator2 := terminator2 copy stepIntoUntil: [:ctx | ctx willReturnTopFromMethod].

"If we would step further, we would see that the execution would now *proceed* with the execution of the original block without returning to the termination logic ever again. At the end, it raises the initial #cannotReturn: simply because we end in the #unwindAndStop: context which is never meant to be reached directly and which is not a valid bottom context.
So the root cause seems to be that the termination request interrupts the unwinding method at a point where it already has identified the next unwind context (nil) but not yet executed it, and the termination then installs its own unwind context as a guard but the unwinding method never chooses it. This looks like we violated an expected transaction here. Next, we need to discuss how we can rewrite the unwinding method to avoid this transactional semantics."