Squeak SmalltalkJoker Squeak Smalltalk : System : prevnext Fork Process Deterministic Scheduler

In my never-ending quest for questionable behavior in multi-threaded
situations just today I ran into a pattern which is dangerously common
in our code. It basically goes like this:

MyClass>>startWorkerProcess
    "worker is an instance variable"
    worker:= [self runWorkerProcess] fork.

MyClass>>runWorkerProcess
    "Run the worker process"
    [Processor activeProcess == worker] whileTrue:[
        "...do the work..."
    ].

MyClass>>stopWorkerProcess
    "Stop the worker process"
    worker := nil. "let it terminate itself"

Those of you who can immediately tell what the problem is should get a medal 
for an outstanding knack of analyzing concurrency problems.
For the rest of us, the problem is that #fork in the above is not 
deterministic in the way that there is no guarantee whether the "worker" 
variable will be assigned when we enter the worker loop. It *would* be 
deterministic if the priority were below or above the current process' 
priority but when it's the same it can be affected by environmental effects 
(external signals, delay processing etc) leading to some very obscure runtime 
problems (in the above, the process would just not start).

To illustrate:

p1 := p2 := p3 := nil.
p1 := [p1Valid := p1 notNil] forkAt: Processor activePriority-1.
p2 := [p2Valid := p2 notNil] forkAt: Processor activePriority.
p3 := [p3Valid := p3 notNil] forkAt: Processor activePriority+1.

Both, the first and the last case are currently deterministic and will stay 
that way: p1 will be consistently non-nil when this code is run; p3 will be 
consistently nil when run. This is currently the case and remains the same 
after applying my changes.

In the second case however, p2 may or may not be nil, depending on whether 
external interrupts occur. Since that is rare, in 99.99+% of all cases p2 will 
be non-nil.

What I am proposing is simply to make p2 non-nil in 100% of the cases. There 
is no change to those parts of the existing semantics that are actually 
well-defined. The only change is that it takes a rare non-deterministic 
occurrence and makes the overall behavior consistent in this case.

--

Being aware of the problem Andreas illustrated, I frequently
write;

workerProcess := [self runWorkerProcess] newProcess.
workerProcess resume.

--

What you wanted to do, rather than redefining the Process class, is:

MyClass>>startWorkerProcess
       "keepRunning is an instance variable"
       keepRunning := true.
       " Always run worker processes as a lower priority than the controller process. "
       [self runWorkerProcess] forkAt: ProcessScheduler somethingeratherPriority.
       " sorry; I don't have Squeak handy right now, but you get the idea. "

MyClass>>runWorkerProcess
       "Run the worker process"
       [keepRunning] whileTrue: [
               "...do the work..."
       ].

MyClass>>stopWorkerProcess
       "Stop the worker process"
       keepRunning := false. "let it terminate itself"

Or better, make an abstraction:

process := WorkerTask doing: [ someObject doSomeWork ].
process start.
process stop.