Skip to content

Commit f027501

Browse files
committed
add io-sim example
1 parent 96ed175 commit f027501

File tree

3 files changed

+168
-78
lines changed

3 files changed

+168
-78
lines changed

quickcheck-dynamic/quickcheck-dynamic.cabal

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,8 @@ library
9090
Test.QuickCheck.DynamicLogic.QuantifySpec
9191
Test.QuickCheck.StateModelSpec
9292
build-depends:
93+
, io-classes
94+
, io-sim
9395
, stm
9496
, tasty
9597
, tasty-hunit
@@ -118,6 +120,8 @@ test-suite quickcheck-dynamic-test
118120
build-depends:
119121
, base
120122
, containers
123+
, io-classes
124+
, io-sim
121125
, mtl
122126
, QuickCheck
123127
, quickcheck-dynamic

quickcheck-dynamic/test/Spec/DynamicLogic/Registry.hs

Lines changed: 65 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,49 +2,90 @@
22
-- process registry.
33
module Spec.DynamicLogic.Registry where
44

5-
import Control.Concurrent.STM
5+
import Control.Concurrent.Class.MonadSTM
66
import Control.Monad
7-
import GHC.Conc
7+
import Control.Monad.Class.MonadFork
8+
import Control.Monad.Class.MonadThrow
9+
import Control.Exception.Base (pattern ErrorCall)
10+
import Data.List qualified as List
811

9-
type Registry = TVar [(String, ThreadId)]
12+
data Reg m = Reg { registered :: [(String, ThreadId m)]
13+
, aliveThreads :: [ThreadId m]
14+
}
1015

11-
isAlive :: ThreadId -> IO Bool
12-
isAlive tid = do
13-
s <- threadStatus tid
14-
return $ s /= ThreadFinished && s /= ThreadDied
16+
emptyReg :: Reg m
17+
emptyReg = Reg [] []
1518

16-
setupRegistry :: IO Registry
17-
setupRegistry = atomically $ newTVar []
19+
modifyRegistered :: ([(String, ThreadId m)] -> [(String, ThreadId m)]) -> Reg m -> Reg m
20+
modifyRegistered f reg = reg{ registered = f (registered reg) }
1821

19-
whereis :: Registry -> String -> IO (Maybe ThreadId)
22+
modifyAlive :: ([ThreadId m] -> [ThreadId m]) -> Reg m -> Reg m
23+
modifyAlive f reg = reg{ aliveThreads = f (aliveThreads reg) }
24+
25+
newtype Registry m = Registry (TVar m (Reg m))
26+
27+
readReg :: MonadSTM m => Registry m -> STM m (Reg m)
28+
readReg (Registry r) = readTVar r
29+
30+
writeReg :: MonadSTM m => Registry m -> Reg m -> STM m ()
31+
writeReg (Registry r) = writeTVar r
32+
33+
modifyReg :: MonadSTM m => Registry m -> (Reg m -> Reg m) -> STM m ()
34+
modifyReg (Registry r) = modifyTVar r
35+
36+
type MonadRegistry m = (MonadSTM m, MonadFork m, MonadThrow m, MonadThrow (STM m))
37+
38+
isAlive :: MonadRegistry m => Registry m -> ThreadId m -> m Bool
39+
isAlive registry tid =
40+
elem tid . aliveThreads <$> atomically (readReg registry)
41+
42+
setupRegistry :: forall m. MonadRegistry m => m (Registry m)
43+
setupRegistry = atomically $ Registry <$> newTVar @m emptyReg
44+
45+
spawn :: MonadRegistry m => Registry m -> m () -> m (ThreadId m)
46+
spawn registry run = do
47+
sync <- atomically newEmptyTMVar
48+
let body = do
49+
self <- myThreadId
50+
atomically $ do
51+
modifyReg registry $ modifyAlive (self :)
52+
writeTMVar sync self
53+
run
54+
after _ = do
55+
self <- myThreadId
56+
atomically $ modifyReg registry $ modifyAlive $ List.delete self
57+
forkFinally body after
58+
atomically $ readTMVar sync
59+
60+
whereis :: MonadRegistry m => Registry m -> String -> m (Maybe (ThreadId m))
2061
whereis registry name = do
2162
reg <- readRegistry registry
2263
return $ lookup name reg
2364

24-
register :: Registry -> String -> ThreadId -> IO ()
65+
register :: MonadRegistry m => Registry m -> String -> ThreadId m -> m ()
2566
register registry name tid = do
26-
ok <- isAlive tid
67+
ok <- isAlive registry tid
2768
reg <- readRegistry registry
2869
if ok && name `notElem` map fst reg && tid `notElem` map snd reg
2970
then atomically $ do
30-
reg' <- readTVar registry
71+
reg' <- registered <$> readReg registry
3172
if name `notElem` map fst reg' && tid `notElem` map snd reg'
32-
then writeTVar registry ((name, tid) : reg')
33-
else error "badarg"
34-
else error "badarg"
73+
then modifyReg registry $ \ reg -> reg{ registered = (name, tid) : reg' }
74+
else throwIO (ErrorCall "badarg")
75+
else throwIO (ErrorCall "badarg")
3576

36-
unregister :: Registry -> String -> IO ()
77+
unregister :: MonadRegistry m => Registry m -> String -> m ()
3778
unregister registry name = do
3879
reg <- readRegistry registry
3980
when (name `elem` map fst reg) $ do
40-
atomically $ modifyTVar registry $ filter ((/= name) . fst)
81+
atomically $ modifyReg registry $ modifyRegistered $ filter ((/= name) . fst)
4182

42-
readRegistry :: Registry -> IO [(String, ThreadId)]
43-
readRegistry registry = garbageCollect registry *> atomically (readTVar registry)
83+
readRegistry :: MonadRegistry m => Registry m -> m [(String, ThreadId m)]
84+
readRegistry registry = garbageCollect registry *> atomically (registered <$> readReg registry)
4485

45-
garbageCollect :: Registry -> IO ()
86+
garbageCollect :: forall m. MonadRegistry m => Registry m -> m ()
4687
garbageCollect registry = do
47-
reg <- atomically $ readTVar registry
48-
garbage <- filterM (fmap not . isAlive) (map snd reg)
49-
atomically $ modifyTVar registry $ filter ((`notElem` garbage) . snd)
88+
reg <- registered <$> atomically (readReg @m registry)
89+
garbage <- filterM (fmap not . isAlive registry) (map snd reg)
90+
atomically $ modifyReg registry $ modifyRegistered $ filter ((`notElem` garbage) . snd)
5091
return ()

0 commit comments

Comments
 (0)