<br><br><div class="gmail_quote">On Wed, Mar 3, 2010 at 5:35 PM, Andreas Raab <span dir="ltr">&lt;<a href="mailto:andreas.raab@gmx.de">andreas.raab@gmx.de</a>&gt;</span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex;">
<div class="im">On 3/3/2010 5:23 PM, Eliot Miranda wrote:<br>
</div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div class="im">
<br>
<br>
On Wed, Mar 3, 2010 at 5:07 PM, Andreas Raab &lt;<a href="mailto:andreas.raab@gmx.de" target="_blank">andreas.raab@gmx.de</a><br></div><div class="im">
&lt;mailto:<a href="mailto:andreas.raab@gmx.de" target="_blank">andreas.raab@gmx.de</a>&gt;&gt; wrote:<br>
<br>
    What do you guys think about this? It behaves no worse than the<br>
    current implementation if it encountered a problematic ensure: block<br>
    in #terminate but it does try to complete one when it finds it. I<br>
    think that short of changing the overall terminate semantics this is<br>
    pretty close to as good as it gets. The test illustrates the problem.<br>
<br>
    Eliot - could you check that my usage of findContextSuchThat /<br>
    closures / runUntilErrorOrReturn: looks reasonable? I&#39;m still<br>
    relatively new to closure land in these areas.<br>
<br>
<br>
That&#39;s fine.  But running unwinds in a different process is, I think,<br>
unwise.  Process identity is important and unwinds should be run in the<br>
context of the process itself.<br>
</div></blockquote>
<br>
I said &quot;short of changing the overall termination semantics&quot; :-)<br></blockquote><div><br></div><div>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.</div>
<div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex;">
<br>
Cheers,<br><font color="#888888">
  - Andreas</font><div><div></div><div class="h5"><br>
<br>
 Here&#39;s the VisualWorks code:<br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<br>
terminate<br>
&quot;Terminate the receiver process, by sending the Process terminateSignal.<br>
Allow all unwind blocks to run, even if they are currently in progress.&quot;<br>
<br>
&quot;If the receiver is the active process then raise the terminateSignal<br>
  which should be<br>
caught by the handler in Process class&gt;&gt;forBlock:priority:.  The handler<br>
will return,<br>
causing all unwind blocks to run, and then will invoke finalTerminate,<br>
actually terminating the receiver.&quot;<br>
Processor activeProcess == self ifTrue:<br>
[ [ Process terminateSignal raise ] on: Signal noHandlerSignal do:<br>
[ :ex |<br>
&quot;Usually, this can only happen if a Process was terminated unnaturally.<br>
Try to recover gracefully.&quot;<br>
Process terminateSignal == ex unhandledException class<br>
ifTrue: [ ex return ]<br>
ifFalse: [ ex pass ].<br>
].<br>
^self finalTerminate<br>
].<br>
<br>
&quot;If the receiver is not the active process and its suspendedContext is<br>
nil then<br>
it is already suspended.&quot;<br>
suspendedContext == nil ifTrue: [^self].<br>
<br>
&quot;Suspend the receiver, then place a block atop the receiver&#39;s stack to<br>
invoke this method as the active process, and resume the receiver.&quot;<br>
interruptProtect critical:<br>
[| savedContext interruptContext outerMostUnwind curr |<br>
myList == nil ifFalse: [self suspend].<br>
curr := suspendedContext.<br>
[curr == nil] whileFalse:<br>
[curr method isMarkedForUnwindInAction<br>
ifTrue: [outerMostUnwind := curr.<br>
curr := curr skipOverUnwindingBlocks].<br>
curr := curr findNextMarkedUpTo: nil].<br>
(outerMostUnwind ~~ nil and: [outerMostUnwind method numTempsOnly &gt; 0])<br>
ifTrue:<br>
[outerMostUnwind localAt: outerMostUnwind method numArgs+1 put: true]<br>
ifFalse:<br>
[savedContext := suspendedContext.<br>
interruptContext := [self terminate] newContext.<br>
interruptContext sender: savedContext.<br>
suspendedContext := interruptContext].<br>
self resume]<br>
<br>
So...<br>
- it only runs unwinds in the context of the process, pushing a block<br>
that will run terminate if other than the current process tries to<br>
terminate it<br>
- it tried to discover whether the attempt to terminate from another<br>
process was made while unwind blocks are being run and marks any such<br>
block as having completed (i.e. it&#39;ll short-circuit an already-executing<br>
unwind block).<br>
<br>
<br>
<br>
P.S. Here&#39;s the identification of an unwind in action:<br>
<br>
BlockClosure&gt;&gt;valueAsUnwindBlockFrom: aContextOrNil<br>
&quot;Unwind blocks are evaluated using this wrapper.<br>
This method is marked as special.  When the<br>
system searches for unwind blocks, it skips over<br>
all contexts between this context and the context<br>
passed in as an argument.  If the argument is<br>
nil, it skips over the sender of this context.<br>
<br>
The purpose of this is that errors inside an<br>
unwind block should not evaluate that unwind<br>
block, and they should not circumvent the<br>
running of other unwind blocks lower on the<br>
stack.&quot;<br>
<br>
&lt;exception: #unwindInAction&gt;<br>
<br>
| shouldTerminate |<br>
&quot;The first temporary variable in this method is treated specially by<br>
Process&gt;&gt;terminate,<br>
which may set it to true. If that happens, terminate the process at the<br>
end of the unwind action.<br>
Normally we should be able to take for granted that shouldTerminate has<br>
nil as its initial<br>
value, but since this variable is modified by another process, it&#39;s<br>
possible that the other<br>
process could set the value to true when this process is waiting at the<br>
initial PC of the method.<br>
In that case, when this process resumes, it needs to make sure that it<br>
doesn&#39;t overwrite<br>
the true with a false.<br>
<br>
On a purely theoretical level, there&#39;s still room for a race condition<br>
between testing for nil<br>
and assigning false to the variable, but in practise our execution<br>
technology doesn&#39;t allow<br>
for a process switch between the test and the assignment, so the only<br>
way it could be a<br>
problem in practise is if the method were being debugged or run by a VI<br>
level interpreter<br>
at the time that some other process wanted to terminate it. At this<br>
time, the risk of that kind<br>
of situation seems so much smaller thatn the risk of a process switch at<br>
the initial PC, that<br>
we&#39;re willing to fix only the initial PC case.&quot;<br>
shouldTerminate == nil ifTrue: [shouldTerminate := false].<br>
self value.<br>
shouldTerminate<br>
ifTrue:<br>
[Processor activeProcess terminate].<br>
<br>
CompiledCode&gt;&gt;isMarkedForUnwindInAction<br>
&quot;Answer true if marked for unwind in action.&quot;<br>
<br>
^false<br>
<br>
MarkedMethod&gt;&gt;isMarkedForUnwindInAction<br>
&quot;Answer true if method is marked for unwinding in action.&quot;<br>
<br>
^markType == #unwindInAction<br>
and I can spell practice ;)<br>
<br>
<br>
HTH<br>
Eliot<br>
<br>
<br>
    Cheers,<br>
      - Andreas<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
</blockquote>
<br>
<br>
</div></div></blockquote></div><br>