[squeak-dev] Re: [Pharo-project] #ensure: issues
eliot.miranda at gmail.com
Thu Mar 4 01:52:52 UTC 2010
On Wed, Mar 3, 2010 at 5:35 PM, Andreas Raab <andreas.raab at gmx.de> wrote:
> On 3/3/2010 5:23 PM, Eliot Miranda wrote:
>> On Wed, Mar 3, 2010 at 5:07 PM, Andreas Raab <andreas.raab at gmx.de
>> <mailto:andreas.raab at gmx.de>> wrote:
>> What do you guys think about this? It behaves no worse than the
>> current implementation if it encountered a problematic ensure: block
>> in #terminate but it does try to complete one when it finds it. I
>> think that short of changing the overall terminate semantics this is
>> pretty close to as good as it gets. The test illustrates the problem.
>> Eliot - could you check that my usage of findContextSuchThat /
>> closures / runUntilErrorOrReturn: looks reasonable? I'm still
>> relatively new to closure land in these areas.
>> That's fine. But running unwinds in a different process is, I think,
>> unwise. Process identity is important and unwinds should be run in the
>> context of the process itself.
> I said "short of changing the overall termination semantics" :-)
But running unwinds in anything other than the owning process is /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 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])
>> [outerMostUnwind localAt: outerMostUnwind method numArgs+1 put: true]
>> [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 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
>> <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 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.
>> [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
-------------- next part --------------
An HTML attachment was scrubbed...
More information about the Squeak-dev