[squeak-dev] Re: [Pharo-project] #ensure: issues
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
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"
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:
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".
> 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.
> - Andreas
> Here's the VisualWorks code:
> "Terminate the receiver process, by sending the Process
> Allow all unwind blocks to run, even if they are currently in
> "If the receiver is the active process then raise the
> which should be
> caught by the handler in Process class>>forBlock:priority:. The
> will return,
> causing all unwind blocks to run, and then will invoke
> 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
> 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])
> [outerMostUnwind localAt: outerMostUnwind method numArgs+1 put:
> [savedContext := suspendedContext.
> interruptContext := [self terminate] newContext.
> interruptContext sender: savedContext.
> suspendedContext := interruptContext].
> self resume]
> - it only runs unwinds in the context of the process, pushing a
> 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
> block as having completed (i.e. it'll short-circuit an
> 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
> <exception: #unwindInAction>
> | shouldTerminate |
> "The first temporary variable in this method is treated specially by
> 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
> 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
> 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.
> [Processor activeProcess terminate].
> "Answer true if marked for unwind in action."
> "Answer true if method is marked for unwinding in action."
> ^markType == #unwindInAction
> and I can spell practice ;)
> - Andreas
More information about the Squeak-dev