Eq instance for ThreadId too coarse
Two Eq
-equal ThreadId
s may refer to two different threads.
It seems that for Eq
and Show
purposes ThreadId
relies on a 32 bit counter that wraps around if forkIO
is called often enough. Yet, killThread
(fortunately) does not mix up two handles that look equal according to Eq
and Show
.
The behaviour is illustrated by the following program:
import Control.Concurrent
import Control.Monad
main :: IO ()
main = do
t0 <- myThreadId
(`mapM_` [0 :: Integer .. ]) $ \i -> do
t <- forkIO $ return ()
when (i `mod` 500000000 == 0) $ print t
when (t == t0) $ do
putStrLn $ "Killing " ++ show t ++ "..."
killThread t
threadDelay 1000000
putStrLn $ "Killing " ++ show t0 ++ "..."
killThread t0
$ ghc -O Bug.hs && ./Bug
[1 of 1] Compiling Main ( Bug.hs, Bug.o )
Linking Bug ...
ThreadId 2
ThreadId 500000002
ThreadId 1000000002
ThreadId 1500000002
ThreadId 2000000002
ThreadId -1794967294
ThreadId -1294967294
ThreadId -794967294
ThreadId -294967294
Killing ThreadId 1...
Killing ThreadId 1...
Bug: thread killed
$ echo $?
1
This reveals that t
and t0
refer to two different threads, although t == t0
: it seems that killThread t
kills the thread just forked, whereas killThread t0
kills the main thread.
If both t
and t0
referred to the thread just forked, the program would continue to run. If both referred to the main thread, the program would already terminate at the first invocation of killThread
.