help-smalltalk
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Help-smalltalk] Return from process


From: Paolo Bonzini
Subject: Re: [Help-smalltalk] Return from process
Date: Fri, 03 Nov 2006 10:54:29 +0100
User-agent: Thunderbird 1.5.0.7 (Macintosh/20060909)

Luca Bruno wrote:
Hello,
the following goes segmentation fault:

[^'test'] fork!
Aha, this was a tough one. :-) I was quite hesitant to muddle with the unwind code, but the regression tests are pretty good for that area so here it is.

The VM changes are relatively simple. The Process changes are too, you just need one more #ensure: there; there was also some debugging code in ProcessorScheduler that I had forgotten, I removed it...

It is quite mysterious instead why you need to execute the exception handler blocks with #valueAndResumeOnUnwind rather than #value. The reason is that when you do [^'test'] in a process the context executing #on:do: would be disabled; then #badReturnError raises an exception, this one terminates the process, and you would miss the termination raised by exception handler.

st> [^'foo'] fork!
Object: 'foo' error: return from a dead method context
SystemExceptions.BadReturn(Exception)>>#signal
SystemExceptions.BadReturn class(Exception class)>>#signal
String(Object)>>#badReturnError
BlockClosure>>#ensure:

:-)

Paolo
2006-11-03  Paolo Bonzini  <address@hidden>

        * kernel/BlkClosure.st: Mark #on:do: contexts as unwinding
        * kernel/ProcSched.st: Remove debug code.
        * kernel/Process.st: Wrap process not only
        in an #on:do:, but also in an #ensure: block.

        * tests/exceptions.st: Test exception raising within a process.

        * libgst/interp.c: Check if we hit the bottom of the stack in
        disable_non_unwind_contexts, and percolate a return value of true
        through unwind_to up to unwind_method.  Otherwise return false from
        both disable_non_unwind_contexts and unwind_to.


--- orig/kernel/BlkClosure.st
+++ mod/kernel/BlkClosure.st
@@ -198,7 +198,7 @@
     <exceptionHandlerSearch: BlockClosure exceptionHandlerSearchBlock
        reset: BlockClosure exceptionHandlerResetBlock>
     active := 0.
-    ^self value
+    ^self valueAndResumeOnUnwind
 !
 
 on: e1 do: b1 on: e2 do: b2
@@ -210,7 +210,7 @@
     <exceptionHandlerSearch: BlockClosure exceptionHandlerSearchBlock
        reset: BlockClosure exceptionHandlerResetBlock>
     active := 0.
-    ^self value
+    ^self valueAndResumeOnUnwind
 !
 
 on: e1 do: b1 on: e2 do: b2 on: e3 do: b3
@@ -223,7 +223,7 @@
     <exceptionHandlerSearch: BlockClosure exceptionHandlerSearchBlock
        reset: BlockClosure exceptionHandlerResetBlock>
     active := 0.
-    ^self value
+    ^self valueAndResumeOnUnwind
 !
 
 on: e1 do: b1 on: e2 do: b2 on: e3 do: b3 on: e4 do: b4
@@ -236,7 +236,7 @@
     <exceptionHandlerSearch: BlockClosure exceptionHandlerSearchBlock
        reset: BlockClosure exceptionHandlerResetBlock>
     active := 0.
-    ^self value
+    ^self valueAndResumeOnUnwind
 !
 
 on: e1 do: b1 on: e2 do: b2 on: e3 do: b3 on: e4 do: b4 on: e5 do: b5
@@ -249,7 +249,7 @@
     <exceptionHandlerSearch: BlockClosure exceptionHandlerSearchBlock
        reset: BlockClosure exceptionHandlerResetBlock>
     active := 0.
-    ^self value
+    ^self valueAndResumeOnUnwind
 !
 
 ifError: aBlock


--- orig/kernel/ProcSched.st
+++ mod/kernel/ProcSched.st
@@ -80,7 +80,6 @@
 
 terminateActive
     "Terminate the active process"
-    Smalltalk backtrace.
     self activeProcess terminate
 !
 


--- orig/kernel/Process.st
+++ mod/kernel/Process.st
@@ -307,13 +310,18 @@
     activePriority := Processor activePriority.
     closure := [
        [
-           self setPriorityFrom: activePriority to: aPriority suspend: 
aBoolean.
-           aBlockClosure value ]
-
-           on: SystemExceptions.ProcessBeingTerminated
-           do: [ :sig | sig return ].
+           [
+               self setPriorityFrom: activePriority to: aPriority suspend: 
aBoolean.
+               aBlockClosure value
+           ]
+               on: SystemExceptions.ProcessBeingTerminated
+
+               "If we terminate in the handler, the 'ensure' blocks are not
+                evaluated.  Instead, if the handler returns, the unwinding
+                is done properly."
+               do: [ :sig | sig return ].
 
-       self primTerminate
+       ] ensure: [ self primTerminate ] .
     ].
 
     "Start the Process immediately so that we get into the


--- orig/libgst/interp.c
+++ mod/libgst/interp.c
@@ -484,7 +484,7 @@
    or an unwind method.  In this case the non-unwind contexts between
    the unwind method and the returnContextOOP must be removed from the
    chain.  */
-static void unwind_to (OOP returnContextOOP);
+static mst_Boolean unwind_to (OOP returnContextOOP);
 
 /* Arrange things so that all the non-unwinding contexts up to
    returnContextOOP aren't executed.  For block contexts this can
@@ -493,7 +493,7 @@
    from them!  For this reason, method contexts are flagged as
    disabled and unwind_context takes care of skipping them when
    doing a local return.  */
-static void disable_non_unwind_contexts (OOP returnContextOOP);
+static mst_Boolean disable_non_unwind_contexts (OOP returnContextOOP);
 
 /* Called to handle signals that are not passed to the Smalltalk
    program, such as interrupts or segmentation violation.  In the
@@ -1120,12 +1120,11 @@
       return (false);
     }
 
-  unwind_to (newContext->parentContext);
-  return (true);
+  return unwind_to (newContext->parentContext);
 }
 
 
-void
+mst_Boolean
 unwind_to (OOP returnContextOOP)
 {
   OOP oldContextOOP, newContextOOP;
@@ -1148,15 +1147,16 @@
       /* Check if we got to an unwinding context (#ensure:).  */
       if UNCOMMON (CONTEXT_FLAGS (newContext) & MCF_IS_UNWIND_CONTEXT)
         {
+         mst_Boolean result;
          _gst_this_context_oop = oldContextOOP;
 
          /* _gst_this_context_oop is the context above the
             one we return to.   We only unwind up to the #ensure:
             context.  */
-         disable_non_unwind_contexts (returnContextOOP);
+         result = disable_non_unwind_contexts (returnContextOOP);
 
          unwind_context ();
-         return;
+         return result;
        }
 
       /* This context cannot be deallocated in a LIFO way.  We must
@@ -1191,9 +1191,10 @@
   _gst_self = newContext->receiver;
 
   SET_THIS_METHOD (newContext->method, GET_CONTEXT_IP (newContext));
+  return (true);
 }
 
-void
+mst_Boolean
 disable_non_unwind_contexts (OOP returnContextOOP)
 {
   OOP oldContextOOP, newContextOOP, *chain;
@@ -1218,6 +1219,12 @@
           collect more context objects.  */
         oldContext->parentContext = _gst_nil_oop;
 
+      if (IS_NIL (newContextOOP))
+       {
+         *chain = newContextOOP;
+         return (false);
+       }
+
       if (newContextOOP == returnContextOOP)
        {
          *chain = newContextOOP;
@@ -1255,6 +1262,7 @@
     }
 
   *chain = newContext->parentContext;
+  return (true);
 }
 
 


--- orig/tests/exceptions.ok
+++ mod/tests/exceptions.ok
@@ -160,3 +160,11 @@
 Execution begins...
  error: did not understand #goodness:
 returned value is nil
+
+Execution begins...
+ error: return from a dead method context
+returned value is Process new "<0>"
+
+Execution begins...
+ error: test
+returned value is Process new "<0>"


--- orig/tests/exceptions.st
+++ mod/tests/exceptions.st
@@ -146,3 +146,9 @@
 
 "used to go in an infinite loop"
 [ self halt ] on: 1 do: [ :ex | 'blah' printNl ]!
+
+"Test error handling within a process."
+[^'test'] fork!
+
+"Test error handling within a process."
+[self error: 'test'] fork!




reply via email to

[Prev in Thread] Current Thread [Next in Thread]