Duncan Coutts pushed to branch wip/io-manager-deadlock-detection at Glasgow Haskell Compiler / GHC
Commits:
-
ba7a6066
by Duncan Coutts at 2026-09-28T09:52:17+01:00
-
80e2ceee
by Duncan Coutts at 2026-09-28T09:52:17+01:00
11 changed files:
- + changelog.d/idle-gc-and-deadlock-detection
- docs/users_guide/profiling.rst
- docs/users_guide/runtime_control.rst
- testsuite/tests/rts/Makefile
- + testsuite/tests/rts/T26408a.hs
- + testsuite/tests/rts/T26408a.stderr
- + testsuite/tests/rts/T26408b.hs
- + testsuite/tests/rts/T26408b.stderr
- + testsuite/tests/rts/T26408c.hs
- + testsuite/tests/rts/T26408c.stdout
- testsuite/tests/rts/all.T
Changes:
| 1 | +section: rts
|
|
| 2 | +issues: #27040 #26408
|
|
| 3 | +mrs: !14820
|
|
| 4 | +synopsis: Idle GC and deadlock detection in the non-threaded RTS now behave the same as in the threaded RTS
|
|
| 5 | +description: {
|
|
| 6 | +Idle GC now works in the non-threaded RTS and is enabled by default.
|
|
| 7 | + |
|
| 8 | +Deadlock detection in the non-threaded RTS now follows the same design and
|
|
| 9 | +behaviour as in the threaded RTS. In particular, it can detect partial
|
|
| 10 | +deadlocks where some but not all threads are deadlocked. The detection is no
|
|
| 11 | +longer prompt however, as the detection runs as part of idle GC, which (by
|
|
| 12 | +default) is performed 0.3 seconds after all capabilities become idle.
|
|
| 13 | + |
|
| 14 | +The user guide now documents that deadlock detection relies on idle GC,
|
|
| 15 | +and thus turning off idle GC disables deadlock detection. This is true
|
|
| 16 | +of the threaded RTS in previous GHC releases but it was not previously
|
|
| 17 | +documented behaviour.
|
|
| 18 | + |
|
| 19 | +Internally, the idle GC and deadlock detection now follow the same design and
|
|
| 20 | +use 99% the same code between the non-threaded and threaded RTS. Having a
|
|
| 21 | +single design improves the comprehension and maintainability of the RTS code.
|
|
| 22 | +} |
| ... | ... | @@ -691,11 +691,13 @@ enclosed between ``+RTS ... -RTS`` as usual): |
| 691 | 691 | order to increase the resolution of the time profiler.
|
| 692 | 692 | |
| 693 | 693 | Using a value of zero disables the RTS clock completely, and has the
|
| 694 | - effect of disabling timers that depend on it: the context switch
|
|
| 695 | - timer and the heap profiling timer. Context switches will still
|
|
| 696 | - happen, but deterministically and at a rate much faster than normal.
|
|
| 697 | - Disabling the interval timer is useful for debugging, because it
|
|
| 698 | - eliminates a source of non-determinism at runtime.
|
|
| 694 | + effect of disabling the timers and features that depend on it: the context
|
|
| 695 | + switch timer, the heap profiling timer, the eventlog flush timer, and the
|
|
| 696 | + idle GC timer. Context switches will still happen, but deterministically
|
|
| 697 | + and at a rate much faster than normal. Disabling the interval timer is
|
|
| 698 | + useful for debugging, because it eliminates a source of non-determinism at
|
|
| 699 | + runtime. Note however that disabling the idle GC timer not only disables
|
|
| 700 | + idle GC but also deadlock detection (see rts-flag:`-I`).
|
|
| 699 | 701 | |
| 700 | 702 | |
| 701 | 703 | .. rts-flag:: -xc
|
| ... | ... | @@ -731,61 +731,6 @@ performance. |
| 731 | 731 | the default small ``-A`` value is suboptimal, as it can be in
|
| 732 | 732 | programs that create large amounts of long-lived data.
|
| 733 | 733 | |
| 734 | -.. rts-flag:: -I ⟨seconds⟩
|
|
| 735 | - |
|
| 736 | - :default: 0.3 seconds in the threaded runtime, 0 in the non-threaded runtime
|
|
| 737 | - |
|
| 738 | - .. index::
|
|
| 739 | - single: idle GC
|
|
| 740 | - |
|
| 741 | - Set the amount of idle time which must pass before a idle GC is
|
|
| 742 | - performed. Setting ``-I0`` disables the idle GC.
|
|
| 743 | - |
|
| 744 | - In the threaded and SMP versions of the RTS (see :ghc-flag:`-threaded`,
|
|
| 745 | - :ref:`options-linker`), a major GC is automatically performed if the
|
|
| 746 | - runtime has been idle (no Haskell computation has been running) for a
|
|
| 747 | - period of time.
|
|
| 748 | - |
|
| 749 | - For an interactive application, it is probably a good idea to use
|
|
| 750 | - the idle GC, because this will allow finalizers to run and
|
|
| 751 | - deadlocked threads to be detected in the idle time when no Haskell
|
|
| 752 | - computation is happening. Also, it will mean that a GC is less
|
|
| 753 | - likely to happen when the application is busy, and so responsiveness
|
|
| 754 | - may be improved. However, if the amount of live data in the heap is
|
|
| 755 | - particularly large, then the idle GC can cause a significant delay,
|
|
| 756 | - and too small an interval could adversely affect interactive
|
|
| 757 | - responsiveness.
|
|
| 758 | - |
|
| 759 | - The idle period timer only resets after some activity
|
|
| 760 | - by a Haskell thread. If your program is doing literally nothing then
|
|
| 761 | - after the first idle collection is triggered then no more future collections
|
|
| 762 | - will be scheduled until more work is performed.
|
|
| 763 | - |
|
| 764 | - This is an experimental feature, please let us know if it causes
|
|
| 765 | - problems and/or could benefit from further tuning.
|
|
| 766 | - |
|
| 767 | -.. rts-flag:: -Iw ⟨seconds⟩
|
|
| 768 | - |
|
| 769 | - :default: 0 seconds
|
|
| 770 | - |
|
| 771 | - .. index::
|
|
| 772 | - single: idle GC
|
|
| 773 | - |
|
| 774 | - Set the minimum wait time between runs of the idle GC.
|
|
| 775 | - |
|
| 776 | - By default, if idle GC is enabled in the threaded runtime, a major
|
|
| 777 | - GC will be performed every time the process goes idle for a
|
|
| 778 | - sufficiently long duration (see :rts-flag:`-I ⟨seconds⟩`). For
|
|
| 779 | - large server processes accepting regular but infrequent requests
|
|
| 780 | - (e.g., once per second), an expensive, major GC may run after
|
|
| 781 | - every request. As an alternative to shutting off idle GC entirely
|
|
| 782 | - (with ``-I0``), a minimum wait time between idle GCs can be
|
|
| 783 | - specified with this flag. For example, ``-Iw60`` will ensure that
|
|
| 784 | - an idle GC runs at most once per minute.
|
|
| 785 | - |
|
| 786 | - This is an experimental feature, please let us know if it causes
|
|
| 787 | - problems and/or could benefit from further tuning.
|
|
| 788 | - |
|
| 789 | 734 | .. rts-flag:: -ki ⟨size⟩
|
| 790 | 735 | |
| 791 | 736 | :default: 1k
|
| ... | ... | @@ -1011,6 +956,116 @@ performance. |
| 1011 | 956 | ``GHC.Stats.getRTSStats`` from Haskell.
|
| 1012 | 957 | |
| 1013 | 958 | |
| 959 | +Idle GC and deadlock detection
|
|
| 960 | +------------------------------
|
|
| 961 | + |
|
| 962 | +The RTS performs idle GC and deadlock detection. These features are more
|
|
| 963 | +closely related and dependent than one might expect. In particular, deadlock
|
|
| 964 | +detection relies on idle GC.
|
|
| 965 | + |
|
| 966 | +The primary purpose of idle GC is to reclaim memory and return it to the system
|
|
| 967 | +when the application has been idle for a short time. It also gives an
|
|
| 968 | +opportunity for finalisers to run more promptly. See :rts-flag:`-I ⟨seconds⟩`.
|
|
| 969 | + |
|
| 970 | +Deadlock detection is a debugging feature to try to discover when some or all
|
|
| 971 | +Haskell threads are deadlocked. For the purpose of this explanation we define:
|
|
| 972 | + |
|
| 973 | + * a "partial deadlock" to be a set of threads that are deadlocked; and
|
|
| 974 | + * a "system deadlock" is when all threads are deadlocked.
|
|
| 975 | + |
|
| 976 | +The deadlock detection is implemented by the GC, and it is capable of detecting
|
|
| 977 | +partial deadlocks. If a deadlock is discovered, the RTS will throw an
|
|
| 978 | +asynchronous exception to (some or all of) the blocked threads to enable
|
|
| 979 | +progress and enable better reporting of the problem. In particular it throws
|
|
| 980 | +the async exceptions ``BlockedIndefinitelyOnMVar`` or
|
|
| 981 | +``BlockedIndefinitelyOnSTM``, as appropriate.
|
|
| 982 | + |
|
| 983 | +The deadlock detection can be run during a major GC, but it is more expensive
|
|
| 984 | +than a normal major GC so it is not run during normal major GCs. The deadlock
|
|
| 985 | +detection mode of GC is run by the idle GC. This is on the basis that when the
|
|
| 986 | +application is idle is when we are most likely to find a deadlock, and also
|
|
| 987 | +that the extra time cost is unlikely to be a problem because the application is
|
|
| 988 | +already idle.
|
|
| 989 | + |
|
| 990 | +There are a few consequence of this:
|
|
| 991 | + |
|
| 992 | + * Deadlock detection relies on idle GC and will not run if idle GC is disabled
|
|
| 993 | + with ``-I0``.
|
|
| 994 | + * Deadlock detection will not run if the application remains busy, and thus
|
|
| 995 | + idle GC is not invoked.
|
|
| 996 | + * Deadlock detection is not prompt: it will only occur after the idle GC
|
|
| 997 | + delay, depending on both :rts-flag:`-I ⟨seconds⟩` and
|
|
| 998 | + :rts-flag:`-Iw ⟨seconds⟩`.
|
|
| 999 | + |
|
| 1000 | +In the non-threaded RTS there is additional behaviour: system deadlocks will be
|
|
| 1001 | +detected promptly, and this works even if idle GC is otherwise disabled.
|
|
| 1002 | + |
|
| 1003 | +One cannot rely on deadlock detection as part of the normal program behaviour.
|
|
| 1004 | +It should only be used for debugging. Firstly, deadlock detection can be
|
|
| 1005 | +disabled or indefinately deferred due to activity. Secondly, the set of threads
|
|
| 1006 | +that deadlock detection throws exceptions to is not guaranteed to be minimal:
|
|
| 1007 | +a thread waiting on a deadlocked thread may also have an exception thrown to it
|
|
| 1008 | +even if it could make progress after the deadlocked thread is terminated.
|
|
| 1009 | + |
|
| 1010 | +Deadlock detection can be invoked manually using ``performDeadlockDetection``
|
|
| 1011 | +from ``System.Mem.Experimental``.
|
|
| 1012 | + |
|
| 1013 | + |
|
| 1014 | +.. rts-flag:: -I ⟨seconds⟩
|
|
| 1015 | + |
|
| 1016 | + :default: 0.3 seconds
|
|
| 1017 | + |
|
| 1018 | + .. index::
|
|
| 1019 | + single: idle GC
|
|
| 1020 | + |
|
| 1021 | + A major GC is automatically performed if the runtime has been idle (no
|
|
| 1022 | + Haskell computation has been running) for a period of time. Set the amount
|
|
| 1023 | + of idle time which must pass before a idle GC is performed.
|
|
| 1024 | + |
|
| 1025 | + Setting ``-I0`` disables the idle GC. This also has the unfortunate side
|
|
| 1026 | + effect of disabling thread deadlock detection (the implementation of which
|
|
| 1027 | + uses the idle GC).
|
|
| 1028 | + |
|
| 1029 | + For an interactive application, it is probably a good idea to use
|
|
| 1030 | + the idle GC, because this will allow finalizers to run and
|
|
| 1031 | + deadlocked threads to be detected in the idle time when no Haskell
|
|
| 1032 | + computation is happening. Also, it will mean that a GC is less
|
|
| 1033 | + likely to happen when the application is busy, and so responsiveness
|
|
| 1034 | + may be improved. However, if the amount of live data in the heap is
|
|
| 1035 | + particularly large, then the idle GC can cause a significant delay,
|
|
| 1036 | + and too small an interval could adversely affect interactive
|
|
| 1037 | + responsiveness.
|
|
| 1038 | + |
|
| 1039 | + The idle period timer only resets after some activity
|
|
| 1040 | + by a Haskell thread. If your program is doing literally nothing then
|
|
| 1041 | + after the first idle collection is triggered then no more future collections
|
|
| 1042 | + will be scheduled until more work is performed.
|
|
| 1043 | + |
|
| 1044 | + Please let us know if it causes problems and/or could benefit from further
|
|
| 1045 | + tuning.
|
|
| 1046 | + |
|
| 1047 | +.. rts-flag:: -Iw ⟨seconds⟩
|
|
| 1048 | + |
|
| 1049 | + :default: 0 seconds
|
|
| 1050 | + |
|
| 1051 | + .. index::
|
|
| 1052 | + single: idle GC
|
|
| 1053 | + |
|
| 1054 | + Set the minimum wait time between runs of the idle GC.
|
|
| 1055 | + |
|
| 1056 | + By default (and if idle GC is not disabled) a major
|
|
| 1057 | + GC will be performed every time the process goes idle for a
|
|
| 1058 | + sufficiently long duration (see :rts-flag:`-I ⟨seconds⟩`). For
|
|
| 1059 | + large server processes accepting regular but infrequent requests
|
|
| 1060 | + (e.g., once per second), an expensive, major GC may run after
|
|
| 1061 | + every request. As an alternative to shutting off idle GC entirely
|
|
| 1062 | + (with ``-I0``), a minimum wait time between idle GCs can be
|
|
| 1063 | + specified with this flag. For example, ``-Iw60`` will ensure that
|
|
| 1064 | + an idle GC runs at most once per minute.
|
|
| 1065 | + |
|
| 1066 | + This is an experimental feature, please let us know if it causes
|
|
| 1067 | + problems and/or could benefit from further tuning.
|
|
| 1068 | + |
|
| 1014 | 1069 | |
| 1015 | 1070 | .. _rts-options-statistics:
|
| 1016 | 1071 |
| ... | ... | @@ -157,3 +157,11 @@ HSC2HS_OPTS = --cc="$(TEST_CC)" $(addprefix --cflag=,$(TEST_CC_OPTS)) --ld=$(TES |
| 157 | 157 | |
| 158 | 158 | IOManager.hs: IOManager.hsc
|
| 159 | 159 | '$(HSC2HS)' $(HSC2HS_OPTS) $<
|
| 160 | + |
|
| 161 | +.PHONY: T26408c
|
|
| 162 | +T26408c:
|
|
| 163 | + "$(TEST_HC)" $(TEST_HC_OPTS) -rtsopts T26408c.hs; \
|
|
| 164 | + ./T26408c +RTS -I0 -T & \
|
|
| 165 | + sleep 1; \
|
|
| 166 | + kill -s USR1 $$!; \
|
|
| 167 | + wait $$! |
| 1 | +import Control.Concurrent
|
|
| 2 | +import Control.Concurrent.STM
|
|
| 3 | +import Control.Exception
|
|
| 4 | +import Control.Monad
|
|
| 5 | + |
|
| 6 | +-- | Test to make sure that we can detect a /partial deadlock/ that is /not/
|
|
| 7 | +-- also a /system deadlock/: that is we can detect a set of deadlocked threads
|
|
| 8 | +-- even when there are other unrelated threads that are waiting on I\/O or
|
|
| 9 | +-- timeouts.
|
|
| 10 | +--
|
|
| 11 | +-- Historically this did not work in the non-threaded RTS which would only do
|
|
| 12 | +-- deadlock detection if there was a system deadlock: no runnable threads /and/
|
|
| 13 | +-- no pending I\/O or timers.
|
|
| 14 | +--
|
|
| 15 | +-- See Note [Deadlock detection]
|
|
| 16 | +--
|
|
| 17 | +main :: IO ()
|
|
| 18 | +main = do
|
|
| 19 | + |
|
| 20 | + -- Set up two threads that are deadlocked on each other
|
|
| 21 | + aDone <- newTVarIO False
|
|
| 22 | + bDone <- newTVarIO False
|
|
| 23 | + let blockingThread theirDone ourDone =
|
|
| 24 | + atomically $ do
|
|
| 25 | + done <- readTVar theirDone
|
|
| 26 | + guard done
|
|
| 27 | + writeTVar ourDone True
|
|
| 28 | + _ <- forkIO (blockingThread bDone aDone)
|
|
| 29 | + _ <- forkIO (blockingThread aDone bDone)
|
|
| 30 | + |
|
| 31 | + -- Set up another thread that is blocked on a long timeout.
|
|
| 32 | + --
|
|
| 33 | + -- We use a timeout rather than I/O as it's more portable, whereas I/O waits
|
|
| 34 | + -- are different between posix and windows I/O managers.
|
|
| 35 | + --
|
|
| 36 | + -- One gotcha is that when the timeout completes then the deadlock will be
|
|
| 37 | + -- detected again (since the bug is about I/O or timeouts masking deadlock
|
|
| 38 | + -- detection). So for a reliable test the timeout used here must be longer
|
|
| 39 | + -- than the test framework's own timeout. So we use maxBound, and we adjust
|
|
| 40 | + -- the test framework's timeout to be short (see run_timeout_multiplier).
|
|
| 41 | + _ <- forkIO (threadDelay maxBound)
|
|
| 42 | + |
|
| 43 | + -- Wait on the two deadlocked threads to terminate. This is now a partial
|
|
| 44 | + -- deadlock but not a system deadlock (the main thread and the first two
|
|
| 45 | + -- threads are deadlocked, but the one waiting on the timeout is ok).
|
|
| 46 | + --
|
|
| 47 | + -- We now expect that the threads that are deadlocked are detected as such
|
|
| 48 | + -- and an exception is thrown to some or all of them.
|
|
| 49 | + --
|
|
| 50 | + -- Note that if this fails, the test itself will effectively deadlock and
|
|
| 51 | + -- will rely on the test framework's timeout.
|
|
| 52 | + atomically $ do
|
|
| 53 | + status <- mapM readTVar [aDone, bDone]
|
|
| 54 | + guard (or status) |
| 1 | +T26408a: Uncaught exception ghc-internal:GHC.Internal.IO.Exception.BlockedIndefinitelyOnSTM:
|
|
| 2 | + |
|
| 3 | +thread blocked indefinitely in an STM transaction |
| 1 | +import Control.Concurrent
|
|
| 2 | +import Control.Concurrent.STM
|
|
| 3 | +import Control.Exception
|
|
| 4 | +import Control.Monad
|
|
| 5 | + |
|
| 6 | +-- | Test to make sure that we can detect a /system deadlock/ promptly which
|
|
| 7 | +-- means without relying on idle GC. To check this we have to turn off idle GC,
|
|
| 8 | +-- (+RTS -I0) which is what normally triggers deadlock detection.
|
|
| 9 | +--
|
|
| 10 | +-- This is only expected to work in the non-threaded RTS. In principle this
|
|
| 11 | +-- test could also work with the threaded RTS when using only a single
|
|
| 12 | +-- capability, if the I\/O managers can support it. Currently that is not the
|
|
| 13 | +-- case.
|
|
| 14 | +--
|
|
| 15 | +-- See Note [Deadlock detection]
|
|
| 16 | +--
|
|
| 17 | +main :: IO ()
|
|
| 18 | +main = do
|
|
| 19 | + |
|
| 20 | + -- Set up two threads that are deadlocked on each other
|
|
| 21 | + aDone <- newTVarIO False
|
|
| 22 | + bDone <- newTVarIO False
|
|
| 23 | + let blockingThread theirDone ourDone =
|
|
| 24 | + atomically $ do
|
|
| 25 | + done <- readTVar theirDone
|
|
| 26 | + guard done
|
|
| 27 | + writeTVar ourDone True
|
|
| 28 | + _ <- forkIO (blockingThread bDone aDone)
|
|
| 29 | + _ <- forkIO (blockingThread aDone bDone)
|
|
| 30 | + |
|
| 31 | + -- Wait on the two deadlocked threads to terminate. This is now a system
|
|
| 32 | + -- deadlock, involving all three threads (the main thread and the other two).
|
|
| 33 | + --
|
|
| 34 | + -- We now expect that the threads that are deadlocked are detected as such
|
|
| 35 | + -- and an exception is thrown to some or all of them.
|
|
| 36 | + --
|
|
| 37 | + -- Note that if this fails, the test itself will effectively deadlock and
|
|
| 38 | + -- will rely on the test framework's timeout.
|
|
| 39 | + atomically $ do
|
|
| 40 | + status <- mapM readTVar [aDone, bDone]
|
|
| 41 | + guard (or status) |
| 1 | +T26408b: Uncaught exception ghc-internal:GHC.Internal.IO.Exception.BlockedIndefinitelyOnSTM:
|
|
| 2 | + |
|
| 3 | +thread blocked indefinitely in an STM transaction |
| 1 | +import Control.Concurrent
|
|
| 2 | +import Control.Concurrent.STM
|
|
| 3 | +import Control.Exception
|
|
| 4 | +import Control.Monad
|
|
| 5 | + |
|
| 6 | +import System.Posix.Signals
|
|
| 7 | +import GHC.Stats.Experimental
|
|
| 8 | + |
|
| 9 | +-- | Test to make sure that we do not falsely detect a deadlock when there is
|
|
| 10 | +-- nothing to do except wait on signals. This is to make sure that our prompt
|
|
| 11 | +-- system deadlock detection is not too eager.
|
|
| 12 | +--
|
|
| 13 | +-- We arrange a partial deadlock that can be broken if a signal arrives and
|
|
| 14 | +-- the signal handler can unblock things.
|
|
| 15 | +--
|
|
| 16 | +-- This should /not/ result in any deadlock being detected, and no other odd
|
|
| 17 | +-- behaviour, like getting caught in a loop and doing too many idle GCs. So we
|
|
| 18 | +-- count and report the number of major GCs.
|
|
| 19 | +--
|
|
| 20 | +main :: IO ()
|
|
| 21 | +main = do
|
|
| 22 | + |
|
| 23 | + -- Set up two threads that are deadlocked on each other
|
|
| 24 | + aDone <- newTVarIO False
|
|
| 25 | + bDone <- newTVarIO False
|
|
| 26 | + let blockingThread theirDone ourDone =
|
|
| 27 | + atomically $ do
|
|
| 28 | + done <- readTVar theirDone
|
|
| 29 | + guard done
|
|
| 30 | + writeTVar ourDone True
|
|
| 31 | + |
|
| 32 | + installHandler userDefinedSignal1
|
|
| 33 | + (Catch $ do
|
|
| 34 | + atomically $ writeTVar aDone True
|
|
| 35 | + >> writeTVar bDone True
|
|
| 36 | + putStrLn "caught SIGUSR1")
|
|
| 37 | + Nothing
|
|
| 38 | + |
|
| 39 | + _ <- forkIO (blockingThread bDone aDone)
|
|
| 40 | + _ <- forkIO (blockingThread aDone bDone)
|
|
| 41 | + yield
|
|
| 42 | + |
|
| 43 | + -- Wait on the deadlocked threads to terminate. We now expect that the threads
|
|
| 44 | + -- that are deadlocked are detected as such and an exception is raised.
|
|
| 45 | + -- Note that if this fails, the test itself will effectively deadlock and
|
|
| 46 | + -- will rely on the test framework's timeout.
|
|
| 47 | + putStrLn "waiting on deadlocked threads..."
|
|
| 48 | + atomically $ do
|
|
| 49 | + status <- mapM readTVar [aDone, bDone]
|
|
| 50 | + guard (or status)
|
|
| 51 | + |
|
| 52 | + -- We expect exactly 1 major GC: the one triggered for deadlock detection
|
|
| 53 | + -- where we find no deadlock. We should then sleep without doing more GC.
|
|
| 54 | + -- An easy mistake would be looping: detecting probable deadlock, do idle GC.
|
|
| 55 | + -- See notifyIdleGcDeadlock().
|
|
| 56 | + stats <- getRTSStats
|
|
| 57 | + putStrLn ("terminated normally, major GCs = " ++ show (major_gcs stats))
|
|
| 58 | + -- This test _could_ be sensitive to changes in default heap sizes etc. If
|
|
| 59 | + -- this number ends up as 2 for example, then adjust the RTS opts (in the
|
|
| 60 | + -- makefile) so that the heap is big enough that normally no major GC would
|
|
| 61 | + -- be needed. |
| 1 | +[1 of 2] Compiling Main ( T26408c.hs, T26408c.o )
|
|
| 2 | +[2 of 2] Linking T26408c
|
|
| 3 | +waiting on deadlocked threads...
|
|
| 4 | +caught SIGUSR1
|
|
| 5 | +terminated normally, major GCs = 1 |
| ... | ... | @@ -682,6 +682,24 @@ test('T22859', |
| 682 | 682 | omit_ways(llvm_ways + ['nonmoving_thr', 'nonmoving_thr_sanity'])],
|
| 683 | 683 | compile_and_run, ['-with-rtsopts -A8K'])
|
| 684 | 684 | |
| 685 | +# Deadlock detection testsL
|
|
| 686 | +# A: check we can detect a partial deadlock that is not a system deadlock
|
|
| 687 | +test('T26408a', [exit_code(1), run_timeout_multiplier(0.1)], compile_and_run, [''])
|
|
| 688 | +# B: check we can detect system deadlock with idle GC turned off (+RTS -I0)
|
|
| 689 | +test('T26408b', [omit_ways(threaded_ways), extra_run_opts('+RTS -I0 -RTS'),
|
|
| 690 | + exit_code(1), run_timeout_multiplier(0.1)],
|
|
| 691 | + compile_and_run, ['-rtsopts'])
|
|
| 692 | +# C: check we do not falsely detect deadlock when blocked only on signals
|
|
| 693 | +# Skip for win32, wasm and js since this test requires posix signals
|
|
| 694 | +test('T26408c', [when(opsys('mingw32'), skip),
|
|
| 695 | + when(arch('wasm32'), skip), js_skip,
|
|
| 696 | + run_timeout_multiplier(0.1)
|
|
| 697 | + ], makefile_test, ['T26408c'])
|
|
| 698 | +# TODO: change omit_ways(threaded_ways) above
|
|
| 699 | +# to all_io_managers_except['mio', 'winio']
|
|
| 700 | +# and test +RTS -N1 with threaded ways, but will be broken for now.
|
|
| 701 | + |
|
| 702 | + |
|
| 685 | 703 | # These tests need access to the internal RTS headers.
|
| 686 | 704 | # TODO: there is probably some cleaner way to do this, and it should probably
|
| 687 | 705 | # be guarded for in-tree tests, since it cannot work against an arbitrary
|