1 /* ---------------------------------------------------------------------------
3 * (c) The GHC Team, 1998-2006
5 * Asynchronous exceptions
7 * --------------------------------------------------------------------------*/
9 #include "PosixSource.h"
13 #include "RaiseAsync.h"
16 #include "LdvProfile.h"
20 #include "Profiling.h"
21 #if defined(mingw32_HOST_OS)
22 #include "win32/IOManager.h"
25 static void raiseAsync (Capability *cap,
27 StgClosure *exception,
28 rtsBool stop_at_atomically,
29 StgUpdateFrame *stop_here);
31 static void removeFromQueues(Capability *cap, StgTSO *tso);
33 static void blockedThrowTo (Capability *cap, StgTSO *source, StgTSO *target);
35 static void performBlockedException (Capability *cap,
36 StgTSO *source, StgTSO *target);
38 /* -----------------------------------------------------------------------------
41 This version of throwTo is safe to use if and only if one of the
46 - all the other threads in the system are stopped (eg. during GC).
48 - we surely own the target TSO (eg. we just took it from the
49 run queue of the current capability, or we are running it).
51 It doesn't cater for blocking the source thread until the exception
53 -------------------------------------------------------------------------- */
56 throwToSingleThreaded(Capability *cap, StgTSO *tso, StgClosure *exception)
58 throwToSingleThreaded_(cap, tso, exception, rtsFalse);
62 throwToSingleThreaded_(Capability *cap, StgTSO *tso, StgClosure *exception,
63 rtsBool stop_at_atomically)
65 // Thread already dead?
66 if (tso->what_next == ThreadComplete || tso->what_next == ThreadKilled) {
70 // Remove it from any blocking queues
71 removeFromQueues(cap,tso);
73 raiseAsync(cap, tso, exception, stop_at_atomically, NULL);
77 suspendComputation(Capability *cap, StgTSO *tso, StgUpdateFrame *stop_here)
79 // Thread already dead?
80 if (tso->what_next == ThreadComplete || tso->what_next == ThreadKilled) {
84 // Remove it from any blocking queues
85 removeFromQueues(cap,tso);
87 raiseAsync(cap, tso, NULL, rtsFalse, stop_here);
90 /* -----------------------------------------------------------------------------
93 This function may be used to throw an exception from one thread to
94 another, during the course of normal execution. This is a tricky
95 task: the target thread might be running on another CPU, or it
96 may be blocked and could be woken up at any point by another CPU.
97 We have some delicate synchronisation to do.
99 There is a completely safe fallback scheme: it is always possible
100 to just block the source TSO on the target TSO's blocked_exceptions
101 queue. This queue is locked using lockTSO()/unlockTSO(). It is
102 checked at regular intervals: before and after running a thread
103 (schedule() and threadPaused() respectively), and just before GC
104 (scheduleDoGC()). Activating a thread on this queue should be done
105 using maybePerformBlockedException(): this is done in the context
106 of the target thread, so the exception can be raised eagerly.
108 This fallback scheme works even if the target thread is complete or
109 killed: scheduleDoGC() will discover the blocked thread before the
112 Blocking the source thread on the target thread's blocked_exception
113 queue is also employed when the target thread is currently blocking
114 exceptions (ie. inside Control.Exception.block).
116 We could use the safe fallback scheme exclusively, but that
117 wouldn't be ideal: most calls to throwTo would block immediately,
118 possibly until the next GC, which might require the deadlock
119 detection mechanism to kick in. So we try to provide promptness
122 We can promptly deliver the exception if the target thread is:
124 - runnable, on the same Capability as the source thread (because
125 we own the run queue and therefore the target thread).
127 - blocked, and we can obtain exclusive access to it. Obtaining
128 exclusive access to the thread depends on how it is blocked.
130 We must also be careful to not trip over threadStackOverflow(),
131 which might be moving the TSO to enlarge its stack.
132 lockTSO()/unlockTSO() are used here too.
136 THROWTO_SUCCESS exception was raised, ok to continue
138 THROWTO_BLOCKED exception was not raised; block the source
139 thread then call throwToReleaseTarget() when
140 the source thread is properly tidied away.
142 -------------------------------------------------------------------------- */
145 throwTo (Capability *cap, // the Capability we hold
146 StgTSO *source, // the TSO sending the exception
147 StgTSO *target, // the TSO receiving the exception
148 StgClosure *exception, // the exception closure
149 /*[out]*/ void **out USED_IF_THREADS)
153 // follow ThreadRelocated links in the target first
154 while (target->what_next == ThreadRelocated) {
155 target = target->_link;
156 // No, it might be a WHITEHOLE:
157 // ASSERT(get_itbl(target)->type == TSO);
160 debugTrace(DEBUG_sched, "throwTo: from thread %lu to thread %lu",
161 (unsigned long)source->id, (unsigned long)target->id);
164 if (traceClass(DEBUG_sched)) {
165 debugTraceBegin("throwTo: target");
166 printThreadStatus(target);
173 debugTrace(DEBUG_sched, "throwTo: retrying...");
176 // Thread already dead?
177 if (target->what_next == ThreadComplete
178 || target->what_next == ThreadKilled) {
179 return THROWTO_SUCCESS;
182 status = target->why_blocked;
186 /* if status==NotBlocked, and target->cap == cap, then
187 we own this TSO and can raise the exception.
189 How do we establish this condition? Very carefully.
192 P = (status == NotBlocked)
193 Q = (tso->cap == cap)
195 Now, if P & Q are true, then the TSO is locked and owned by
196 this capability. No other OS thread can steal it.
198 If P==0 and Q==1: the TSO is blocked, but attached to this
199 capabilty, and it can be stolen by another capability.
201 If P==1 and Q==0: the TSO is runnable on another
202 capability. At any time, the TSO may change from runnable
203 to blocked and vice versa, while it remains owned by
206 Suppose we test like this:
212 this is defeated by another capability stealing a blocked
213 TSO from us to wake it up (Schedule.c:unblockOne()). The
214 other thread is doing
219 assuming arbitrary reordering, we could see this
229 so we need a memory barrier:
236 this avoids the problematic case. There are other cases
237 to consider, but this is the tricky one.
239 Note that we must be sure that unblockOne() does the
240 writes in the correct order: Q before P. The memory
241 barrier ensures that if we have seen the write to P, we
242 have also seen the write to Q.
245 Capability *target_cap;
248 target_cap = target->cap;
249 if (target_cap == cap && (target->flags & TSO_BLOCKEX) == 0) {
250 // It's on our run queue and not blocking exceptions
251 raiseAsync(cap, target, exception, rtsFalse, NULL);
252 return THROWTO_SUCCESS;
254 // Otherwise, just block on the blocked_exceptions queue
255 // of the target thread. The queue will get looked at
256 // soon enough: it is checked before and after running a
257 // thread, and during GC.
260 // Avoid race with threadStackOverflow, which may have
261 // just moved this TSO.
262 if (target->what_next == ThreadRelocated) {
264 target = target->_link;
267 blockedThrowTo(cap,source,target);
269 return THROWTO_BLOCKED;
276 To establish ownership of this TSO, we need to acquire a
277 lock on the MVar that it is blocked on.
280 StgInfoTable *info USED_IF_THREADS;
282 mvar = (StgMVar *)target->block_info.closure;
284 // ASSUMPTION: tso->block_info must always point to a
285 // closure. In the threaded RTS it does.
286 switch (get_itbl(mvar)->type) {
294 info = lockClosure((StgClosure *)mvar);
296 if (target->what_next == ThreadRelocated) {
297 target = target->_link;
298 unlockClosure((StgClosure *)mvar,info);
301 // we have the MVar, let's check whether the thread
302 // is still blocked on the same MVar.
303 if (target->why_blocked != BlockedOnMVar
304 || (StgMVar *)target->block_info.closure != mvar) {
305 unlockClosure((StgClosure *)mvar, info);
309 if ((target->flags & TSO_BLOCKEX) &&
310 ((target->flags & TSO_INTERRUPTIBLE) == 0)) {
311 lockClosure((StgClosure *)target);
312 blockedThrowTo(cap,source,target);
313 unlockClosure((StgClosure *)mvar, info);
315 return THROWTO_BLOCKED; // caller releases TSO
317 removeThreadFromMVarQueue(cap, mvar, target);
318 raiseAsync(cap, target, exception, rtsFalse, NULL);
319 unblockOne(cap, target);
320 unlockClosure((StgClosure *)mvar, info);
321 return THROWTO_SUCCESS;
325 case BlockedOnBlackHole:
327 ACQUIRE_LOCK(&sched_mutex);
328 // double checking the status after the memory barrier:
329 if (target->why_blocked != BlockedOnBlackHole) {
330 RELEASE_LOCK(&sched_mutex);
334 if (target->flags & TSO_BLOCKEX) {
336 blockedThrowTo(cap,source,target);
337 RELEASE_LOCK(&sched_mutex);
339 return THROWTO_BLOCKED; // caller releases TSO
341 removeThreadFromQueue(cap, &blackhole_queue, target);
342 raiseAsync(cap, target, exception, rtsFalse, NULL);
343 unblockOne(cap, target);
344 RELEASE_LOCK(&sched_mutex);
345 return THROWTO_SUCCESS;
349 case BlockedOnException:
355 To obtain exclusive access to a BlockedOnException thread,
356 we must call lockClosure() on the TSO on which it is blocked.
357 Since the TSO might change underneath our feet, after we
358 call lockClosure() we must check that
360 (a) the closure we locked is actually a TSO
361 (b) the original thread is still BlockedOnException,
362 (c) the original thread is still blocked on the TSO we locked
363 and (d) the target thread has not been relocated.
365 We synchronise with threadStackOverflow() (which relocates
366 threads) using lockClosure()/unlockClosure().
368 target2 = target->block_info.tso;
370 info = lockClosure((StgClosure *)target2);
371 if (info != &stg_TSO_info) {
372 unlockClosure((StgClosure *)target2, info);
375 if (target->what_next == ThreadRelocated) {
376 target = target->_link;
380 if (target2->what_next == ThreadRelocated) {
381 target->block_info.tso = target2->_link;
385 if (target->why_blocked != BlockedOnException
386 || target->block_info.tso != target2) {
392 Now we have exclusive rights to the target TSO...
394 If it is blocking exceptions, add the source TSO to its
395 blocked_exceptions queue. Otherwise, raise the exception.
397 if ((target->flags & TSO_BLOCKEX) &&
398 ((target->flags & TSO_INTERRUPTIBLE) == 0)) {
400 blockedThrowTo(cap,source,target);
403 return THROWTO_BLOCKED;
405 removeThreadFromQueue(cap, &target2->blocked_exceptions, target);
406 raiseAsync(cap, target, exception, rtsFalse, NULL);
407 unblockOne(cap, target);
409 return THROWTO_SUCCESS;
415 // Unblocking BlockedOnSTM threads requires the TSO to be
416 // locked; see STM.c:unpark_tso().
417 if (target->why_blocked != BlockedOnSTM) {
421 if ((target->flags & TSO_BLOCKEX) &&
422 ((target->flags & TSO_INTERRUPTIBLE) == 0)) {
423 blockedThrowTo(cap,source,target);
425 return THROWTO_BLOCKED;
427 raiseAsync(cap, target, exception, rtsFalse, NULL);
428 unblockOne(cap, target);
430 return THROWTO_SUCCESS;
434 case BlockedOnCCall_NoUnblockExc:
435 // I don't think it's possible to acquire ownership of a
436 // BlockedOnCCall thread. We just assume that the target
437 // thread is blocking exceptions, and block on its
438 // blocked_exception queue.
440 if (target->why_blocked != BlockedOnCCall &&
441 target->why_blocked != BlockedOnCCall_NoUnblockExc) {
445 blockedThrowTo(cap,source,target);
447 return THROWTO_BLOCKED;
449 #ifndef THREADEDED_RTS
453 #if defined(mingw32_HOST_OS)
454 case BlockedOnDoProc:
456 if ((target->flags & TSO_BLOCKEX) &&
457 ((target->flags & TSO_INTERRUPTIBLE) == 0)) {
458 blockedThrowTo(cap,source,target);
459 return THROWTO_BLOCKED;
461 removeFromQueues(cap,target);
462 raiseAsync(cap, target, exception, rtsFalse, NULL);
463 return THROWTO_SUCCESS;
468 barf("throwTo: unrecognised why_blocked value");
473 // Block a TSO on another TSO's blocked_exceptions queue.
474 // Precondition: we hold an exclusive lock on the target TSO (this is
475 // complex to achieve as there's no single lock on a TSO; see
478 blockedThrowTo (Capability *cap, StgTSO *source, StgTSO *target)
480 debugTrace(DEBUG_sched, "throwTo: blocking on thread %lu", (unsigned long)target->id);
481 setTSOLink(cap, source, target->blocked_exceptions);
482 target->blocked_exceptions = source;
483 dirty_TSO(cap,target); // we modified the blocked_exceptions queue
485 source->block_info.tso = target;
486 write_barrier(); // throwTo_exception *must* be visible if BlockedOnException is.
487 source->why_blocked = BlockedOnException;
493 throwToReleaseTarget (void *tso)
495 unlockTSO((StgTSO *)tso);
499 /* -----------------------------------------------------------------------------
500 Waking up threads blocked in throwTo
502 There are two ways to do this: maybePerformBlockedException() will
503 perform the throwTo() for the thread at the head of the queue
504 immediately, and leave the other threads on the queue.
505 maybePerformBlockedException() also checks the TSO_BLOCKEX flag
506 before raising an exception.
508 awakenBlockedExceptionQueue() will wake up all the threads in the
509 queue, but not perform any throwTo() immediately. This might be
510 more appropriate when the target thread is the one actually running
513 Returns: non-zero if an exception was raised, zero otherwise.
514 -------------------------------------------------------------------------- */
517 maybePerformBlockedException (Capability *cap, StgTSO *tso)
521 if (tso->what_next == ThreadComplete || tso->what_next == ThreadFinished) {
522 if (tso->blocked_exceptions != END_TSO_QUEUE) {
523 awakenBlockedExceptionQueue(cap,tso);
530 if (tso->blocked_exceptions != END_TSO_QUEUE &&
531 (tso->flags & TSO_BLOCKEX) != 0) {
532 debugTrace(DEBUG_sched, "throwTo: thread %lu has blocked exceptions but is inside block", (unsigned long)tso->id);
535 if (tso->blocked_exceptions != END_TSO_QUEUE
536 && ((tso->flags & TSO_BLOCKEX) == 0
537 || ((tso->flags & TSO_INTERRUPTIBLE) && interruptible(tso)))) {
539 // Lock the TSO, this gives us exclusive access to the queue
542 // Check the queue again; it might have changed before we
544 if (tso->blocked_exceptions == END_TSO_QUEUE) {
549 // We unblock just the first thread on the queue, and perform
550 // its throw immediately.
551 source = tso->blocked_exceptions;
552 performBlockedException(cap, source, tso);
553 tso->blocked_exceptions = unblockOne_(cap, source,
554 rtsFalse/*no migrate*/);
561 // awakenBlockedExceptionQueue(): Just wake up the whole queue of
562 // blocked exceptions and let them try again.
565 awakenBlockedExceptionQueue (Capability *cap, StgTSO *tso)
567 if (tso->blocked_exceptions != END_TSO_QUEUE) {
569 awakenBlockedQueue(cap, tso->blocked_exceptions);
570 tso->blocked_exceptions = END_TSO_QUEUE;
576 performBlockedException (Capability *cap, StgTSO *source, StgTSO *target)
578 StgClosure *exception;
580 ASSERT(source->why_blocked == BlockedOnException);
581 ASSERT(source->block_info.tso->id == target->id);
582 ASSERT(source->sp[0] == (StgWord)&stg_block_throwto_info);
583 ASSERT(((StgTSO *)source->sp[1])->id == target->id);
584 // check ids not pointers, because the thread might be relocated
586 exception = (StgClosure *)source->sp[2];
587 throwToSingleThreaded(cap, target, exception);
591 /* -----------------------------------------------------------------------------
592 Remove a thread from blocking queues.
594 This is for use when we raise an exception in another thread, which
597 Precondition: we have exclusive access to the TSO, via the same set
598 of conditions as throwToSingleThreaded() (c.f.).
599 -------------------------------------------------------------------------- */
602 removeFromQueues(Capability *cap, StgTSO *tso)
604 switch (tso->why_blocked) {
610 // Be careful: nothing to do here! We tell the scheduler that the
611 // thread is runnable and we leave it to the stack-walking code to
612 // abort the transaction while unwinding the stack. We should
613 // perhaps have a debugging test to make sure that this really
614 // happens and that the 'zombie' transaction does not get
619 removeThreadFromMVarQueue(cap, (StgMVar *)tso->block_info.closure, tso);
622 case BlockedOnBlackHole:
623 removeThreadFromQueue(cap, &blackhole_queue, tso);
626 case BlockedOnException:
628 StgTSO *target = tso->block_info.tso;
630 // NO: when called by threadPaused(), we probably have this
631 // TSO already locked (WHITEHOLEd) because we just placed
632 // ourselves on its queue.
633 // ASSERT(get_itbl(target)->type == TSO);
635 while (target->what_next == ThreadRelocated) {
636 target = target->_link;
639 removeThreadFromQueue(cap, &target->blocked_exceptions, tso);
643 #if !defined(THREADED_RTS)
646 #if defined(mingw32_HOST_OS)
647 case BlockedOnDoProc:
649 removeThreadFromDeQueue(cap, &blocked_queue_hd, &blocked_queue_tl, tso);
650 #if defined(mingw32_HOST_OS)
651 /* (Cooperatively) signal that the worker thread should abort
654 abandonWorkRequest(tso->block_info.async_result->reqID);
659 removeThreadFromQueue(cap, &sleeping_queue, tso);
664 barf("removeFromQueues: %d", tso->why_blocked);
668 tso->_link = END_TSO_QUEUE; // no write barrier reqd
669 tso->why_blocked = NotBlocked;
670 tso->block_info.closure = NULL;
671 appendToRunQueue(cap,tso);
673 // We might have just migrated this TSO to our Capability:
675 tso->bound->cap = cap;
680 /* -----------------------------------------------------------------------------
683 * The following function implements the magic for raising an
684 * asynchronous exception in an existing thread.
686 * We first remove the thread from any queue on which it might be
687 * blocked. The possible blockages are MVARs and BLACKHOLE_BQs.
689 * We strip the stack down to the innermost CATCH_FRAME, building
690 * thunks in the heap for all the active computations, so they can
691 * be restarted if necessary. When we reach a CATCH_FRAME, we build
692 * an application of the handler to the exception, and push it on
693 * the top of the stack.
695 * How exactly do we save all the active computations? We create an
696 * AP_STACK for every UpdateFrame on the stack. Entering one of these
697 * AP_STACKs pushes everything from the corresponding update frame
698 * upwards onto the stack. (Actually, it pushes everything up to the
699 * next update frame plus a pointer to the next AP_STACK object.
700 * Entering the next AP_STACK object pushes more onto the stack until we
701 * reach the last AP_STACK object - at which point the stack should look
702 * exactly as it did when we killed the TSO and we can continue
703 * execution by entering the closure on top of the stack.
705 * We can also kill a thread entirely - this happens if either (a) the
706 * exception passed to raiseAsync is NULL, or (b) there's no
707 * CATCH_FRAME on the stack. In either case, we strip the entire
708 * stack and replace the thread with a zombie.
710 * ToDo: in THREADED_RTS mode, this function is only safe if either
711 * (a) we hold all the Capabilities (eg. in GC, or if there is only
712 * one Capability), or (b) we own the Capability that the TSO is
713 * currently blocked on or on the run queue of.
715 * -------------------------------------------------------------------------- */
718 raiseAsync(Capability *cap, StgTSO *tso, StgClosure *exception,
719 rtsBool stop_at_atomically, StgUpdateFrame *stop_here)
721 StgRetInfoTable *info;
726 debugTrace(DEBUG_sched,
727 "raising exception in thread %ld.", (long)tso->id);
729 #if defined(PROFILING)
731 * Debugging tool: on raising an exception, show where we are.
732 * See also Exception.cmm:raisezh_fast.
733 * This wasn't done for asynchronous exceptions originally; see #1450
735 if (RtsFlags.ProfFlags.showCCSOnException)
737 fprintCCS_stderr(tso->prof.CCCS);
741 // mark it dirty; we're about to change its stack.
746 // ASSUMES: the thread is not already complete or dead. Upper
747 // layers should deal with that.
748 ASSERT(tso->what_next != ThreadComplete && tso->what_next != ThreadKilled);
750 if (stop_here != NULL) {
751 updatee = stop_here->updatee;
756 // The stack freezing code assumes there's a closure pointer on
757 // the top of the stack, so we have to arrange that this is the case...
759 if (sp[0] == (W_)&stg_enter_info) {
763 sp[0] = (W_)&stg_dummy_ret_closure;
767 while (stop_here == NULL || frame < (StgPtr)stop_here) {
769 // 1. Let the top of the stack be the "current closure"
771 // 2. Walk up the stack until we find either an UPDATE_FRAME or a
774 // 3. If it's an UPDATE_FRAME, then make an AP_STACK containing the
775 // current closure applied to the chunk of stack up to (but not
776 // including) the update frame. This closure becomes the "current
777 // closure". Go back to step 2.
779 // 4. If it's a CATCH_FRAME, then leave the exception handler on
780 // top of the stack applied to the exception.
782 // 5. If it's a STOP_FRAME, then kill the thread.
784 // NB: if we pass an ATOMICALLY_FRAME then abort the associated
787 info = get_ret_itbl((StgClosure *)frame);
789 switch (info->i.type) {
796 // First build an AP_STACK consisting of the stack chunk above the
797 // current update frame, with the top word on the stack as the
800 words = frame - sp - 1;
801 ap = (StgAP_STACK *)allocateLocal(cap,AP_STACK_sizeW(words));
804 ap->fun = (StgClosure *)sp[0];
806 for(i=0; i < (nat)words; ++i) {
807 ap->payload[i] = (StgClosure *)*sp++;
810 SET_HDR(ap,&stg_AP_STACK_info,
811 ((StgClosure *)frame)->header.prof.ccs /* ToDo */);
812 TICK_ALLOC_UP_THK(words+1,0);
814 //IF_DEBUG(scheduler,
815 // debugBelch("sched: Updating ");
816 // printPtr((P_)((StgUpdateFrame *)frame)->updatee);
817 // debugBelch(" with ");
818 // printObj((StgClosure *)ap);
821 if (((StgUpdateFrame *)frame)->updatee == updatee) {
822 // If this update frame points to the same closure as
823 // the update frame further down the stack
824 // (stop_here), then don't perform the update. We
825 // want to keep the blackhole in this case, so we can
826 // detect and report the loop (#2783).
827 ap = (StgAP_STACK*)updatee;
829 // Perform the update
830 // TODO: this may waste some work, if the thunk has
831 // already been updated by another thread.
832 UPD_IND(((StgUpdateFrame *)frame)->updatee, (StgClosure *)ap);
835 sp += sizeofW(StgUpdateFrame) - 1;
836 sp[0] = (W_)ap; // push onto stack
838 continue; //no need to bump frame
843 // We've stripped the entire stack, the thread is now dead.
844 tso->what_next = ThreadKilled;
845 tso->sp = frame + sizeofW(StgStopFrame);
850 // If we find a CATCH_FRAME, and we've got an exception to raise,
851 // then build the THUNK raise(exception), and leave it on
852 // top of the CATCH_FRAME ready to enter.
856 StgCatchFrame *cf = (StgCatchFrame *)frame;
860 if (exception == NULL) break;
862 // we've got an exception to raise, so let's pass it to the
863 // handler in this frame.
865 raise = (StgThunk *)allocateLocal(cap,sizeofW(StgThunk)+1);
866 TICK_ALLOC_SE_THK(1,0);
867 SET_HDR(raise,&stg_raise_info,cf->header.prof.ccs);
868 raise->payload[0] = exception;
870 // throw away the stack from Sp up to the CATCH_FRAME.
874 /* Ensure that async excpetions are blocked now, so we don't get
875 * a surprise exception before we get around to executing the
878 tso->flags |= TSO_BLOCKEX | TSO_INTERRUPTIBLE;
880 /* Put the newly-built THUNK on top of the stack, ready to execute
881 * when the thread restarts.
884 sp[-1] = (W_)&stg_enter_info;
886 tso->what_next = ThreadRunGHC;
887 IF_DEBUG(sanity, checkTSO(tso));
891 case ATOMICALLY_FRAME:
892 if (stop_at_atomically) {
893 ASSERT(stmGetEnclosingTRec(tso->trec) == NO_TREC);
894 stmCondemnTransaction(cap, tso -> trec);
896 tso->what_next = ThreadRunGHC;
899 // Not stop_at_atomically... fall through and abort the
902 case CATCH_STM_FRAME:
903 case CATCH_RETRY_FRAME:
904 // IF we find an ATOMICALLY_FRAME then we abort the
905 // current transaction and propagate the exception. In
906 // this case (unlike ordinary exceptions) we do not care
907 // whether the transaction is valid or not because its
908 // possible validity cannot have caused the exception
909 // and will not be visible after the abort.
912 StgTRecHeader *trec = tso -> trec;
913 StgTRecHeader *outer = stmGetEnclosingTRec(trec);
914 debugTrace(DEBUG_stm,
915 "found atomically block delivering async exception");
916 stmAbortTransaction(cap, trec);
917 stmFreeAbortedTRec(cap, trec);
926 // move on to the next stack frame
927 frame += stack_frame_sizeW((StgClosure *)frame);
930 // if we got here, then we stopped at stop_here
931 ASSERT(stop_here != NULL);