|
2 | 2 | -- process registry. |
3 | 3 | module Spec.DynamicLogic.Registry where |
4 | 4 |
|
5 | | -import Control.Concurrent.STM |
| 5 | +import Control.Concurrent.Class.MonadSTM |
6 | 6 | 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 |
8 | 11 |
|
9 | | -type Registry = TVar [(String, ThreadId)] |
| 12 | +data Reg m = Reg { registered :: [(String, ThreadId m)] |
| 13 | + , aliveThreads :: [ThreadId m] |
| 14 | + } |
10 | 15 |
|
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 [] [] |
15 | 18 |
|
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) } |
18 | 21 |
|
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)) |
20 | 61 | whereis registry name = do |
21 | 62 | reg <- readRegistry registry |
22 | 63 | return $ lookup name reg |
23 | 64 |
|
24 | | -register :: Registry -> String -> ThreadId -> IO () |
| 65 | +register :: MonadRegistry m => Registry m -> String -> ThreadId m -> m () |
25 | 66 | register registry name tid = do |
26 | | - ok <- isAlive tid |
| 67 | + ok <- isAlive registry tid |
27 | 68 | reg <- readRegistry registry |
28 | 69 | if ok && name `notElem` map fst reg && tid `notElem` map snd reg |
29 | 70 | then atomically $ do |
30 | | - reg' <- readTVar registry |
| 71 | + reg' <- registered <$> readReg registry |
31 | 72 | 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") |
35 | 76 |
|
36 | | -unregister :: Registry -> String -> IO () |
| 77 | +unregister :: MonadRegistry m => Registry m -> String -> m () |
37 | 78 | unregister registry name = do |
38 | 79 | reg <- readRegistry registry |
39 | 80 | when (name `elem` map fst reg) $ do |
40 | | - atomically $ modifyTVar registry $ filter ((/= name) . fst) |
| 81 | + atomically $ modifyReg registry $ modifyRegistered $ filter ((/= name) . fst) |
41 | 82 |
|
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) |
44 | 85 |
|
45 | | -garbageCollect :: Registry -> IO () |
| 86 | +garbageCollect :: forall m. MonadRegistry m => Registry m -> m () |
46 | 87 | 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) |
50 | 91 | return () |
0 commit comments