<br><br><div class="gmail_quote">On Wed, Mar 3, 2010 at 5:07 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;">
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.<br>

<br>
Eliot - could you check that my usage of findContextSuchThat / closures / runUntilErrorOrReturn: looks reasonable? I&#39;m still relatively new to closure land in these areas.<br></blockquote><div><br></div><div>That&#39;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.  Here&#39;s the VisualWorks code:</div>
<div><br></div><div><div>terminate</div><div><span class="Apple-tab-span" style="white-space:pre">        </span>&quot;Terminate the receiver process, by sending the Process terminateSignal. Allow all unwind blocks to run, even if they are currently in progress.&quot;</div>
<div><br></div><div><span class="Apple-tab-span" style="white-space:pre">        </span>&quot;If the receiver is the active process then raise the terminateSignal  which should be </div><div><span class="Apple-tab-span" style="white-space:pre">        </span>caught by the handler in Process class&gt;&gt;forBlock:priority:.  The handler will return, </div>
<div><span class="Apple-tab-span" style="white-space:pre">        </span>causing all unwind blocks to run, and then will invoke finalTerminate, actually terminating the receiver.&quot;</div><div><span class="Apple-tab-span" style="white-space:pre">        </span>Processor activeProcess == self ifTrue: </div>
<div><span class="Apple-tab-span" style="white-space:pre">                </span>[ [ Process terminateSignal raise ] on: Signal noHandlerSignal do: </div><div><span class="Apple-tab-span" style="white-space:pre">                        </span>[ :ex |</div>
<div><span class="Apple-tab-span" style="white-space:pre">                        </span>&quot;Usually, this can only happen if a Process was terminated unnaturally.  </div><div><span class="Apple-tab-span" style="white-space:pre">                        </span>Try to recover gracefully.&quot;</div>
<div><span class="Apple-tab-span" style="white-space:pre">                        </span>Process terminateSignal == ex unhandledException class</div><div><span class="Apple-tab-span" style="white-space:pre">                                </span>ifTrue: [ ex return ]</div>
<div><span class="Apple-tab-span" style="white-space:pre">                                </span>ifFalse: [ ex pass ].</div><div><span class="Apple-tab-span" style="white-space:pre">                        </span>].</div><div><span class="Apple-tab-span" style="white-space:pre">                </span>^self finalTerminate</div>
<div><span class="Apple-tab-span" style="white-space:pre">                </span>].</div><div><br></div><div><span class="Apple-tab-span" style="white-space:pre">        </span>&quot;If the receiver is not the active process and its suspendedContext is nil then</div>
<div><span class="Apple-tab-span" style="white-space:pre">        </span> it is already suspended.&quot;</div><div><span class="Apple-tab-span" style="white-space:pre">        </span>suspendedContext == nil ifTrue: [^self].</div><div><br>
</div><div><span class="Apple-tab-span" style="white-space:pre">        </span>&quot;Suspend the receiver, then place a block atop the receiver&#39;s stack to</div><div><span class="Apple-tab-span" style="white-space:pre">        </span> invoke this method as the active process, and resume the receiver.&quot;</div>
<div><span class="Apple-tab-span" style="white-space:pre">        </span>interruptProtect critical:</div><div><span class="Apple-tab-span" style="white-space:pre">                </span>[| savedContext interruptContext outerMostUnwind curr |</div>
<div><span class="Apple-tab-span" style="white-space:pre">                </span>myList == nil ifFalse: [self suspend].</div><div><span class="Apple-tab-span" style="white-space:pre">                </span>curr := suspendedContext.</div><div><span class="Apple-tab-span" style="white-space:pre">                </span>[curr == nil] whileFalse:</div>
<div><span class="Apple-tab-span" style="white-space:pre">                        </span>[curr method isMarkedForUnwindInAction</div><div><span class="Apple-tab-span" style="white-space:pre">                                </span>ifTrue: [outerMostUnwind := curr.</div><div>
<span class="Apple-tab-span" style="white-space:pre">                                        </span>curr := curr skipOverUnwindingBlocks].</div><div><span class="Apple-tab-span" style="white-space:pre">                        </span>curr := curr findNextMarkedUpTo: nil].</div>
<div><span class="Apple-tab-span" style="white-space:pre">                </span>(outerMostUnwind ~~ nil and: [outerMostUnwind method numTempsOnly &gt; 0])</div><div><span class="Apple-tab-span" style="white-space:pre">                        </span>ifTrue:</div>
<div><span class="Apple-tab-span" style="white-space:pre">                                </span>[outerMostUnwind localAt: outerMostUnwind method numArgs+1 put: true]</div><div><span class="Apple-tab-span" style="white-space:pre">                        </span>ifFalse:</div>
<div><span class="Apple-tab-span" style="white-space:pre">                                </span>[savedContext := suspendedContext.</div><div><span class="Apple-tab-span" style="white-space:pre">                                </span>interruptContext := [self terminate] newContext.</div>
<div><span class="Apple-tab-span" style="white-space:pre">                                </span>interruptContext sender: savedContext.</div><div><span class="Apple-tab-span" style="white-space:pre">                                </span>suspendedContext := interruptContext].</div>
<div><span class="Apple-tab-span" style="white-space:pre">                </span>self resume]</div><div><br></div><div>So...</div><div>- 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</div>
<div>- 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&#39;ll short-circuit an already-executing unwind block).</div>
<div><br></div><div><br></div><div><br></div><div>P.S. Here&#39;s the identification of an unwind in action:</div><div><br></div><div>BlockClosure&gt;&gt;valueAsUnwindBlockFrom: aContextOrNil</div><div><span class="Apple-tab-span" style="white-space:pre">        </span>&quot;Unwind blocks are evaluated using this wrapper.</div>
<div><span class="Apple-tab-span" style="white-space:pre">        </span>This method is marked as special.  When the</div><div><span class="Apple-tab-span" style="white-space:pre">        </span>system searches for unwind blocks, it skips over</div>
<div><span class="Apple-tab-span" style="white-space:pre">        </span>all contexts between this context and the context</div><div><span class="Apple-tab-span" style="white-space:pre">        </span>passed in as an argument.  If the argument is</div>
<div><span class="Apple-tab-span" style="white-space:pre">        </span>nil, it skips over the sender of this context.</div><div><br></div><div><span class="Apple-tab-span" style="white-space:pre">        </span>The purpose of this is that errors inside an</div>
<div><span class="Apple-tab-span" style="white-space:pre">        </span>unwind block should not evaluate that unwind</div><div><span class="Apple-tab-span" style="white-space:pre">        </span>block, and they should not circumvent the</div>
<div><span class="Apple-tab-span" style="white-space:pre">        </span>running of other unwind blocks lower on the</div><div><span class="Apple-tab-span" style="white-space:pre">        </span>stack.&quot;</div><div><br></div><div><span class="Apple-tab-span" style="white-space:pre">        </span>&lt;exception: #unwindInAction&gt;</div>
<div><br></div><div><span class="Apple-tab-span" style="white-space:pre">        </span>| shouldTerminate |</div><div><span class="Apple-tab-span" style="white-space:pre">                </span>&quot;The first temporary variable in this method is treated specially by Process&gt;&gt;terminate,</div>
<div><span class="Apple-tab-span" style="white-space:pre">                </span>which may set it to true. If that happens, terminate the process at the end of the unwind action.</div><div><span class="Apple-tab-span" style="white-space:pre">                </span>Normally we should be able to take for granted that shouldTerminate has nil as its initial</div>
<div><span class="Apple-tab-span" style="white-space:pre">                </span>value, but since this variable is modified by another process, it&#39;s possible that the other </div><div><span class="Apple-tab-span" style="white-space:pre">                </span>process could set the value to true when this process is waiting at the initial PC of the method.</div>
<div><span class="Apple-tab-span" style="white-space:pre">                </span>In that case, when this process resumes, it needs to make sure that it doesn&#39;t overwrite</div><div><span class="Apple-tab-span" style="white-space:pre">                </span>the true with a false.</div>
<div><br></div><div><span class="Apple-tab-span" style="white-space:pre">                </span>On a purely theoretical level, there&#39;s still room for a race condition between testing for nil</div><div><span class="Apple-tab-span" style="white-space:pre">                </span>and assigning false to the variable, but in practise our execution technology doesn&#39;t allow</div>
<div><span class="Apple-tab-span" style="white-space:pre">                </span>for a process switch between the test and the assignment, so the only way it could be a</div><div><span class="Apple-tab-span" style="white-space:pre">                </span>problem in practise is if the method were being debugged or run by a VI level interpreter</div>
<div><span class="Apple-tab-span" style="white-space:pre">                </span>at the time that some other process wanted to terminate it. At this time, the risk of that kind</div><div><span class="Apple-tab-span" style="white-space:pre">                </span>of situation seems so much smaller thatn the risk of a process switch at the initial PC, that</div>
<div><span class="Apple-tab-span" style="white-space:pre">                </span>we&#39;re willing to fix only the initial PC case.&quot;</div><div><span class="Apple-tab-span" style="white-space:pre">        </span>shouldTerminate == nil ifTrue: [shouldTerminate := false].</div>
<div><span class="Apple-tab-span" style="white-space:pre">        </span>self value.</div><div><span class="Apple-tab-span" style="white-space:pre">        </span>shouldTerminate</div><div><span class="Apple-tab-span" style="white-space:pre">                </span>ifTrue:</div>
<div><span class="Apple-tab-span" style="white-space:pre">                        </span>[Processor activeProcess terminate].</div><div><br></div><div>CompiledCode&gt;&gt;isMarkedForUnwindInAction</div><div><span class="Apple-tab-span" style="white-space:pre">        </span>&quot;Answer true if marked for unwind in action.&quot;</div>
<div><br></div><div><span class="Apple-tab-span" style="white-space:pre">        </span>^false</div><div><br></div><div>MarkedMethod&gt;&gt;isMarkedForUnwindInAction</div><div><span class="Apple-tab-span" style="white-space:pre">        </span>&quot;Answer true if method is marked for unwinding in action.&quot;</div>
<div><br></div><div><span class="Apple-tab-span" style="white-space:pre">        </span>^markType == #unwindInAction</div></div><div> </div><div>and I can spell practice ;)</div><div><br></div><div><br></div><div>HTH</div><div>Eliot</div>
<div><br></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<br>
<br>
</font><br><br>
<br></blockquote></div><br>