[Cuis-dev] Unwind mechanism during termination is broken and inconsistent

Jaromir Matas mail at jaromir.net
Sat Apr 10 12:20:16 PDT 2021


Hi All,

I'd like to present to you a rewrite of #terminate and #isTerminated that fixes a few bugs and inconsistencies in process termination. I hoped it might interest you.

I'm a Smalltalk enthusiast with education background in math/CS. I've been experimenting with processes in Squeak lately and discovered a few bugs (or at least inconsistencies) in process termination and would like to offer and discuss a solution for Cuis.

The bugs are not unique to Cuis; Squeak/Pharo inherited them too and to a degree even Visual Works and VA are affected. The proposal presented here doesn't copy any VW or VA solution but rather implements a different approach :)

Before boring you to death I'll list the bugs:

1. #isTerminated falsely reports almost any bottom context as terminated

2. an active process termination bug causes an image freeze

3. a nested unwind bug: ensure blocks may get skipped during unwind

4. a failure to complete nested unwind blocks halfway thru execution

5. a failure to correctly execute a non-local return or an error in an unwind block

6. inconsistent semantics of protected blocks unwind during active vs. suspended process termination

The current implementation of #terminate uses three different approaches to terminate a process:
- the active process is terminated via a 'standard' unwind algorithm used in context #unwindTo: or #resume:,
- a suspended process termination attempts completing unwind blocks halfway through their execution first using #runUntilErrorOrReturnFrom:
- and after that the termination continues unwinding the remaining unwind blocks using the simulation algorithm of #popTo:

This approach looks inconsistent and indeed leads to inconsistencies, undesirable behavior and an instability mentioned above.

The Idea: In my view the easiest and most consistent solution is to simply extend the existing mechanism for completing halfway-through unwind blocks and let it deal with all unwind blocks. To make this approach applicable to terminating the active process, we suspend it first and then terminate it as a suspended process.

A commented code is enclosed.

I know it's pretty difficult to get a feedback on an ancient code; I'll be all the more grateful for your inputs.

Best regards,

Jaromir


PS:
Note the change in newProcess - `Processor activeProcess terminate` is replaced by `Processor activeProcess suspend` because there's no need for `terminate` at the bottom context ("terminate = unwind + suspend") and because it would lead to an infinite loop combined with my proposed changes to #terminate.

PPS:
More on the bugs:
ad 1) The #isTerminated condition `suspendedContext pc > suspendedContext startpc` is always met after executing the first instruction of the bottom context – generally not a sign of a terminated process.

ad 2) Explained in [1]. Concerns e.g. examples like
              [ [ Processor activeProcess terminate ] ensure: [ Processor activeProcess terminate ] ] fork.

ad 3-4) Explained in detail in [2].

One example to illustrate the bug:

| p |
p := [
              [
                           [ ] ensure: [
                                         [ ] ensure: [
                                                       Processor activeProcess suspend.
                                                       Transcript show: 'x1'].
                                         Transcript show: 'x2']
              ] ensure: [
                           Transcript show: 'x3']
] newProcess.
p resume.
Processor yield.
p terminate

===> x1

The unwind procedure prints just x1 and skips not only x2 but x3 as well ! You'd like to see them all.

ad 5) This happens in cases like (better save your image before trying this):
              [self error: 'e1'] ensure: [^2]        "discovered by Christoph Thiede"
and
              [self error: 'e1'] ensure: [self error: 'e2']
These are generally the types of situations causing the Unwind errors. The root cause is that the unwinding is done via simulation (#popTo) rather than 'directly'; the problem is during the simulated execution of unwind blocks a non-local return forwards the execution into a wrong stack - resulting in the Unwind errors.

ad 6) I just prefer a unified approach... unless I somehow overlooked a reason for two different approaches (I hope not).


[1] http://forum.world.st/A-bug-in-active-process-termination-crashing-image-td5128186.html
[2] http://forum.world.st/Another-bug-in-Process-gt-gt-terminate-in-unwinding-contexts-td5128171.html#a5128178

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.cuis.st/mailman/archives/cuis-dev/attachments/20210410/1e5e2e7e/attachment.htm>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: 4527-CuisCore-JaromirMatas-2021Apr08-21h20m-jar.001.cs.st
Type: application/octet-stream
Size: 4236 bytes
Desc: 4527-CuisCore-JaromirMatas-2021Apr08-21h20m-jar.001.cs.st
URL: <http://lists.cuis.st/mailman/archives/cuis-dev/attachments/20210410/1e5e2e7e/attachment.obj>


More information about the Cuis-dev mailing list