Ben Gamari pushed to branch wip/stm-mvar-deadlock-backtrace at Glasgow Haskell Compiler / GHC
Commits:
-
57e9a132
by Ben Gamari at 2026-06-21T11:49:24-04:00
-
c126d27d
by Ben Gamari at 2026-06-21T11:49:24-04:00
-
6ce60aca
by Ben Gamari at 2026-06-21T11:49:24-04:00
-
82c1e8b9
by Ben Gamari at 2026-06-21T11:49:24-04:00
19 changed files:
- libraries/base/src/Control/Exception/Base.hs
- libraries/base/src/GHC/IO/Exception.hs
- libraries/ghc-internal/include/RtsIfaceSymbols.h
- libraries/ghc-internal/src/GHC/Internal/Control/Exception/Base.hs
- libraries/ghc-internal/src/GHC/Internal/IO/Exception.hs
- rts/Prelude.h
- rts/RaiseAsync.c
- rts/RaiseAsync.h
- rts/RtsStartup.c
- rts/Schedule.c
- rts/include/rts/RtsToHsIface.h
- + testsuite/tests/rts/LoopBacktrace.hs
- + testsuite/tests/rts/LoopBacktrace.stderr
- + testsuite/tests/rts/LoopBacktrace.stdout
- + testsuite/tests/rts/MVarDeadlockBacktrace.hs
- + testsuite/tests/rts/MVarDeadlockBacktrace.stderr
- + testsuite/tests/rts/STMDeadlockBacktrace.hs
- + testsuite/tests/rts/STMDeadlockBacktrace.stderr
- testsuite/tests/rts/all.T
Changes:
| ... | ... | @@ -85,7 +85,6 @@ module Control.Exception.Base |
| 85 | 85 | patError,
|
| 86 | 86 | noMethodBindingError,
|
| 87 | 87 | typeError,
|
| 88 | - nonTermination,
|
|
| 89 | 88 | nestedAtomically,
|
| 90 | 89 | noMatchingContinuationPrompt
|
| 91 | 90 | ) where
|
| ... | ... | @@ -19,8 +19,8 @@ |
| 19 | 19 | --
|
| 20 | 20 | |
| 21 | 21 | module GHC.IO.Exception (
|
| 22 | - BlockedIndefinitelyOnMVar(..), blockedIndefinitelyOnMVar,
|
|
| 23 | - BlockedIndefinitelyOnSTM(..), blockedIndefinitelyOnSTM,
|
|
| 22 | + BlockedIndefinitelyOnMVar(..),
|
|
| 23 | + BlockedIndefinitelyOnSTM(..),
|
|
| 24 | 24 | Deadlock(..),
|
| 25 | 25 | AllocationLimitExceeded(..), allocationLimitExceeded,
|
| 26 | 26 | AssertionFailed(..),
|
| ... | ... | @@ -15,12 +15,12 @@ CLOSURE(GHCziInternalziWeakziFinalizze, runFinalizzerBatch_closure) |
| 15 | 15 | CLOSURE(GHCziInternalziIOziException, stackOverflow_closure)
|
| 16 | 16 | CLOSURE(GHCziInternalziIOziException, heapOverflow_closure)
|
| 17 | 17 | CLOSURE(GHCziInternalziIOziException, allocationLimitExceeded_closure)
|
| 18 | -CLOSURE(GHCziInternalziIOziException, blockedIndefinitelyOnMVar_closure)
|
|
| 19 | -CLOSURE(GHCziInternalziIOziException, blockedIndefinitelyOnSTM_closure)
|
|
| 18 | +CLOSURE(GHCziInternalziIOziException, blockedIndefinitelyOnMVarError_closure)
|
|
| 19 | +CLOSURE(GHCziInternalziIOziException, blockedIndefinitelyOnSTMError_closure)
|
|
| 20 | 20 | CLOSURE(GHCziInternalziIOziException, cannotCompactFunction_closure)
|
| 21 | 21 | CLOSURE(GHCziInternalziIOziException, cannotCompactPinned_closure)
|
| 22 | 22 | CLOSURE(GHCziInternalziIOziException, cannotCompactMutable_closure)
|
| 23 | -CLOSURE(GHCziInternalziControlziExceptionziBase, nonTermination_closure)
|
|
| 23 | +CLOSURE(GHCziInternalziControlziExceptionziBase, nonTerminationError_closure)
|
|
| 24 | 24 | CLOSURE(GHCziInternalziControlziExceptionziBase, nestedAtomically_closure)
|
| 25 | 25 | CLOSURE(GHCziInternalziControlziExceptionziBase, noMatchingContinuationPrompt_closure)
|
| 26 | 26 | #if defined(mingw32_HOST_OS)
|
| ... | ... | @@ -108,7 +108,7 @@ module GHC.Internal.Control.Exception.Base ( |
| 108 | 108 | impossibleError, impossibleConstraintError,
|
| 109 | 109 | nonExhaustiveGuardsError, patError, noMethodBindingError,
|
| 110 | 110 | typeError,
|
| 111 | - nonTermination, nestedAtomically, noMatchingContinuationPrompt,
|
|
| 111 | + nonTerminationError, nestedAtomically, noMatchingContinuationPrompt,
|
|
| 112 | 112 | ) where
|
| 113 | 113 | |
| 114 | 114 | import GHC.Internal.Base (
|
| ... | ... | @@ -448,8 +448,9 @@ impossibleConstraintError s = errorWithoutStackTrace (unpackCStringUtf8# s) |
| 448 | 448 | |
| 449 | 449 | |
| 450 | 450 | -- GHC's RTS calls this
|
| 451 | -nonTermination :: SomeException
|
|
| 452 | -nonTermination = toException NonTermination
|
|
| 451 | +nonTerminationError :: IO ()
|
|
| 452 | +nonTerminationError = throwIO NonTermination
|
|
| 453 | + |
|
| 453 | 454 | |
| 454 | 455 | -- GHC's RTS calls this
|
| 455 | 456 | nestedAtomically :: SomeException
|
| ... | ... | @@ -24,8 +24,8 @@ |
| 24 | 24 | -----------------------------------------------------------------------------
|
| 25 | 25 | |
| 26 | 26 | module GHC.Internal.IO.Exception (
|
| 27 | - BlockedIndefinitelyOnMVar(..), blockedIndefinitelyOnMVar,
|
|
| 28 | - BlockedIndefinitelyOnSTM(..), blockedIndefinitelyOnSTM,
|
|
| 27 | + BlockedIndefinitelyOnMVar(..), blockedIndefinitelyOnMVarError,
|
|
| 28 | + BlockedIndefinitelyOnSTM(..), blockedIndefinitelyOnSTMError,
|
|
| 29 | 29 | Deadlock(..),
|
| 30 | 30 | AllocationLimitExceeded(..), allocationLimitExceeded,
|
| 31 | 31 | AssertionFailed(..),
|
| ... | ... | @@ -84,8 +84,8 @@ instance Exception BlockedIndefinitelyOnMVar |
| 84 | 84 | instance Show BlockedIndefinitelyOnMVar where
|
| 85 | 85 | showsPrec _ BlockedIndefinitelyOnMVar = showString "thread blocked indefinitely in an MVar operation"
|
| 86 | 86 | |
| 87 | -blockedIndefinitelyOnMVar :: SomeException -- for the RTS
|
|
| 88 | -blockedIndefinitelyOnMVar = toException BlockedIndefinitelyOnMVar
|
|
| 87 | +blockedIndefinitelyOnMVarError :: IO () -- for the RTS
|
|
| 88 | +blockedIndefinitelyOnMVarError = throwIO BlockedIndefinitelyOnMVar
|
|
| 89 | 89 | |
| 90 | 90 | -----
|
| 91 | 91 | |
| ... | ... | @@ -100,8 +100,8 @@ instance Exception BlockedIndefinitelyOnSTM |
| 100 | 100 | instance Show BlockedIndefinitelyOnSTM where
|
| 101 | 101 | showsPrec _ BlockedIndefinitelyOnSTM = showString "thread blocked indefinitely in an STM transaction"
|
| 102 | 102 | |
| 103 | -blockedIndefinitelyOnSTM :: SomeException -- for the RTS
|
|
| 104 | -blockedIndefinitelyOnSTM = toException BlockedIndefinitelyOnSTM
|
|
| 103 | +blockedIndefinitelyOnSTMError :: IO () -- for the RTS
|
|
| 104 | +blockedIndefinitelyOnSTMError = throwIO BlockedIndefinitelyOnSTM
|
|
| 105 | 105 | |
| 106 | 106 | -----
|
| 107 | 107 |
| ... | ... | @@ -53,12 +53,12 @@ extern StgClosure ZCMain_main_closure; |
| 53 | 53 | #define stackOverflow_closure ghc_hs_iface->stackOverflow_closure
|
| 54 | 54 | #define heapOverflow_closure ghc_hs_iface->heapOverflow_closure
|
| 55 | 55 | #define allocationLimitExceeded_closure ghc_hs_iface->allocationLimitExceeded_closure
|
| 56 | -#define blockedIndefinitelyOnMVar_closure ghc_hs_iface->blockedIndefinitelyOnMVar_closure
|
|
| 57 | -#define blockedIndefinitelyOnSTM_closure ghc_hs_iface->blockedIndefinitelyOnSTM_closure
|
|
| 56 | +#define blockedIndefinitelyOnMVarError_closure ghc_hs_iface->blockedIndefinitelyOnMVarError_closure
|
|
| 57 | +#define blockedIndefinitelyOnSTMError_closure ghc_hs_iface->blockedIndefinitelyOnSTMError_closure
|
|
| 58 | 58 | #define cannotCompactFunction_closure ghc_hs_iface->cannotCompactFunction_closure
|
| 59 | 59 | #define cannotCompactPinned_closure ghc_hs_iface->cannotCompactPinned_closure
|
| 60 | 60 | #define cannotCompactMutable_closure ghc_hs_iface->cannotCompactMutable_closure
|
| 61 | -#define nonTermination_closure ghc_hs_iface->nonTermination_closure
|
|
| 61 | +#define nonTerminationError_closure ghc_hs_iface->nonTerminationError_closure
|
|
| 62 | 62 | #define nestedAtomically_closure ghc_hs_iface->nestedAtomically_closure
|
| 63 | 63 | #define absentSumFieldError_closure ghc_hs_iface->absentSumFieldError_closure
|
| 64 | 64 | #define underflowException_closure ghc_hs_iface->underflowException_closure
|
| ... | ... | @@ -87,6 +87,55 @@ suspendComputation (Capability *cap, StgTSO *tso, StgUpdateFrame *stop_here) |
| 87 | 87 | throwToSingleThreaded__ (cap, tso, NULL, false, stop_here);
|
| 88 | 88 | }
|
| 89 | 89 | |
| 90 | +/* -----------------------------------------------------------------------------
|
|
| 91 | + scheduleRaiseViaIO
|
|
| 92 | + |
|
| 93 | + Schedule `tso` to raise an exception by running `io_action`, an IO () that
|
|
| 94 | + performs `throwIO`. Unlike throwToSingleThreaded (which injects an exception
|
|
| 95 | + *value* via raiseAsync), the exception is raised by throwIO *within* the
|
|
| 96 | + thread, so it acquires a backtrace of the thread's stack. This is used by
|
|
| 97 | + resurrectThreads to deliver the "blocked indefinitely" exceptions
|
|
| 98 | + (BlockedIndefinitelyOnMVar, BlockedIndefinitelyOnSTM, NonTermination).
|
|
| 99 | + |
|
| 100 | + We push a "run this IO action" frame on top of the thread's existing
|
|
| 101 | + (suspended) stack and make it runnable; when the thread runs, throwIO raises
|
|
| 102 | + the exception and its own stack unwinding handles any CATCH_FRAME /
|
|
| 103 | + ATOMICALLY_FRAME (e.g. aborting a blocked STM transaction).
|
|
| 104 | + |
|
| 105 | + removeFromQueues takes care of unlinking the thread from any blocking queue
|
|
| 106 | + (notably the MVar blocked queue) and appends it to the run queue. As with
|
|
| 107 | + throwToSingleThreaded, the caller must own the TSO (e.g. hold all
|
|
| 108 | + capabilities during GC); in particular this relies on the thread not being
|
|
| 109 | + scheduled between removeFromQueues' enqueue and our stack push.
|
|
| 110 | + -------------------------------------------------------------------------- */
|
|
| 111 | + |
|
| 112 | +void
|
|
| 113 | +scheduleRaiseViaIO (Capability *cap, StgTSO *tso, StgClosure *io_action)
|
|
| 114 | +{
|
|
| 115 | + // Thread already dead?
|
|
| 116 | + if (tso->what_next == ThreadComplete || tso->what_next == ThreadKilled) {
|
|
| 117 | + return;
|
|
| 118 | + }
|
|
| 119 | + |
|
| 120 | + // Unlink from any blocking queues; sets why_blocked = NotBlocked and
|
|
| 121 | + // appends the thread to the run queue.
|
|
| 122 | + removeFromQueues(cap, tso);
|
|
| 123 | + |
|
| 124 | + StgStack *stack = tso->stackobj;
|
|
| 125 | + |
|
| 126 | + // We are about to mutate the stack, so dirty it for the GC write barrier
|
|
| 127 | + // (resurrectThreads runs right after GC).
|
|
| 128 | + dirty_TSO(cap, tso);
|
|
| 129 | + dirty_STACK(cap, stack);
|
|
| 130 | + |
|
| 131 | + // Push a frame that enters `io_action` and applies the resulting IO action
|
|
| 132 | + // to the void (RealWorld) argument: [stg_enter_info, io_action, stg_ap_v_info]
|
|
| 133 | + stack->sp -= 3;
|
|
| 134 | + stack->sp[0] = (W_)&stg_enter_info;
|
|
| 135 | + stack->sp[1] = (W_)io_action;
|
|
| 136 | + stack->sp[2] = (W_)&stg_ap_v_info;
|
|
| 137 | +}
|
|
| 138 | + |
|
| 90 | 139 | /* -----------------------------------------------------------------------------
|
| 91 | 140 | throwToSelf
|
| 92 | 141 |
| ... | ... | @@ -38,6 +38,10 @@ void suspendComputation (Capability *cap, |
| 38 | 38 | StgTSO *tso,
|
| 39 | 39 | StgUpdateFrame *stop_here);
|
| 40 | 40 | |
| 41 | +void scheduleRaiseViaIO (Capability *cap,
|
|
| 42 | + StgTSO *tso,
|
|
| 43 | + StgClosure *io_action);
|
|
| 44 | + |
|
| 41 | 45 | MessageThrowTo *throwTo (Capability *cap, // the Capability we hold
|
| 42 | 46 | StgTSO *source,
|
| 43 | 47 | StgTSO *target,
|
| ... | ... | @@ -192,9 +192,9 @@ static void initBuiltinGcRoots(void) |
| 192 | 192 | getStablePtr((StgPtr)stackOverflow_closure);
|
| 193 | 193 | getStablePtr((StgPtr)heapOverflow_closure);
|
| 194 | 194 | getStablePtr((StgPtr)unpackCString_closure);
|
| 195 | - getStablePtr((StgPtr)blockedIndefinitelyOnMVar_closure);
|
|
| 196 | - getStablePtr((StgPtr)nonTermination_closure);
|
|
| 197 | - getStablePtr((StgPtr)blockedIndefinitelyOnSTM_closure);
|
|
| 195 | + getStablePtr((StgPtr)blockedIndefinitelyOnMVarError_closure);
|
|
| 196 | + getStablePtr((StgPtr)nonTerminationError_closure);
|
|
| 197 | + getStablePtr((StgPtr)blockedIndefinitelyOnSTMError_closure);
|
|
| 198 | 198 | getStablePtr((StgPtr)allocationLimitExceeded_closure);
|
| 199 | 199 | getStablePtr((StgPtr)cannotCompactFunction_closure);
|
| 200 | 200 | getStablePtr((StgPtr)cannotCompactPinned_closure);
|
| ... | ... | @@ -3309,16 +3309,16 @@ resurrectThreads (StgTSO *threads) |
| 3309 | 3309 | case BlockedOnMVar:
|
| 3310 | 3310 | case BlockedOnMVarRead:
|
| 3311 | 3311 | /* Called by GC - sched_mutex lock is currently held. */
|
| 3312 | - throwToSingleThreaded(cap, tso,
|
|
| 3313 | - (StgClosure *)blockedIndefinitelyOnMVar_closure);
|
|
| 3312 | + scheduleRaiseViaIO(cap, tso,
|
|
| 3313 | + (StgClosure *)blockedIndefinitelyOnMVarError_closure);
|
|
| 3314 | 3314 | break;
|
| 3315 | 3315 | case BlockedOnBlackHole:
|
| 3316 | - throwToSingleThreaded(cap, tso,
|
|
| 3317 | - (StgClosure *)nonTermination_closure);
|
|
| 3316 | + scheduleRaiseViaIO(cap, tso,
|
|
| 3317 | + (StgClosure *)nonTerminationError_closure);
|
|
| 3318 | 3318 | break;
|
| 3319 | 3319 | case BlockedOnSTM:
|
| 3320 | - throwToSingleThreaded(cap, tso,
|
|
| 3321 | - (StgClosure *)blockedIndefinitelyOnSTM_closure);
|
|
| 3320 | + scheduleRaiseViaIO(cap, tso,
|
|
| 3321 | + (StgClosure *)blockedIndefinitelyOnSTMError_closure);
|
|
| 3322 | 3322 | break;
|
| 3323 | 3323 | case NotBlocked:
|
| 3324 | 3324 | /* This might happen if the thread was blocked on a black hole
|
| ... | ... | @@ -20,12 +20,12 @@ typedef struct { |
| 20 | 20 | StgClosure *stackOverflow_closure; // GHC.Internal.IO.Exception.stackOverflow_closure
|
| 21 | 21 | StgClosure *heapOverflow_closure; // GHC.Internal.IO.Exception.heapOverflow_closure
|
| 22 | 22 | StgClosure *allocationLimitExceeded_closure; // GHC.Internal.IO.Exception.allocationLimitExceeded_closure
|
| 23 | - StgClosure *blockedIndefinitelyOnMVar_closure; // GHC.Internal.IO.Exception.blockedIndefinitelyOnMVar_closure
|
|
| 24 | - StgClosure *blockedIndefinitelyOnSTM_closure; // GHC.Internal.IO.Exception.blockedIndefinitelyOnSTM_closure
|
|
| 23 | + StgClosure *blockedIndefinitelyOnMVarError_closure; // GHC.Internal.IO.Exception.blockedIndefinitelyOnMVarError_closure
|
|
| 24 | + StgClosure *blockedIndefinitelyOnSTMError_closure; // GHC.Internal.IO.Exception.blockedIndefinitelyOnSTMError_closure
|
|
| 25 | 25 | StgClosure *cannotCompactFunction_closure; // GHC.Internal.IO.Exception.cannotCompactFunction_closure
|
| 26 | 26 | StgClosure *cannotCompactPinned_closure; // GHC.Internal.IO.Exception.cannotCompactPinned_closure
|
| 27 | 27 | StgClosure *cannotCompactMutable_closure; // GHC.Internal.IO.Exception.cannotCompactMutable_closure
|
| 28 | - StgClosure *nonTermination_closure; // GHC.Internal.Control.Exception.Base.nonTermination_closure
|
|
| 28 | + StgClosure *nonTerminationError_closure; // GHC.Internal.Control.Exception.Base.nonTerminationError_closure
|
|
| 29 | 29 | StgClosure *nestedAtomically_closure; // GHC.Internal.Control.Exception.Base.nestedAtomically_closure
|
| 30 | 30 | StgClosure *noMatchingContinuationPrompt_closure; // GHC.Internal.Control.Exception.Base.noMatchingContinuationPrompt_closure
|
| 31 | 31 | StgClosure *blockedOnBadFD_closure; // GHC.Internal.Event.Thread.blockedOnBadFD_closure
|
| 1 | +{-# OPTIONS_GHC -finfo-table-map -forig-thunk-info #-}
|
|
| 2 | + |
|
| 3 | +import GHC.Exception.Backtrace.Experimental
|
|
| 4 | + |
|
| 5 | +x :: Integer
|
|
| 6 | +x = x + 1
|
|
| 7 | + |
|
| 8 | +testing :: IO ()
|
|
| 9 | +testing = do
|
|
| 10 | + putStrLn "hello"
|
|
| 11 | + print x
|
|
| 12 | + putStrLn "world"
|
|
| 13 | + |
|
| 14 | +main :: IO ()
|
|
| 15 | +main = do
|
|
| 16 | + setBacktraceMechanismState IPEBacktrace True
|
|
| 17 | + testing |
| 1 | +LoopBacktrace: Uncaught exception ghc-internal:GHC.Internal.Control.Exception.Base.NonTermination:
|
|
| 2 | + |
|
| 3 | +<<loop>>
|
|
| 4 | + |
|
| 5 | +IPE backtrace:
|
|
| 6 | + GHC.Internal.Exception.Backtrace.collectBacktraces' (libraries/ghc-internal/src/GHC/Internal/Exception/Backtrace.hs:(179,1)-(202,25))
|
|
| 7 | + GHC.Internal.Exception.Backtrace.collectBacktraces (libraries/ghc-internal/src/GHC/Internal/Exception/Backtrace.hs:174:39-56)
|
|
| 8 | + GHC.Internal.Exception.toExceptionWithBacktrace (libraries/ghc-internal/src/GHC/Internal/Exception.hs:(179,26)-(181,53))
|
|
| 9 | + GHC.Internal.IO.throwIO (libraries/ghc-internal/src/GHC/Internal/IO.hs:293:36)
|
|
| 10 | + Cmm$rts/HeapStackCheck.cmm. (:)
|
|
| 11 | + GHC.Internal.Bignum.Integer.integerAdd (libraries/ghc-internal/src/GHC/Internal/Bignum/Integer.hs:(547,1)-(571,52))
|
|
| 12 | + Cmm$rts/Updates.cmm. (:)
|
|
| 13 | + Main.x (LoopBacktrace.hs:6:1-9)
|
|
| 14 | + GHC.Internal.Show.show (libraries/ghc-internal/src/GHC/Internal/Show.hs:497:10-21)
|
|
| 15 | + GHC.Internal.IO.Handle.Text.hPutStr' (libraries/ghc-internal/src/GHC/Internal/IO/Handle/Text.hs:667:29-37)
|
|
| 16 | + GHC.Internal.Base.thenIO (libraries/ghc-internal/src/GHC/Internal/Base.hs:2337:1-72)
|
|
| 17 | + Cmm$rts/Exception.cmm. (:)
|
|
| 18 | + Cmm$rts/StgStartup.cmm. (:)
|
|
| 19 | +HasCallStack backtrace:
|
|
| 20 | + throwIO, called at libraries/ghc-internal/src/GHC/Internal/Control/Exception/Base.hs:452:23 in ghc-internal:GHC.Internal.Control.Exception.Base
|
|
| 21 | + |
| 1 | +hello |
| 1 | +{-# OPTIONS_GHC -finfo-table-map #-}
|
|
| 2 | + |
|
| 3 | +-- | Check that a @BlockedIndefinitelyOnMVar@ deadlock exception carries a
|
|
| 4 | +-- backtrace mentioning the blocking site in this module.
|
|
| 5 | +import Control.Concurrent.MVar
|
|
| 6 | +import GHC.Exception.Backtrace.Experimental
|
|
| 7 | + |
|
| 8 | +main :: IO ()
|
|
| 9 | +main = do
|
|
| 10 | + setBacktraceMechanismState IPEBacktrace True
|
|
| 11 | + mv <- newEmptyMVar :: IO (MVar ())
|
|
| 12 | + x <- takeMVar mv
|
|
| 13 | + print x |
| 1 | +MVarDeadlockBacktrace: Uncaught exception ghc-internal:GHC.Internal.IO.Exception.BlockedIndefinitelyOnMVar:
|
|
| 2 | + Main.main (MVarDeadlockBacktrace.hs:16:3-36) |
| 1 | +{-# OPTIONS_GHC -finfo-table-map #-}
|
|
| 2 | + |
|
| 3 | +-- | Check that a @BlockedIndefinitelyOnSTM@ deadlock exception carries a
|
|
| 4 | +-- backtrace mentioning the blocking site in this module.
|
|
| 5 | +import GHC.Conc (atomically, retry)
|
|
| 6 | +import GHC.Exception.Backtrace.Experimental
|
|
| 7 | + |
|
| 8 | +main :: IO ()
|
|
| 9 | +main = do
|
|
| 10 | + setBacktraceMechanismState IPEBacktrace True
|
|
| 11 | + x <- atomically retry :: IO ()
|
|
| 12 | + print x |
| 1 | +STMDeadlockBacktrace: Uncaught exception ghc-internal:GHC.Internal.IO.Exception.BlockedIndefinitelyOnSTM:
|
|
| 2 | + Main.main (STMDeadlockBacktrace.hs:16:3-32) |
| ... | ... | @@ -687,3 +687,11 @@ test('ClosureTable', |
| 687 | 687 | ['-debug -O0 ClosureTable_c.c -I{top}/../rts -I{top}/../rts/include'])
|
| 688 | 688 | |
| 689 | 689 | test('resizeMutableByteArrayInPlace', [req_cmm, extra_ways(['optasm', 'sanity']), only_ways(['optasm', 'sanity'])], compile_and_run, [''])
|
| 690 | + |
|
| 691 | +test('LoopBacktrace', [exit_code(1)], compile_and_run, [''])
|
|
| 692 | + |
|
| 693 | +deadlock_backtrace_norm = grep_errmsg(r'(Uncaught exception|Main\.)')
|
|
| 694 | +test('MVarDeadlockBacktrace', [exit_code(1), only_ways(['normal']), deadlock_backtrace_norm],
|
|
| 695 | + compile_and_run, ['-O'])
|
|
| 696 | +test('STMDeadlockBacktrace', [exit_code(1), only_ways(['normal']), deadlock_backtrace_norm],
|
|
| 697 | + compile_and_run, ['-O']) |