[squeak-dev] Re: [Pharo-project] #ensure: issues

Andreas Raab andreas.raab at gmx.de
Thu Mar 4 02:13:09 UTC 2010


On 3/3/2010 5:52 PM, Eliot Miranda wrote:
> But running unwinds in anything other than the owning process is
> /broken/

No, Eliot, it's *different*. If it were outright broken then lots of 
unwind actions shouldn't work. That is not the case. In fact, I am more 
and more starting to think that it may be the *right* thing to do 
because you get the process context associated with the terminating 
process allowing to do something like:

   "kill the process silently"
   [p terminate]
     on: Timeout do:[:ex| ex return "skip it"]
     on: Error do:["ignore"].

There are obviously issues if the unwind actions refer to process local 
state; but then again perhaps providing said context for the duration of 
the unwind is the right thing, along the lines of:

Process>>terminate

   self isActiveProcess ifFalse:[
     Processor activeProcess evaluate: [self unwind] onBehalfOf: self.
   ].

This for example would guard errors during unwind as initially intended 
while ensuring that process relative state is retrieved correctly.

In any case, I think the issue is *significantly* more faceted than just 
saying "it's broken".

Cheers,
   - Andreas


> and arranging that a process runs its own unwinds on
> termination is straight-forward.  The VW code does a few other things,
> but the way it transfers control from one process to another is easy to
> do in Squeak.
>
>
>     Cheers,
>       - Andreas
>
>
>       Here's the VisualWorks code:
>
>
>         terminate
>         "Terminate the receiver process, by sending the Process
>         terminateSignal.
>         Allow all unwind blocks to run, even if they are currently in
>         progress."
>
>         "If the receiver is the active process then raise the
>         terminateSignal
>           which should be
>         caught by the handler in Process class>>forBlock:priority:.  The
>         handler
>         will return,
>         causing all unwind blocks to run, and then will invoke
>         finalTerminate,
>         actually terminating the receiver."
>         Processor activeProcess == self ifTrue:
>         [ [ Process terminateSignal raise ] on: Signal noHandlerSignal do:
>         [ :ex |
>         "Usually, this can only happen if a Process was terminated
>         unnaturally.
>         Try to recover gracefully."
>         Process terminateSignal == ex unhandledException class
>         ifTrue: [ ex return ]
>         ifFalse: [ ex pass ].
>         ].
>         ^self finalTerminate
>         ].
>
>         "If the receiver is not the active process and its
>         suspendedContext is
>         nil then
>         it is already suspended."
>         suspendedContext == nil ifTrue: [^self].
>
>         "Suspend the receiver, then place a block atop the receiver's
>         stack to
>         invoke this method as the active process, and resume the receiver."
>         interruptProtect critical:
>         [| savedContext interruptContext outerMostUnwind curr |
>         myList == nil ifFalse: [self suspend].
>         curr := suspendedContext.
>         [curr == nil] whileFalse:
>         [curr method isMarkedForUnwindInAction
>         ifTrue: [outerMostUnwind := curr.
>         curr := curr skipOverUnwindingBlocks].
>         curr := curr findNextMarkedUpTo: nil].
>         (outerMostUnwind ~~ nil and: [outerMostUnwind method
>         numTempsOnly > 0])
>         ifTrue:
>         [outerMostUnwind localAt: outerMostUnwind method numArgs+1 put:
>         true]
>         ifFalse:
>         [savedContext := suspendedContext.
>         interruptContext := [self terminate] newContext.
>         interruptContext sender: savedContext.
>         suspendedContext := interruptContext].
>         self resume]
>
>         So...
>         - it only runs unwinds in the context of the process, pushing a
>         block
>         that will run terminate if other than the current process tries to
>         terminate it
>         - it tried to discover whether the attempt to terminate from another
>         process was made while unwind blocks are being run and marks any
>         such
>         block as having completed (i.e. it'll short-circuit an
>         already-executing
>         unwind block).
>
>
>
>         P.S. Here's the identification of an unwind in action:
>
>         BlockClosure>>valueAsUnwindBlockFrom: aContextOrNil
>         "Unwind blocks are evaluated using this wrapper.
>         This method is marked as special.  When the
>         system searches for unwind blocks, it skips over
>         all contexts between this context and the context
>         passed in as an argument.  If the argument is
>         nil, it skips over the sender of this context.
>
>         The purpose of this is that errors inside an
>         unwind block should not evaluate that unwind
>         block, and they should not circumvent the
>         running of other unwind blocks lower on the
>         stack."
>
>         <exception: #unwindInAction>
>
>         | shouldTerminate |
>         "The first temporary variable in this method is treated specially by
>         Process>>terminate,
>         which may set it to true. If that happens, terminate the process
>         at the
>         end of the unwind action.
>         Normally we should be able to take for granted that
>         shouldTerminate has
>         nil as its initial
>         value, but since this variable is modified by another process, it's
>         possible that the other
>         process could set the value to true when this process is waiting
>         at the
>         initial PC of the method.
>         In that case, when this process resumes, it needs to make sure
>         that it
>         doesn't overwrite
>         the true with a false.
>
>         On a purely theoretical level, there's still room for a race
>         condition
>         between testing for nil
>         and assigning false to the variable, but in practise our execution
>         technology doesn't allow
>         for a process switch between the test and the assignment, so the
>         only
>         way it could be a
>         problem in practise is if the method were being debugged or run
>         by a VI
>         level interpreter
>         at the time that some other process wanted to terminate it. At this
>         time, the risk of that kind
>         of situation seems so much smaller thatn the risk of a process
>         switch at
>         the initial PC, that
>         we're willing to fix only the initial PC case."
>         shouldTerminate == nil ifTrue: [shouldTerminate := false].
>         self value.
>         shouldTerminate
>         ifTrue:
>         [Processor activeProcess terminate].
>
>         CompiledCode>>isMarkedForUnwindInAction
>         "Answer true if marked for unwind in action."
>
>         ^false
>
>         MarkedMethod>>isMarkedForUnwindInAction
>         "Answer true if method is marked for unwinding in action."
>
>         ^markType == #unwindInAction
>         and I can spell practice ;)
>
>
>         HTH
>         Eliot
>
>
>             Cheers,
>               - Andreas
>
>
>
>
>
>
>
>
>
>
>
>
>
>




More information about the Squeak-dev mailing list