[POLL] Process>>terminate

Igor Stasenko siguctua at gmail.com
Mon Oct 15 18:55:19 UTC 2007


Lukas Renggli:

- #terminate can badly harm system if two processes concurrently try
to terminate a third process.

Yes, and that means that #terminate must be enclosed by semaphore to
ensure that termination code evaluated only once.
And moreover, things must be harvested to see, what is happening when
process which causes other process to terminate terminating too ..
like:

proc := [ ... ] fork.
[ proc terminate ] fork terminate.


On 15/10/2007, Paolo Bonzini <bonzini at gnu.org> wrote:
> > Well, I must *personally* say that I always associated #ensure: and
> > friends with the exception framework - thus, yes, I also want my ensure:
> > blocks to run - when there are *exceptions* thrown - but also when the
> > Process is *terminated*?
>
> That's the source of the misunderstanding.  #ensure: is completely
> detached from the exception framework.  In fact, you can implement
> exceptions only with thisContext, but it's really hard to implement
> #ensure: correctly without support in the virtual machine.  #ensure: and
> #ifCurtailed: blocks, for example, are run also when a block performs a
> non-local return, as in
>
>      ^[ ^#outer ] ensure: [ Transcript show: 'executed' ]
>      ^[ ^#outer ] ifCurtailed: [ Transcript show: 'executed' ]
>
> Note that #ifCurtailed: and #ensure: are equally powerful.  Here is
> #ifCurtailed: implemented with #ensure: and vice versa:
>
> BlockClosure>>ifCurtailed: curtailedBlock
>      | result |
>      curtailed := true.
>      ^[ result := aBlock value. curtailed := false. result ]
>          ensure: [ curtailed ifTrue: [ curtailedBlock value ] ]
>
> BlockClosure>>ensure: ensureBlock
>      | result |
>      [ result := aBlock value ] ifCurtailed: [
>          ensureBlock value.
>          ^result ].
>      "Don't reenter the ensureBlock if it is curtailed!"
>      ensureBlock value.
>      ^result
>
> > - #ensure: blocks are evaluated in the wrong process, that is the
> > process that calls #terminate not the one that runs the process. This
> > can have very strange effects, if your code depends on the current
> > process.
>
> This can be solved easily.  You define a ProcessBeingTerminated
> exception and wrap the process block aBlock into something like this:
>
> BlockClosure>>newProcess
>      ^Process
>          forContext: self processBlock
>          priority: Processor userSchedulingPriority
>
> BlockClosure>>processBlock
>      ^[[aBlock
>           on: ProcessBeingTerminated
>           do: [:sig |
>              "If we terminate in the handler, the 'ensure' blocks
>               are not evaluated.  Instead, if the handler returns, the
>               unwinding is done properly."
>              sig return]]
>          ensure: [self primTerminate]]
>
>
> where #primTerminate is the "hard terminate" that Igor was mentioning.
>
> Process>>primTerminate
>      self isActiveProcess ifFalse: [ self error: 'booh' ].
>      thisContext terminateTo: nil.
>      self suspend
>
>
> and termination becomes a matter of signaling the exception:
>
> ProcessorScheduler>>isProcessReady: aProcess
>      ^aProcess suspendingList ==
>          (quiescentProcessLists at: aProcess priority)
>
> Process>>isReady
>      ^Processor isProcessReady: self
>
> Process>>terminate
>      | pbt |
>      "A while ago I explained how to use this to properly implement
>       Semaphore>>#critical:."
>      pbt := ProcessBeingTerminated new.
>      self isReady ifFalse: [ pbt semaphore: myList ].
>      self signalException: pbt.
>
>
> You can do whatever you want with this code, it is not taken from GNU
> Smalltalk.
>
> Paolo
>

For cases, when my process is terminated and i need to execute some
code before(or at the moment) when this happens, better, i think is to
provide a #onTerminated: method for Process, where you can put a
hadler which will be executed whenever process is going to be
terminated. In normal conditions, if process finished w/o explicit
call to #terminate, this code should not be invoked.

So, you can just write something like that:

[ ... ] fork onTerminate: [ :p | Transcript show: 'Ouch, i terminated
by..' , p name ]

or even #addOnTerminate: block , which will add a block to a list of
blocks which will be evaluated when process is terminating.

Also, i not agree that terminate must be connected with exceptions.
Process provides a ways how to control its execution flow, and its not
an exceptional behavior, its a part of its interface. And i think that
exceptions framework must not be involved here.
Of course, its just my humble opinion :)

-- 
Best regards,
Igor Stasenko AKA sig.



More information about the Squeak-dev mailing list