[commit: ghc] master: If exceptions are blocked, add stack overflow to blocked exceptions list. Fixes #8303. (c6af06e)

git at git.haskell.org git
Fri Oct 11 23:50:48 UTC 2013


Repository : ssh://git at git.haskell.org/ghc

On branch  : master
Link       : http://ghc.haskell.org/trac/ghc/changeset/c6af06ef38813c460eaa0dec613eae98f4a57796/ghc

>---------------------------------------------------------------

commit c6af06ef38813c460eaa0dec613eae98f4a57796
Author: Edward Z. Yang <ezyang at mit.edu>
Date:   Thu Oct 10 18:37:32 2013 -0700

    If exceptions are blocked, add stack overflow to blocked exceptions list. Fixes #8303.
    
    Signed-off-by: Edward Z. Yang <ezyang at mit.edu>


>---------------------------------------------------------------

c6af06ef38813c460eaa0dec613eae98f4a57796
 rts/RaiseAsync.c |    5 +----
 rts/RaiseAsync.h |    3 +++
 rts/Threads.c    |   66 ++++++++++++++++++++++++++++++++++++++++++------------
 3 files changed, 56 insertions(+), 18 deletions(-)

diff --git a/rts/RaiseAsync.c b/rts/RaiseAsync.c
index edc4a91..86a16d3 100644
--- a/rts/RaiseAsync.c
+++ b/rts/RaiseAsync.c
@@ -33,9 +33,6 @@ static void removeFromQueues(Capability *cap, StgTSO *tso);
 
 static void removeFromMVarBlockedQueue (StgTSO *tso);
 
-static void blockedThrowTo (Capability *cap, 
-                            StgTSO *target, MessageThrowTo *msg);
-
 static void throwToSendMsg (Capability *cap USED_IF_THREADS,
                             Capability *target_cap USED_IF_THREADS, 
                             MessageThrowTo *msg USED_IF_THREADS);
@@ -467,7 +464,7 @@ throwToSendMsg (Capability *cap STG_UNUSED,
 // Block a throwTo message on the target TSO's blocked_exceptions
 // queue.  The current Capability must own the target TSO in order to
 // modify the blocked_exceptions queue.
-static void
+void
 blockedThrowTo (Capability *cap, StgTSO *target, MessageThrowTo *msg)
 {
     debugTraceCap(DEBUG_sched, cap, "throwTo: blocking on thread %lu",
diff --git a/rts/RaiseAsync.h b/rts/RaiseAsync.h
index d804f6b..1f61b8c 100644
--- a/rts/RaiseAsync.h
+++ b/rts/RaiseAsync.h
@@ -16,6 +16,9 @@
 
 #include "BeginPrivate.h"
 
+void blockedThrowTo (Capability *cap, 
+                     StgTSO *target, MessageThrowTo *msg);
+
 void throwToSingleThreaded (Capability *cap,
 			    StgTSO *tso,
 			    StgClosure *exception);
diff --git a/rts/Threads.c b/rts/Threads.c
index 0971288..1c45000 100644
--- a/rts/Threads.c
+++ b/rts/Threads.c
@@ -499,18 +499,7 @@ threadStackOverflow (Capability *cap, StgTSO *tso)
 
     IF_DEBUG(sanity,checkTSO(tso));
 
-    if (tso->tot_stack_size >= RtsFlags.GcFlags.maxStkSize
-        && !(tso->flags & TSO_BLOCKEX)) {
-        // NB. never raise a StackOverflow exception if the thread is
-        // inside Control.Exception.mask.  It is impractical to protect
-        // against stack overflow exceptions, since virtually anything
-        // can raise one (even 'catch'), so this is the only sensible
-        // thing to do here.  See bug #767.
-        //
-
-        if (tso->flags & TSO_SQUEEZED) {
-            return;
-        }
+    if (tso->tot_stack_size >= RtsFlags.GcFlags.maxStkSize) {
         // #3677: In a stack overflow situation, stack squeezing may
         // reduce the stack size, but we don't know whether it has been
         // reduced enough for the stack check to succeed if we try
@@ -520,6 +509,9 @@ threadStackOverflow (Capability *cap, StgTSO *tso)
         // happened, then we try running the thread again.  The
         // TSO_SQUEEZED flag is set by threadPaused() to tell us whether
         // squeezing happened or not.
+        if (tso->flags & TSO_SQUEEZED) {
+            return;
+        }
 
         debugTrace(DEBUG_gc,
                    "threadStackOverflow of TSO %ld (%p): stack too large (now %ld; max is %ld)",
@@ -531,8 +523,20 @@ threadStackOverflow (Capability *cap, StgTSO *tso)
                                  stg_min(tso->stackobj->stack + tso->stackobj->stack_size,
                                          tso->stackobj->sp+64)));
 
-        // Send this thread the StackOverflow exception
-        throwToSingleThreaded(cap, tso, (StgClosure *)stackOverflow_closure);
+        if (tso->flags & TSO_BLOCKEX) {
+            // NB. StackOverflow exceptions must be deferred if the thread is
+            // inside Control.Exception.mask.  See bug #767 and bug #8303.
+            // This implementation is a minor hack, see Note [Throw to self when masked]
+            MessageThrowTo *msg = (MessageThrowTo*)allocate(cap, sizeofW(MessageThrowTo));
+            SET_HDR(msg, &stg_MSG_THROWTO_info, CCS_SYSTEM);
+            msg->source = tso;
+            msg->target = tso;
+            msg->exception = (StgClosure *)stackOverflow_closure;
+            blockedThrowTo(cap, tso, msg);
+        } else {
+            // Send this thread the StackOverflow exception
+            throwToSingleThreaded(cap, tso, (StgClosure *)stackOverflow_closure);
+        }
     }
 
 
@@ -663,6 +667,40 @@ threadStackOverflow (Capability *cap, StgTSO *tso)
     // IF_DEBUG(scheduler,printTSO(new_tso));
 }
 
+/* Note [Throw to self when masked]
+ *
+ * When a StackOverflow occurs when the thread is masked, we want to
+ * defer the exception to when the thread becomes unmasked/hits an
+ * interruptible point.  We already have a mechanism for doing this,
+ * the blocked_exceptions list, but the use here is a bit unusual,
+ * because an exception is normally only added to this list upon
+ * an asynchronous 'throwTo' call (with all of the relevant
+ * multithreaded nonsense). Morally, a stack overflow should be an
+ * asynchronous exception sent by a thread to itself, and it should
+ * have the same semantics.  But there are a few key differences:
+ *
+ * - If you actually tried to send an asynchronous exception to
+ *   yourself using throwTo, the exception would actually immediately
+ *   be delivered.  This is because throwTo itself is considered an
+ *   interruptible point, so the exception is always deliverable. Thus,
+ *   ordinarily, we never end up with a message to onesself in the
+ *   blocked_exceptions queue.
+ *
+ * - In the case of a StackOverflow, we don't actually care about the
+ *   wakeup semantics; when an exception is delivered, the thread that
+ *   originally threw the exception should be woken up, since throwTo
+ *   blocks until the exception is successfully thrown.  Fortunately,
+ *   it is harmless to wakeup a thread that doesn't actually need waking
+ *   up, e.g. ourselves.
+ *
+ * - No synchronization is necessary, because we own the TSO and the
+ *   capability.  You can observe this by tracing through the execution
+ *   of throwTo.  We skip synchronizing the message and inter-capability
+ *   communication.
+ *
+ * We think this doesn't break any invariants, but do be careful!
+ */
+
 
 /* ---------------------------------------------------------------------------
    Stack underflow - called from the stg_stack_underflow_info frame




More information about the ghc-commits mailing list