Process - Cuis terminate examples Some examples to illustrate the termination bugs and test the proposed rewrite of #terminate ========================================== terminate suspended: | p | p := [ [ [ ] ensure: [ [Processor activeProcess suspend] ensure: [ Transcript show: 'x1']. Transcript show: 'x2'] ] ensure: [ Transcript show: 'x3']. Transcript show: 'x4' ] fork. Processor yield. p terminate. Transcript show: p isTerminated printString "prints x1 x2 x3" | p | p := [ [ [ ] ensure: [ [ ] ensure: [ Processor activeProcess suspend. Transcript show: 'x1']. Transcript show: 'x2'] ] ensure: [ Transcript show: 'x3']. Transcript show: 'x4' ] fork. Processor yield. p terminate. Transcript show: p isTerminated printString "prints x1 x2 x3" ....................................... terminate runnable: | p | p := [ [ [ ] ensure: [ [Processor yield] ensure: [ Transcript show: 'x1']. Transcript show: 'x2'] ] ensure: [ Transcript show: 'x3']. Transcript show: 'x4' ] fork. Processor yield. p terminate. Transcript show: p isTerminated printString "prints x1 x2 x3" | p | p := [ [ [ ] ensure: [ [ ] ensure: [ Processor yield. Transcript show: 'x1']. Transcript show: 'x2'] ] ensure: [ Transcript show: 'x3']. Transcript show: 'x4' ] fork. Processor yield. p terminate. Transcript show: p isTerminated printString "prints x1 x2 x3" ....................................... terminate blocked: | p s | s := Semaphore new. p := [ [ [ ] ensure: [ [s wait] ensure: [ Transcript show: 'x1']. Transcript show: 'x2'] ] ensure: [ Transcript show: 'x3']. Transcript show: 'x4' ] fork. Processor yield. p terminate. Transcript show: p isTerminated printString "prints x1 x2 x3" | p s | s := Semaphore new. p := [ [ [ ] ensure: [ [ ] ensure: [ s wait. Transcript show: 'x1']. Transcript show: 'x2'] ] ensure: [ Transcript show: 'x3']. Transcript show: 'x4' ] fork. Processor yield. p terminate. Transcript show: p isTerminated printString "prints x1 x2 x3" ....................................... terminate active: | p | p := [ [ [ ] ensure: [ [Processor activeProcess terminate] ensure: [ Transcript show: 'x1']. Transcript show: 'x2'] ] ensure: [ Transcript show: 'x3']. Transcript show: 'x4' ] fork. "Two yields necessary: terminate active is a two-step procedure" Processor yield. Processor yield. Transcript show: p isTerminated printString "prints x1 x2 x3" | p | p := [ [ [ ] ensure: [ [ ] ensure: [ Processor activeProcess terminate. Transcript show: 'x1']. Transcript show: 'x2'] ] ensure: [ Transcript show: 'x3']. Transcript show: 'x4' ] fork. "Two yields necessary: terminate active is a two-step procedure" Processor yield. Processor yield. Transcript show: p isTerminated printString "prints x1 x2 x3" ========================================== unhandled error: Termination happens when the user hits Abandon on the Debugger window. "cf.: prints x1 x2 x3 x4 when hit Proceed" [ [ ] ensure: [ [self error: 'unwind test'] ensure: [ Transcript show: 'x1']. Transcript show: 'x2'] ] ensure: [ Transcript show: 'x3']. Transcript show: 'x4' "prints x1 x2 x3" [ [ ] ensure: [ [ ] ensure: [ self error: 'unwind test'. Transcript show: 'x1']. Transcript show: 'x2'] ] ensure: [ Transcript show: 'x3']. Transcript show: 'x4' "prints x1 x2 x3" ========================================= error and non-local return combined: Termination happens when the user hits Abandon on the Debugger window. ........... non-local return inside inner-most halfway thru unwind block: [ [ ] ensure: [ [self error: 'unwind test'] ensure: [ ^Transcript show: 'x1']. Transcript show: 'x2'] ] ensure: [ Transcript show: 'x3']. Transcript show: 'x4' "prints x1" [ [ ] ensure: [ [ ] ensure: [ self error: 'unwind test'. ^Transcript show: 'x1']. Transcript show: 'x2'] ] ensure: [ Transcript show: 'x3']. Transcript show: 'x4' "prints x1" ........................ non-local return inside outer-most halfway thru unwind block: [ [ ] ensure: [ [self error: 'unwind test'] ensure: [ Transcript show: 'x1']. ^Transcript show: 'x2'] ] ensure: [ Transcript show: 'x3']. Transcript show: 'x4' "prints x1 x2" [ [ ] ensure: [ [ ] ensure: [ self error: 'unwind test'. Transcript show: 'x1']. ^Transcript show: 'x2'] ] ensure: [ Transcript show: 'x3']. Transcript show: 'x4' "prints x1 x2" ............................. non-local return inside outside halfway thru unwind blocks: [ [ ] ensure: [ [self error: 'unwind test'] ensure: [ Transcript show: 'x1']. Transcript show: 'x2'] ] ensure: [ ^Transcript show: 'x3']. Transcript show: 'x4' "prints x1 x2 x3" [ [ ] ensure: [ [ ] ensure: [ self error: 'unwind test'. Transcript show: 'x1']. Transcript show: 'x2'] ] ensure: [ ^Transcript show: 'x3']. Transcript show: 'x4' "prints x1 x2 x3" [ [ [ ] ensure: [ [ ] ensure: [ self error: 'unwind test'. Transcript show: 'x1']. Transcript show: 'x2'] ] ensure: [ ^Transcript show: 'x3'] ] ensure: [ Transcript show: 'x4'] "prints x1 x2 x3" ====================== For comparison only: The tests presented here are not affected by the new #terminate. (A) an unhandled error unwind (in the preceding examples) follows the new termination path via a suspended process termination" (B) a handled error unwind follows the traditional 'direct' unwind path using slightly different semantics - it doesn't complete unwind blocks halfway through their execution (!) - should it? ......................................... handled error: [ [ [ ] ensure: [ [self error] ensure: [ Transcript show: 'x1']. Transcript show: 'x2'] ] ensure: [ Transcript show: 'x3'] ] on: Error do: []. Transcript show: 'x4' "prints x1 x3 x4" [ [ [ ] ensure: [ [] ensure: [ self error. Transcript show: 'x1']. Transcript show: 'x2'] ] ensure: [ Transcript show: 'x3'] ] on: Error do: []. Transcript show: 'x4' "prints x3 x4" ............................................ non-local return: Similarly a simple non-local return execution follows a 'direct' unwind path in #resume[:through:] using slightly different semantics. [ [ ] ensure: [ [^1] ensure: [ Transcript show: 'x1']. Transcript show: 'x2'] ] ensure: [ Transcript show: 'x3']. Transcript show: 'x4' "prints x1 x3" [ [ ] ensure: [ [] ensure: [ ^Transcript show: 'x1']. Transcript show: 'x2'] ] ensure: [ Transcript show: 'x3']. Transcript show: 'x4' "prints x1 x3" ========================================== crazies: These tests explore fixed #teminate behavior under more extreme circumstances. Double termination: | p | p := [ [ [ ] ensure: [ [Processor activeProcess suspend] ensure: [ Processor activeProcess suspend. Transcript show: 'x1']. Transcript show: 'x2'] ] ensure: [ Transcript show: 'x3'] ] fork. Processor yield. p terminate "suspends UI, Squeak after Alt+. prints x1 x2 x3; Cuis crashes badly" | p | p := [ [ [ ] ensure: [ [Processor activeProcess terminate] ensure: [ Processor activeProcess terminate. Transcript show: 'x1']. Transcript show: 'x2'] ] ensure: [ Transcript show: 'x3']. Transcript show: 'x4' ] fork. Processor yield. Processor yield. Processor yield. Processor yield. Transcript show: p isTerminated printString "lets UI live and prints x1 x2 x3" | p | p := [ [ [ ] ensure: [ [self error: 'unwind test'] ensure: [ self error: 'unwind test'. Transcript show: 'x1']. Transcript show: 'x2'] ] ensure: [ Transcript show: 'x3']. Transcript show: 'x4' ] fork. Processor yield. "prints x3. Follows #runUntilErrorOrReturnFrom: semantics" | p | p := [ [ [ [ ] ensure: [ [self error] ensure: [ self error. Transcript show: 'x1']. Transcript show: 'x2'] ] ensure: [ Transcript show: 'x3'] ] on: Error do: []. Transcript show: 'x4' ] fork. Processor yield. Processor yield. "prints x3 x4. Follows #runUntilErrorOrReturnFrom: semantics" ................. [ ] ensure: [ [Processor activeProcess terminate] ensure: [Transcript show: 'x1']. Transcript show: 'x2' ] "Squeak prints x1 x2 and terminates UI - recoverable via Alt+. Cuis prints x1 and x2 and crashes due to Cannot delete file exception" | p | p := [ [] ensure: [ Processor activeProcess terminate. Transcript show: 'x1'. Processor activeProcess terminate. Transcript show: 'x2']. ] fork. Processor yield. Processor yield. Processor yield. Processor yield. Transcript show: p isTerminated printString "lets current UI live and prints x1 x2" | p | p := [ [] ensure: [ Processor activeProcess terminate. Transcript show: 'x1'. true ifTrue: [^2]. Processor activeProcess terminate. Transcript show: 'x2'] ] fork. Processor yield. Processor yield. Transcript show: p isTerminated printString "prints x1 and successfully escapes termination" | p ap | p := [ [ ap := Processor activeProcess. ap terminate. Transcript show: 'x1' ] ensure: [ ap terminate. Transcript show: 'x2' ]. ] fork. Processor yield. Processor yield. Processor yield. Transcript show: p isTerminated printString "lets current UI live, prints x2 and raises Computation finished error" | p | p := [ [ Processor activeProcess terminate ] ensure: [ Processor activeProcess terminate ]. ] fork. Processor yield. Processor yield. Processor yield. Transcript show: p isTerminated printString "Squeak left one unterminated process behind when active termination not enclosed in ensure (before unifying active/suspended termination); without #asContextWithSender: answers false; otherwise answers true"