#fork and deterministic resumption of the resulting process

Michael van der Gulik mikevdg at gmail.com
Mon Feb 4 22:00:32 UTC 2008


On Feb 5, 2008 10:04 AM, Andreas Raab <andreas.raab at gmx.de> wrote:

> Hi -
>
> 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 fix this problem I have changed BlockContext>>fork and
> BlockContext>>forkAt: to read, e.g.,
>
> BlockContext>>fork
>   "Create and schedule a Process running the code in the receiver."
>   ^self forkAt: Processor activePriority
>
> BlockContext>>forkAt: priority
>   "Create and schedule a Process running the code in the receiver
>   at the given priority. Answer the newly created process."
>   | forkedProcess helperProcess |
>   forkedProcess := self newProcess.
>   forkedProcess priority: priority.
>   priority = Processor activePriority ifTrue:[
>     helperProcess := [forkedProcess resume] newProcess.
>     helperProcess priority: priority-1.
>     helperProcess resume.
>   ] ifFalse:[
>     forkedProcess resume
>   ].
>   ^forkedProcess
>
> This will make sure that #fork has (for the purpose of resumption) the
> same semantics as forking at a lower priority has.
>
> What do people think about this?
>


I'm thinking that the above is an ugly hack. When we eventually write an
interpreter which is truly multitasking, your original bug will re-appear.

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.

Gulik.







-- 
http://people.squeakfoundation.org/person/mikevdg
http://gulik.pbwiki.com/
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.squeakfoundation.org/pipermail/squeak-dev/attachments/20080205/a84d518e/attachment.htm


More information about the Squeak-dev mailing list