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
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);
Sp(0) = stg_unblockAsyncExceptionszh_ret_info;
}
}
- Sp_adj(-1);
TICK_UNKNOWN_CALL();
TICK_SLOW_CALL_v();
- jump RET_LBL(stg_ap_v);
+ jump stg_ap_v_fast;
}
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;
Sp(0) = stg_blockAsyncExceptionszh_ret_info;
}
}
- Sp_adj(-1);
TICK_UNKNOWN_CALL();
TICK_SLOW_CALL_v();
- jump RET_LBL(stg_ap_v);
+ jump stg_ap_v_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;
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));
{ \
W_ rval; \
rval = Sp(0); \
- Sp_adj(1); \
- Sp = Sp + SIZEOF_StgCatchFrame - WDS(1); \
+ Sp = Sp + SIZEOF_StgCatchFrame; \
Sp(0) = rval; \
jump ret; \
}
/* 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) =
TICK_CATCHF_PUSHED();
/* Apply R1 to the realworld token */
- Sp_adj(-1);
TICK_UNKNOWN_CALL();
TICK_SLOW_CALL_v();
- jump RET_LBL(stg_ap_v);
+ jump stg_ap_v_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;
}
* 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(MyCapability() "ptr", 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(MyCapability() "ptr", NO_TREC "ptr");
+ StgTSO_trec(CurrentTSO) = trec;
+ R1 = StgAtomicallyFrame_code(Sp);
+ jump stg_ap_v_fast;
+ }
+ }
if (frame_type == STOP_FRAME) {
- /* We've stripped the entire stack, the thread is now dead. */
- Sp = CurrentTSO + OFFSET_StgTSO_stack
- + WDS(StgTSO_stack_size(CurrentTSO)) - WDS(1);
- Sp(0) = R1; /* save the exception */
+ /*
+ * We've stripped the entire stack, the thread is now dead.
+ * We will leave the stack in a GC'able state, see the stg_stop_thread
+ * entry code in StgStartup.cmm.
+ */
+ Sp = CurrentTSO + TSO_OFFSET_StgTSO_stack
+ + WDS(StgTSO_stack_size(CurrentTSO)) - WDS(2);
+ Sp(1) = R1; /* save the exception */
+ Sp(0) = stg_enter_info; /* so that GC can traverse this stack */
StgTSO_what_next(CurrentTSO) = ThreadKilled::I16;
SAVE_THREAD_STATE(); /* inline! */
- R1 = ThreadFinished;
+
+ /* The return code goes in BaseReg->rRet, and BaseReg is returned in R1 */
+ StgRegTable_rRet(BaseReg) = ThreadFinished;
+ R1 = BaseReg;
+
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.
*/
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.