diff options
author | Gregor Kleen <gkleen@yggdrasil.li> | 2016-02-17 22:08:36 +0000 |
---|---|---|
committer | Gregor Kleen <gkleen@yggdrasil.li> | 2016-02-17 22:08:36 +0000 |
commit | b5b4b86427286002081f102d1e97baef9162851e (patch) | |
tree | 5d3eeef8abc884931756dd3f9711c355f4a88a4e /server/src/Thermoprint/Server | |
parent | eebc709302833932d7fca95dfc5c8536f8911e69 (diff) | |
download | thermoprint-b5b4b86427286002081f102d1e97baef9162851e.tar thermoprint-b5b4b86427286002081f102d1e97baef9162851e.tar.gz thermoprint-b5b4b86427286002081f102d1e97baef9162851e.tar.bz2 thermoprint-b5b4b86427286002081f102d1e97baef9162851e.tar.xz thermoprint-b5b4b86427286002081f102d1e97baef9162851e.zip |
concurrency & dyre fixes for server spec
Diffstat (limited to 'server/src/Thermoprint/Server')
-rw-r--r-- | server/src/Thermoprint/Server/Fork.hs | 81 |
1 files changed, 81 insertions, 0 deletions
diff --git a/server/src/Thermoprint/Server/Fork.hs b/server/src/Thermoprint/Server/Fork.hs new file mode 100644 index 0000000..402c1f8 --- /dev/null +++ b/server/src/Thermoprint/Server/Fork.hs | |||
@@ -0,0 +1,81 @@ | |||
1 | {-# LANGUAGE FlexibleContexts #-} | ||
2 | |||
3 | module Thermoprint.Server.Fork | ||
4 | ( ThreadManager | ||
5 | , fork | ||
6 | , cleanup | ||
7 | , threadManager | ||
8 | ) where | ||
9 | |||
10 | import Control.Monad.Reader.Class | ||
11 | import Control.Monad.Trans.Class | ||
12 | import Control.Monad.Trans.Reader (ReaderT, runReaderT) | ||
13 | import Control.Monad.Catch | ||
14 | |||
15 | import Control.Monad.IO.Class | ||
16 | |||
17 | import Control.Monad | ||
18 | import Control.Applicative | ||
19 | import Data.Maybe | ||
20 | |||
21 | import Data.Foldable | ||
22 | |||
23 | import Data.Map (Map) | ||
24 | import qualified Data.Map as Map | ||
25 | |||
26 | import Control.Concurrent | ||
27 | import Control.Concurrent.STM | ||
28 | import Control.Concurrent.STM.TVar (TVar) | ||
29 | import qualified Control.Concurrent.STM.TVar as T | ||
30 | import Control.Concurrent.STM.TSem (TSem) | ||
31 | import qualified Control.Concurrent.STM.TSem as S | ||
32 | |||
33 | data ThreadManager m = ThreadManager | ||
34 | { fork :: m () -> m ThreadId | ||
35 | , cleanup :: m () | ||
36 | } | ||
37 | |||
38 | threadManager :: (MonadIO m, MonadMask m) => (m () -> m ThreadId) -> m (ThreadManager m) | ||
39 | threadManager f = do | ||
40 | tVar <- newTVar Map.empty | ||
41 | return ThreadManager | ||
42 | { fork = \act -> do | ||
43 | let | ||
44 | unregisterSelf :: MonadIO m => m () | ||
45 | unregisterSelf = do | ||
46 | tMap <- readTVar tVar | ||
47 | tId <- liftIO $ myThreadId | ||
48 | modifyTVar' tVar $ Map.delete tId | ||
49 | maybeM signalTSem $ Map.lookup tId tMap | ||
50 | |||
51 | mask $ \unmask -> do | ||
52 | tId <- f (unmask act `finally` unregisterSelf) | ||
53 | modifyTVar' tVar =<< (Map.insert tId <$> newTSem 0) | ||
54 | return tId | ||
55 | , cleanup = liftIO $ | ||
56 | mapM_ (\(tId, s) -> killThread tId >> waitTSem s) . Map.toList =<< readTVar tVar | ||
57 | } | ||
58 | where | ||
59 | atomically' :: MonadIO m => STM a -> m a | ||
60 | atomically' = liftIO . atomically | ||
61 | |||
62 | newTSem :: MonadIO m => Int -> m TSem | ||
63 | newTSem = atomically' . S.newTSem | ||
64 | |||
65 | waitTSem :: MonadIO m => TSem -> m () | ||
66 | waitTSem = atomically' . S.waitTSem | ||
67 | |||
68 | signalTSem :: MonadIO m => TSem -> m () | ||
69 | signalTSem = atomically' . S.signalTSem | ||
70 | |||
71 | newTVar :: MonadIO m => a -> m (TVar a) | ||
72 | newTVar = atomically' . T.newTVar | ||
73 | |||
74 | readTVar :: MonadIO m => TVar a -> m a | ||
75 | readTVar = atomically' . T.readTVar | ||
76 | |||
77 | modifyTVar' :: MonadIO m => TVar a -> (a -> a) -> m () | ||
78 | modifyTVar' t = atomically' . T.modifyTVar t | ||
79 | |||
80 | maybeM :: Applicative m => (a -> m ()) -> Maybe a -> m () | ||
81 | maybeM = maybe $ pure () | ||