[squeak-dev] Re: [Pharo-project] #ensure: issues
andreas.raab at gmx.de
Thu Mar 4 01:35:38 UTC 2010
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" :-)
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
More information about the Squeak-dev