dejafu/dejafu-tests/Cases/MultiThreaded.hs

164 lines
4.1 KiB
Haskell
Raw Normal View History

2015-12-01 08:13:47 +03:00
{-# LANGUAGE CPP #-}
2015-11-16 05:48:54 +03:00
{-# LANGUAGE ImpredicativeTypes #-}
module Cases.MultiThreaded (tests) where
import Control.Monad (void)
import Test.DejaFu (Failure(..), gives, gives')
import Test.Framework (Test, testGroup)
import Test.Framework.Providers.HUnit (hUnitTestToTests)
import Test.HUnit (test)
2015-11-16 05:48:54 +03:00
import Test.HUnit.DejaFu (testDejafu)
import Control.Concurrent.Classy
2015-11-16 05:48:54 +03:00
import Control.Monad.STM.Class
2015-12-01 08:13:47 +03:00
#if __GLASGOW_HASKELL__ < 710
import Control.Applicative ((<$>), (<*>))
#endif
tests :: [Test]
tests =
[ testGroup "Threading" . hUnitTestToTests $ test
[ testDejafu threadId1 "child thread ID" $ gives' [True]
, testDejafu threadId2 "parent thread ID" $ gives' [True]
, testDejafu threadNoWait "no wait" $ gives' [Nothing, Just ()]
]
2016-03-23 06:36:07 +03:00
, testGroup "MVar" . hUnitTestToTests $ test
[ testDejafu cvarLock "deadlock" $ gives [Left Deadlock, Right 0]
, testDejafu cvarRace "race" $ gives' [0,1]
]
, testGroup "CRef" . hUnitTestToTests $ test
[ testDejafu crefRace "race" $ gives' [0,1]
]
, testGroup "STM" . hUnitTestToTests $ test
[ testDejafu stmAtomic "atomicity" $ gives' [0,2]
]
, testGroup "Killing Threads" . hUnitTestToTests $ test
[ testDejafu threadKill "no masking" $ gives [Left Deadlock, Right ()]
, testDejafu threadKillMask "masked" $ gives' [()]
, testDejafu threadKillUmask "unmasked" $ gives [Left Deadlock, Right ()]
]
2015-11-16 05:48:54 +03:00
]
--------------------------------------------------------------------------------
-- Threading
-- | Fork reports the good @ThreadId@.
threadId1 :: MonadConc m => m Bool
threadId1 = do
2016-03-23 06:36:07 +03:00
var <- newEmptyMVar
2015-11-16 05:48:54 +03:00
2016-03-23 06:36:07 +03:00
tid <- fork $ myThreadId >>= putMVar var
2015-11-16 05:48:54 +03:00
2016-03-23 06:36:07 +03:00
(tid ==) <$> readMVar var
2015-11-16 05:48:54 +03:00
-- | A child and parent thread have different @ThreadId@s.
threadId2 :: MonadConc m => m Bool
threadId2 = do
tid <- spawn myThreadId
2016-03-23 06:36:07 +03:00
(/=) <$> myThreadId <*> readMVar tid
2015-11-16 05:48:54 +03:00
-- | A parent thread doesn't wait for child threads before
-- terminating.
threadNoWait :: MonadConc m => m (Maybe ())
threadNoWait = do
x <- newCRef Nothing
void . fork . writeCRef x $ Just ()
readCRef x
--------------------------------------------------------------------------------
2016-03-23 06:36:07 +03:00
-- @MVar@s
2015-11-16 05:48:54 +03:00
-- | Deadlocks sometimes due to order of acquision of locks.
cvarLock :: MonadConc m => m Int
cvarLock = do
2016-03-23 06:36:07 +03:00
a <- newEmptyMVar
b <- newEmptyMVar
2015-11-16 05:48:54 +03:00
2016-03-23 06:36:07 +03:00
c <- newMVar 0
2015-11-16 05:48:54 +03:00
2016-03-23 06:36:07 +03:00
j1 <- spawn $ lock a >> lock b >> modifyMVar_ c (return . succ) >> unlock b >> unlock a
j2 <- spawn $ lock b >> lock a >> modifyMVar_ c (return . pred) >> unlock a >> unlock b
2015-11-16 05:48:54 +03:00
2016-03-23 06:36:07 +03:00
takeMVar j1
takeMVar j2
2015-11-16 05:48:54 +03:00
2016-03-23 06:36:07 +03:00
takeMVar c
2015-11-16 05:48:54 +03:00
2016-03-23 06:36:07 +03:00
-- | When racing two @putMVar@s, one of them will win.
2015-11-16 05:48:54 +03:00
cvarRace :: MonadConc m => m Int
cvarRace = do
2016-03-23 06:36:07 +03:00
x <- newEmptyMVar
2015-11-16 05:48:54 +03:00
2016-03-23 06:36:07 +03:00
void . fork $ putMVar x 0
void . fork $ putMVar x 1
2015-11-16 05:48:54 +03:00
2016-03-23 06:36:07 +03:00
readMVar x
2015-11-16 05:48:54 +03:00
--------------------------------------------------------------------------------
-- @CRef@s
--
-- TODO: Tests on CAS operations
-- | When racing two @writeCRef@s, one of them will win.
crefRace :: MonadConc m => m Int
crefRace = do
x <- newCRef (0::Int)
j1 <- spawn $ writeCRef x 0
j2 <- spawn $ writeCRef x 1
2016-03-23 06:36:07 +03:00
takeMVar j1
takeMVar j2
2015-11-16 05:48:54 +03:00
readCRef x
--------------------------------------------------------------------------------
-- STM
-- | Transactions are atomic.
stmAtomic :: MonadConc m => m Int
stmAtomic = do
2016-03-23 06:27:51 +03:00
x <- atomically $ newTVar (0::Int)
void . fork . atomically $ writeTVar x 1 >> writeTVar x 2
atomically $ readTVar x
2015-11-16 05:48:54 +03:00
--------------------------------------------------------------------------------
-- Exceptions
-- | Cause a deadlock sometimes by killing a thread.
threadKill :: MonadConc m => m ()
threadKill = do
2016-03-23 06:36:07 +03:00
x <- newEmptyMVar
tid <- fork $ putMVar x ()
2015-11-16 05:48:54 +03:00
killThread tid
2016-03-23 06:36:07 +03:00
readMVar x
2015-11-16 05:48:54 +03:00
-- | Never deadlock by masking a thread.
threadKillMask :: MonadConc m => m ()
threadKillMask = do
2016-03-23 06:36:07 +03:00
x <- newEmptyMVar
y <- newEmptyMVar
tid <- fork . mask . const $ putMVar x () >> putMVar y ()
readMVar x
2015-11-16 05:48:54 +03:00
killThread tid
2016-03-23 06:36:07 +03:00
readMVar y
2015-11-16 05:48:54 +03:00
-- | Sometimes deadlock by killing a thread.
threadKillUmask :: MonadConc m => m ()
threadKillUmask = do
2016-03-23 06:36:07 +03:00
x <- newEmptyMVar
y <- newEmptyMVar
tid <- fork . mask $ \umask -> putMVar x () >> umask (putMVar y ())
readMVar x
2015-11-16 05:48:54 +03:00
killThread tid
2016-03-23 06:36:07 +03:00
readMVar y