Thread GC frees roots before thread actually finishes
In the following program, an IORef is garbage collected after a NonTermination
exception, but is subsequently accessed:
import Control.Exception as E
import Data.IORef
import System.Mem.Weak
main :: IO ()
main = do
ref <- newIORef 'x'
weak <- mkWeakIORef ref $ putStrLn "IORef finalized"
let check = deRefWeak weak >>= \m -> case m of
Nothing -> putStrLn "IORef was GCed"
Just ref' -> do
x <- readIORef ref'
putStrLn $ "IORef still alive, and contains " ++ show x
let loop = loop
check
loop `catch` \ex -> do
putStrLn $ "caught exception: " ++ show (ex :: SomeException)
check
readIORef ref >>= print
Output:
IORef still alive, and contains 'x'
IORef finalized
caught exception: <<loop>>
IORef was GCed
'x'
The same happens with other thread deadlocks, such as:
- newEmptyMVar >>= takeMVar
- atomically retry
It does not happen when a StackOverflow
or UserInterrupt
exception is caught.
This also affects ForeignPtr
; see the attached "database" example. This is what really triggered #7170 (closed). I marked this "Runtime crash" because it can lead to a ForeignPtr
being accessed after the garbage collector finalized it.
Trac metadata
Trac field | Value |
---|---|
Version | 7.6.3 |
Type | Bug |
TypeOfFailure | OtherFailure |
Priority | normal |
Resolution | Unresolved |
Component | Runtime System |
Test case | |
Differential revisions | |
BlockedBy | |
Related | |
Blocking | |
CC | |
Operating system | |
Architecture |