[project @ 2005-10-21 14:02:17 by simonmar]
[ghc-hetmet.git] / ghc / rts / Exception.cmm
index 04f328b..4007b78 100644 (file)
    it.  The action of unblocking exceptions in a thread will release all
    the threads waiting to deliver exceptions to that thread.
 
+   NB. there's a bug in here.  If a thread is inside an
+   unsafePerformIO, and inside blockAsyncExceptions# (there is an
+   unblockAsyncExceptions_ret on the stack), and it is blocked in an
+   interruptible operation, and it receives an exception, then the
+   unsafePerformIO thunk will be updated with a stack object
+   containing the unblockAsyncExceptions_ret frame.  Later, when
+   someone else evaluates this thunk, the blocked exception state is
+   not restored, and the result is that unblockAsyncExceptions_ret
+   will attempt to unblock exceptions in the current thread, but it'll
+   find that the CurrentTSO->blocked_exceptions is NULL.  Hence, we
+   work around this by checking for NULL in awakenBlockedQueue().
+
    -------------------------------------------------------------------------- */
 
 INFO_TABLE_RET( stg_unblockAsyncExceptionszh_ret,
                0/*framesize*/, 0/*bitmap*/, RET_SMALL )
 {
-    ASSERT(StgTSO_blocked_exceptions(CurrentTSO) != NULL);
+    // Not true: see comments above
+    // ASSERT(StgTSO_blocked_exceptions(CurrentTSO) != NULL);
 #if defined(GRAN) || defined(PAR)
-    foreign "C" awakenBlockedQueue(StgTSO_blocked_exceptions(CurrentTSO) "ptr", 
+    foreign "C" awakenBlockedQueue(MyCapability() "ptr", StgTSO_blocked_exceptions(CurrentTSO) "ptr", 
                                   NULL "ptr"); 
 #else
-    foreign "C" awakenBlockedQueue(StgTSO_blocked_exceptions(CurrentTSO) "ptr");
+    foreign "C" awakenBlockedQueue(MyCapability() "ptr", StgTSO_blocked_exceptions(CurrentTSO) "ptr");
 #endif
     StgTSO_blocked_exceptions(CurrentTSO) = NULL;
 #ifdef REG_R1
@@ -61,7 +74,8 @@ INFO_TABLE_RET( stg_unblockAsyncExceptionszh_ret,
 INFO_TABLE_RET( stg_blockAsyncExceptionszh_ret,
                0/*framesize*/, 0/*bitmap*/, RET_SMALL )
 {
-    ASSERT(StgTSO_blocked_exceptions(CurrentTSO) == NULL);
+    // Not true: see comments above
+    // ASSERT(StgTSO_blocked_exceptions(CurrentTSO) == NULL);
     StgTSO_blocked_exceptions(CurrentTSO) = END_TSO_QUEUE;
 #ifdef REG_R1
     Sp_adj(1);
@@ -101,10 +115,10 @@ unblockAsyncExceptionszh_fast
 
     if (StgTSO_blocked_exceptions(CurrentTSO) != NULL) {
 #if defined(GRAN) || defined(PAR)
-      foreign "C" awakenBlockedQueue(StgTSO_blocked_exceptions(CurrentTSO) "ptr", 
+      foreign "C" awakenBlockedQueue(MyCapability() "ptr", StgTSO_blocked_exceptions(CurrentTSO) "ptr", 
                                     StgTSO_block_info(CurrentTSO) "ptr");
 #else
-      foreign "C" awakenBlockedQueue(StgTSO_blocked_exceptions(CurrentTSO) "ptr");
+      foreign "C" awakenBlockedQueue(MyCapability() "ptr", StgTSO_blocked_exceptions(CurrentTSO) "ptr");
 #endif
       StgTSO_blocked_exceptions(CurrentTSO) = NULL;
 
@@ -177,7 +191,7 @@ killThreadzh_fast
    */
   if (R1 == CurrentTSO) {
        SAVE_THREAD_STATE();
-       foreign "C" raiseAsyncWithLock(R1 "ptr", R2 "ptr");
+       foreign "C" raiseAsync(MyCapability() "ptr", R1 "ptr", R2 "ptr");
        if (StgTSO_what_next(CurrentTSO) == ThreadKilled::I16) {
                R1 = ThreadFinished;
                jump StgReturn;
@@ -187,7 +201,7 @@ killThreadzh_fast
                jump %ENTRY_CODE(Sp(0));
        }
   } else {
-       foreign "C" raiseAsyncWithLock(R1 "ptr", R2 "ptr");
+       foreign "C" raiseAsync(MyCapability() "ptr", R1 "ptr", R2 "ptr");
   }
 
   jump %ENTRY_CODE(Sp(0));
@@ -210,8 +224,7 @@ killThreadzh_fast
    {                                           \
       W_ rval;                                 \
       rval = Sp(0);                            \
-      Sp_adj(1);                               \
-      Sp = Sp + SIZEOF_StgCatchFrame - WDS(1);  \
+      Sp = Sp + SIZEOF_StgCatchFrame;          \
       Sp(0) = rval;                            \
       jump ret;                                        \
    }
@@ -286,7 +299,7 @@ catchzh_fast
   
     /* Set up the catch frame */
     Sp = Sp - SIZEOF_StgCatchFrame;
-    SET_HDR(Sp,stg_catch_frame_info,CCCS);
+    SET_HDR(Sp,stg_catch_frame_info,W_[CCCS]);
     
     StgCatchFrame_handler(Sp) = R2;
     StgCatchFrame_exceptions_blocked(Sp) = 
@@ -310,9 +323,9 @@ catchzh_fast
  * It is used in raisezh_fast to update thunks on the update list
  * -------------------------------------------------------------------------- */
 
-INFO_TABLE(stg_raise,1,0,THUNK,"raise","raise")
+INFO_TABLE(stg_raise,1,0,THUNK_1_0,"raise","raise")
 {
-  R1 = StgClosure_payload(R1,0);
+  R1 = StgThunk_payload(R1,0);
   jump raisezh_fast;
 }
 
@@ -331,13 +344,38 @@ raisezh_fast
      * the info was only displayed for an *uncaught* exception.
      */
     if (RtsFlags_ProfFlags_showCCSOnException(RtsFlags)) {
-      foreign "C" fprintCCS(stderr,CCCS);
+      foreign "C" fprintCCS_stderr(W_[CCCS] "ptr");
     }
 #endif
 
+retry_pop_stack:
     StgTSO_sp(CurrentTSO) = Sp;
-    frame_type = foreign "C" raiseExceptionHelper(CurrentTSO "ptr", R1 "ptr");
+    frame_type = foreign "C" raiseExceptionHelper(BaseReg "ptr", CurrentTSO "ptr", R1 "ptr");
     Sp = StgTSO_sp(CurrentTSO);
+    if (frame_type == ATOMICALLY_FRAME) {
+      /* The exception has reached the edge of a memory transaction.  Check that 
+       * the transaction is valid.  If not then perhaps the exception should
+       * not have been thrown: re-run the transaction */
+      W_ trec;
+      W_ r;
+      trec = StgTSO_trec(CurrentTSO);
+      r = foreign "C" stmValidateNestOfTransactions(trec "ptr");
+      foreign "C" stmAbortTransaction(trec "ptr");
+      StgTSO_trec(CurrentTSO) = NO_TREC;
+      if (r) {
+        // Transaction was valid: continue searching for a catch frame
+        Sp = Sp + SIZEOF_StgAtomicallyFrame;
+        goto retry_pop_stack;
+      } else {
+        // Transaction was not valid: we retry the exception (otherwise continue
+        // with a further call to raiseExceptionHelper)
+        "ptr" trec = foreign "C" stmStartTransaction(BaseReg "ptr", NO_TREC "ptr");
+        StgTSO_trec(CurrentTSO) = trec;
+        R1 = StgAtomicallyFrame_code(Sp);
+        Sp_adj(-1);
+        jump RET_LBL(stg_ap_v);
+      }          
+    }
 
     if (frame_type == STOP_FRAME) {
        /* We've stripped the entire stack, the thread is now dead. */
@@ -350,10 +388,14 @@ raisezh_fast
        jump StgReturn;
     }
 
-    /* Ok, Sp points to the enclosing CATCH_FRAME.  Pop everything down to
-     * and including this frame, update Su, push R1, and enter the handler.
+    /* Ok, Sp points to the enclosing CATCH_FRAME or CATCH_STM_FRAME.  Pop everything
+     * down to and including this frame, update Su, push R1, and enter the handler.
      */
-    handler = StgCatchFrame_handler(Sp);
+    if (frame_type == CATCH_FRAME) {
+      handler = StgCatchFrame_handler(Sp);
+    } else {
+      handler = StgCatchSTMFrame_handler(Sp);
+    }
 
     /* Restore the blocked/unblocked state for asynchronous exceptions
      * at the CATCH_FRAME.  
@@ -364,11 +406,14 @@ raisezh_fast
      */
     W_ frame;
     frame = Sp;
-    Sp = Sp + SIZEOF_StgCatchFrame;
-
-    if (StgCatchFrame_exceptions_blocked(frame) == 0) {
-      Sp_adj(-1);
-      Sp(0) = stg_unblockAsyncExceptionszh_ret_info;
+    if (frame_type == CATCH_FRAME) {
+      Sp = Sp + SIZEOF_StgCatchFrame;
+      if (StgCatchFrame_exceptions_blocked(frame) == 0) {
+        Sp_adj(-1);
+        Sp(0) = stg_unblockAsyncExceptionszh_ret_info;
+      }
+    } else {
+      Sp = Sp + SIZEOF_StgCatchSTMFrame;
     }
 
     /* Ensure that async excpetions are blocked when running the handler.