[POLL] Process>>terminate

Paolo Bonzini bonzini at gnu.org
Mon Oct 15 12:03:44 UTC 2007


> 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




More information about the Squeak-dev mailing list