packages feed

patterns 0.1.0 → 0.1.1

raw patch · 8 files changed

+218/−251 lines, 8 filesPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

API changes (from Hackage documentation)

- Network.Mom.Patterns.Broker.Common: SingleBrokerExc :: String -> MDPException
+ Network.Mom.Patterns.Basic.Client: checkResult :: Client -> Timeout -> SinkR (Maybe r) -> IO (Maybe r)
+ Network.Mom.Patterns.Broker.Client: checkResult :: Client -> Timeout -> SinkR (Maybe a) -> IO (Maybe a)
- Network.Mom.Patterns.Basic.Client: request :: Client -> Timeout -> Source -> SinkR (Maybe a) -> IO (Maybe a)
+ Network.Mom.Patterns.Basic.Client: request :: Client -> Timeout -> Source -> SinkR (Maybe r) -> IO (Maybe r)

Files

patterns.cabal view
@@ -1,5 +1,5 @@ Name:            patterns-Version:         0.1.0+Version:         0.1.1 Cabal-Version:   >= 1.14.0 Copyright:       Copyright (c) Tobias Schoofs, 2011 - 2013 License:         LGPL@@ -29,6 +29,13 @@   .    Release History:++  .++  [0.1.1] Client (Basic and MDP):+          checkResult added;+          MDP Broker:+          Many brokers in one process.    . 
src/Network/Mom/Patterns.hs view
@@ -207,7 +207,7 @@       Advanced communication patterns use      message exchange protocols and other means,-     even brokers, to make communication reliably and scalable.+     even brokers, to make communication reliable and scalable.       The 'patterns' library aims to provide      advanced topics as libraries that can be used 
src/Network/Mom/Patterns/Basic/Client.hs view
@@ -9,7 +9,7 @@ -- Client side of Client/Server ------------------------------------------------------------------------------- module Network.Mom.Patterns.Basic.Client (-         Client, clService, withClient, request)+         Client, clService, withClient, request, checkResult) where    import qualified System.ZMQ            as Z@@ -85,8 +85,21 @@   -- >                     Nothing -> return Nothing   -- >                     Just x  -> return $ Just $ B.unpack x   -------------------------------------------------------------------------  request :: Client -> Timeout -> Source -> SinkR (Maybe a) -> IO (Maybe a)+  request :: Client -> Timeout -> Source -> SinkR (Maybe r) -> IO (Maybe r)   request c tmo src snk = do     runSender   (clSock c) src     if tmo /= 0 then runReceiver (clSock c) tmo snk                 else return Nothing++  ------------------------------------------------------------------------+  -- | Check for a of a previously requested result;+  --   use case: request with timout 0, do some work+  --             and check for a result later.+  --   Do not use this function without having requested the service+  --      previously.+  --   The parameters equal those of 'request',+  --   but do not include a 'Source'.+  ------------------------------------------------------------------------+  checkResult :: Client -> Timeout -> SinkR (Maybe r) -> IO (Maybe r)+  checkResult c tmo snk | tmo == 0  = return Nothing+                        | otherwise = runReceiver (clSock c) tmo snk
src/Network/Mom/Patterns/Broker.hs view
@@ -87,12 +87,6 @@      The functionality provided by the broker module      can be used to implement a standalone broker      or an in-process broker linked with its clients and servers.-     Note, however, -     that brokers must not share the same process-     with other brokers, since, internally,-     the broker module uses a global registry,-     which would be shared among all brokers -     in the same process as well.    -}    {- $common
src/Network/Mom/Patterns/Broker/Broker.hs view
@@ -28,11 +28,9 @@   import           Control.Monad.Trans (liftIO)   import           Control.Monad       (when)   import           Prelude hiding (catch)-  import           Control.Exception (throwIO, finally)+  import           Control.Exception (throwIO)   import           Control.Concurrent.MVar -  import           System.IO.Unsafe-   ------------------------------------------------------------------------   -- | Start a broker as a background process   -- @@ -59,22 +57,24 @@                 OnError_ -> (Controller -> IO r)                -> IO r   withBroker ctx srv tmo aClients aServers onerr ctrl | tmo <= 0  =      throwIO $ MDPExc "Heartbeat is mandatory"-                                                      | otherwise = -    lockBroker $ \_ -> do-      R.clean-      R.setHbPeriod tmo-      t <- getCurrentTime-      m <- newMVar t-      withStreams ctx srv (1000 * fromIntegral tmo)+                                                      | otherwise = do+    r <- R.newReg tmo+    t <- getCurrentTime+    m <- newMVar t+    withStreams ctx srv (1000 * fromIntegral tmo)                   [Poll "servers" aServers RouterT Bind [] [],-                  Poll "clients" aClients RouterT Bind [] []]-                  (handleTmo    m tmo) onerr -                  (handleStream m tmo) ctrl +                   Poll "clients" aClients RouterT Bind [] []]+                  (handleTmo    r m tmo) onerr +                  (handleStream r m tmo) ctrl  -  -- handle heartbeat --------------------------------------------------------  handleTmo :: MVar UTCTime -> Msec -> Streamer -> IO ()-  handleTmo m tmo s = hbPeriodReached m tmo >>= \x -> -                         when x $ R.checkWorker >>= mapM_ sndHb+  ------------------------------------------------------------------------+  -- handle heartbeat:+  -- - remove unresponsive workers +  -- - heartbeat those that have been inactive for at least one hb period+  ------------------------------------------------------------------------+  handleTmo :: R.Registry -> MVar UTCTime -> Msec -> Streamer -> IO ()+  handleTmo r m tmo s = hbPeriodReached m tmo >>= \x -> +                          when x $ R.checkWorker r >>= mapM_ sndHb     where hbM   i   = [i, B.empty, mdpW01, xHeartBeat]           sndHb i   = C.runResourceT $ streamList (hbM i) $$                                          passAll s ["servers"]@@ -82,23 +82,23 @@   ------------------------------------------------------------------------   -- Handle incoming Streams   -------------------------------------------------------------------------  handleStream :: MVar UTCTime -> Msec -> StreamSink-  handleStream m x s = -    let action | getSource s == "clients" = recvClient s-               | getSource s == "servers" = recvWorker s+  handleStream :: R.Registry -> MVar UTCTime -> Msec -> StreamSink+  handleStream r m x s = +    let action | getSource s == "clients" = recvClient r s+               | getSource s == "servers" = recvWorker r s                | otherwise                = return ()         -- and handle heartbeat afterwards---------------------------------     in action >> liftIO (handleTmo m x s)+     in action >> liftIO (handleTmo r m x s)    ------------------------------------------------------------------------   -- Receive stream from client   -------------------------------------------------------------------------  recvClient :: StreamSink-  recvClient s = mdpCRcvReq >>= uncurry go+  recvClient :: R.Registry -> StreamSink+  recvClient r s = mdpCRcvReq >>= uncurry go     where go i sn | hdr sn == mmiHdr = handleMMI i sn                   | otherwise        = handleReq i sn           handleReq i sn = do-            mbW <- liftIO $ R.getWorker sn+            mbW <- liftIO $ R.getWorker r sn             case mbW of               Nothing -> noWorker sn               Just w  -> sendRequest w [i] s@@ -110,7 +110,7 @@               Nothing -> liftIO (throwIO $ MMIExc                                     "No ServiceName in mmi.service request")               Just x  -> do-                m <- bool2MMI <$> liftIO (R.lookupService x)+                m <- bool2MMI <$> liftIO (R.lookupService r x)                 C.yield m =$ sendReply sn [i] s           bool2MMI True  = mmiFound           bool2MMI False = mmiNotFound@@ -122,25 +122,25 @@   ------------------------------------------------------------------------   -- Receive stream from worker    -------------------------------------------------------------------------  recvWorker :: StreamSink -  recvWorker s = do+  recvWorker :: R.Registry -> StreamSink +  recvWorker r s = do     f <- mdpWRcvRep     case f of-      WBeat  w    -> liftIO (R.updWorkerHb w) -- update his heartbeat -      WReady w sn -> liftIO $ R.insert w sn   -- insert new worker-      WReply w is -> handleReply w is s       -- handle reply from worker-      WDisc  w    -> liftIO $ R.remove w      -- disconnect from worker+      WBeat  w    -> liftIO (R.updWorkerHb r w) -- update his heartbeat +      WReady w sn -> liftIO $ R.insert r w sn   -- insert new worker+      WReply w is -> handleReply r w is s       -- handle reply from worker+      WDisc  w    -> liftIO $ R.remove r w      -- disconnect from worker       _           -> liftIO (throwIO $ Ouch "Unexpected Frame from Worker!")    ------------------------------------------------------------------------   -- Handle reply   -------------------------------------------------------------------------  handleReply :: Identity -> [Identity] -> StreamSink-  handleReply w is s = do-    mbS <- liftIO $ R.getServiceName w+  handleReply :: R.Registry -> Identity -> [Identity] -> StreamSink+  handleReply r w is s = do+    mbS <- liftIO $ R.getServiceName r w     case mbS of       Nothing -> liftIO (throwIO $ ServerExc "Unknown Worker")-      Just sn -> sendReply sn is s >> liftIO (R.freeWorker w)+      Just sn -> sendReply sn is s >> liftIO (R.freeWorker r w)    ------------------------------------------------------------------------   -- Send request to worker@@ -153,20 +153,4 @@   ------------------------------------------------------------------------   sendReply :: B.ByteString -> [Identity] -> StreamSink   sendReply sn is s = mdpCSndRep sn is =$ passAll s ["clients"]--  -------------------------------------------------------------------------  -- Mechanism to exclude two brokers -  -- from running in the same process-  -------------------------------------------------------------------------  {-# NOINLINE _brk #-}-  _brk :: MVar () -  _brk = unsafePerformIO $ newMVar ()--  lockBroker :: (() -> IO b) -> IO b-  lockBroker act = do-    mb_ <- tryTakeMVar _brk -- withMVar _brk -    case mb_ of-      Nothing -> throwIO $ SingleBrokerExc "Another broker is running!"-      Just _  -> finally (act ()) (putMVar _brk ())- 
src/Network/Mom/Patterns/Broker/Client.hs view
@@ -10,7 +10,7 @@ ------------------------------------------------------------------------------- module Network.Mom.Patterns.Broker.Client (                    Client, withClient, checkService,-                   request)+                   request, checkResult) where    import qualified Data.ByteString.Char8  as B@@ -117,13 +117,22 @@   request :: Client -> Timeout -> Source -> SinkR (Maybe a) -> IO (Maybe a)   request c tmo src snk = do     runSender   (clSock c)     $ mdpSrc (clService c) src-    runReceiver (clSock c) tmo $ mdpSnk (clService c) snk+    if tmo /= 0 then runReceiver (clSock c) tmo $ mdpSnk (clService c) snk+                else return Nothing -  {- currently not provided-  checkReceive :: Client -> Timeout -> SinkR (Maybe a) -> IO (Maybe a)-  checkReceive c tmo snk = +  ------------------------------------------------------------------------+  -- | Check for a of a previously requested result;+  --   use case: request with timout 0, do some work+  --             and check for a result later.+  --   Do not use this function without having requested the service+  --      previously.+  --   The parameters equal those of 'request',+  --   but do not include a 'Source'.+  ------------------------------------------------------------------------+  checkResult :: Client -> Timeout -> SinkR (Maybe a) -> IO (Maybe a)+  checkResult c tmo snk | tmo == 0  = return Nothing+                        | otherwise =      runReceiver (clSock c) tmo $ mdpSnk (clService c) snk-  -}        ------------------------------------------------------------------------   -- Add MDP headers to a stream created by source
src/Network/Mom/Patterns/Broker/Common.hs view
@@ -289,9 +289,6 @@          | MDPExc   String          -- | MMI Protocol          | MMIExc   String-         -- | SingleBroker error-         --   (another broker is already running in the same process)-         | SingleBrokerExc String -- move to Broker.Common     deriving (Show, Read, Typeable, Eq)    instance Exception MDPException
src/broker/Registry.hs view
@@ -11,7 +11,8 @@ ------------------------------------------------------------------------------- module Registry  #ifndef TEST-       (insert, remove, +       (Registry, newReg,+        insert, remove,          checkWorker, getWorker, freeWorker,          getServiceName,         updWorkerHb, lookupService, @@ -25,11 +26,8 @@   import           Network.Mom.Patterns.Types (Msec, Identity)    import           Control.Concurrent-  import           Control.Monad (when)   import           Control.Applicative ((<$>)) -  import           System.IO.Unsafe-   import qualified Data.ByteString.Char8 as B   import           Data.Map   (Map)   import qualified Data.Map   as   M -- filter, keys@@ -37,7 +35,34 @@   import           Data.Sequence (Seq, (|>), (<|), (><), ViewL(..), ViewR(..))    import           Data.Foldable (toList)   import           Data.Time.Clock+                 +  ------------------------------------------------------------------------+  -- Registry +  ------------------------------------------------------------------------+  type Registry = MVar Reg+  data Reg      = NewReg {+                     rgHb  :: Msec,+                     rgSrv :: SrvTree,+                     rgWrk :: WrkTree,+                     rgS   :: Seq B.ByteString+                  } +  newReg :: Msec -> IO Registry+  newReg ms = newMVar NewReg {+                    rgHb  = ms,+                    rgSrv = M.empty,+                    rgWrk = M.empty,+                    rgS   = S.empty}++  modifyReg :: Registry -> (Reg -> IO (Reg, r)) -> IO r+  modifyReg = modifyMVar ++  modifyReg_ :: Registry -> (Reg -> IO Reg) -> IO ()+  modifyReg_ = modifyMVar_ ++  withReg :: Registry -> (Reg -> IO r) -> IO r+  withReg = withMVar +   ------------------------------------------------------------------------   -- Worker may be free or busy   ------------------------------------------------------------------------@@ -49,25 +74,16 @@   ------------------------------------------------------------------------   data Worker = Worker {                   wrkId    :: Identity,-                  wrkState :: State,                   wrkHB    :: Heartbeat,                   wrkQ     :: MVar Queue                 } -  -------------------------------------------------------------------------  -- Base for Heartbeat calculation:-  -- expected heartbeat is _hb * tolerance,-  -- heartbeat to be sent is _hb -  -------------------------------------------------------------------------  {-# NOINLINE _hb #-}-  _hb :: MVar Msec-  _hb = unsafePerformIO $ newMVar 2000 -- default: 2 seconds-   -------------------------------------------------------------------------   -- Set common heartbeat period   --------------------------------------------------------------------------  setHbPeriod :: Msec -> IO ()-  setHbPeriod hb = modifyMVar_ _hb $ \_ -> return hb+  setHbPeriod :: Registry -> Msec -> IO ()+  setHbPeriod r hb = modifyReg_ r $ \reg -> +                       return reg{rgHb = hb}    -------------------------------------------------------------------------   -- Update worker's heartbeat descriptor according to function@@ -79,7 +95,7 @@   ------------------------------------------------------------------------   -- A Worker Node is an Identity paired with a worker (lookup)   -- A Worker Tree is a map of identities and services,-  -- where service is a queue of free and busy workers;+  -- where a service is basically a queue of free and busy workers;   -- the worker tree, hence, does not point directly to the worker,   -- but to the queue where the worker can be found:   --@@ -111,119 +127,95 @@   type SrvTree = Map B.ByteString Service    --------------------------------------------------------------------------  -- service tree-  --------------------------------------------------------------------------  {-# NOINLINE _srv #-}-  _srv :: MVar SrvTree-  _srv = unsafePerformIO $ newMVar M.empty --  --------------------------------------------------------------------------  -- A sequence of services, used for removing unresponsive workers:-  -- We clean up one service at a time, so there are not too long-  -- work intervals due to clean-up.-  --------------------------------------------------------------------------  {-# NOINLINE _s #-}-  _s :: MVar (Seq B.ByteString)-  _s = unsafePerformIO $ newMVar S.empty --  --------------------------------------------------------------------------  -- worker tree-  --------------------------------------------------------------------------  {-# NOINLINE _wrk #-}-  _wrk :: MVar WrkTree-  _wrk = unsafePerformIO $ newMVar M.empty --  -------------------------------------------------------------------------   -- Register new worker for service   --------------------------------------------------------------------------  insert :: Identity -> B.ByteString -> IO ()-  insert i s = do-    mbW <- lookupW i-    case mbW of-      Just _  -> return () -- identity already known+  insert :: Registry -> Identity -> B.ByteString -> IO ()+  insert r i s = modifyReg_ r $ \reg -> +    case M.lookup i (rgWrk reg) of+      Just _  -> return reg -- identity already known       Nothing -> do-        sn <- lookupS s >>= getSn-        initW   i sn-        insertW i sn-    where getSn mbS = -            case mbS of-              Just sn -> return sn-              Nothing -> do-                 q <- newMVar $ Q S.empty S.empty-                 let sn = Service {-                            srvName = s,-                            srvQ    = q}-                 insertS s sn-                 return sn+        (reg2, sn) <- case M.lookup s (rgSrv reg) of+                        Just x  -> return (reg, x) -- service exists+                        Nothing -> do              -- new service+                          q <- newMVar $ Q S.empty S.empty+                          let x  = Service {+                                    srvName = s,+                                    srvQ    = q}+                          return (reg {rgSrv = M.insert s x (rgSrv reg),+                                       rgS   = rgS reg |> s}, x)+        hb <- newHeartbeat (rgHb reg2)+        initW  i hb sn                  -- add worker to service queue+        return reg2 {rgWrk =            -- insert worker into wrk tree+          M.insert i sn $ rgWrk reg2}    -------------------------------------------------------------------------   -- Remove worker    --------------------------------------------------------------------------  remove :: Identity -> IO ()-  remove i = do-    mbW <- lookupW i-    case mbW of-      Nothing -> return ()+  remove :: Registry -> Identity -> IO ()+  remove r i = modifyReg_ r $ \reg -> +    case M.lookup i (rgWrk reg) of+      Nothing -> return reg                        -- worker does not exist       Just sn -> do-        deleteW i-        e <- modifyMVar (srvQ sn) $ \q -> +        let reg2 = reg {rgWrk = M.delete i (rgWrk reg)} -- delete from tree+        e <- modifyMVar (srvQ sn) $ \q ->               -- remove from queue                let q' = removeQ i q                    e  = emptyQ q'                 in return (q', e)-        when e $ deleteS (srvName sn)+        if e then return reg2{rgSrv =                -- remove empty service+                    M.delete (srvName sn) (rgSrv reg2)}+             else return reg2    -------------------------------------------------------------------------   -- Free a worker that has been busy   -- updating his heatbeat   --------------------------------------------------------------------------  freeWorker :: Identity -> IO ()-  freeWorker i = getCurrentTime >>= \now ->-                   freeWorkerWithUpd i (updHb updHim now)+  freeWorker :: Registry -> Identity -> IO ()+  freeWorker r i = getCurrentTime >>= \now ->+                     freeWorkerWithUpd r i (updHb updHim now)    -------------------------------------------------------------------------   -- Free a worker that has been busy   -- updating heartbeat (because we apparently have feedback )   --------------------------------------------------------------------------  freeWorkerWithUpd :: Identity -> (WrkNode -> WrkNode) -> IO ()-  freeWorkerWithUpd i upd = do-    mbW <- lookupW i-    case mbW of-      Nothing -> return () -- silent error-      Just sn -> modifyMVar_ (srvQ sn) $ \q -> return $ setStateQ i Free upd q +  freeWorkerWithUpd :: Registry -> Identity -> (WrkNode -> WrkNode) -> IO ()+  freeWorkerWithUpd r i upd = withReg r $ \reg -> +    case M.lookup i (rgWrk reg) of+      Nothing -> return ()                       -- worker does not exist+      Just sn -> modifyMVar_ (srvQ sn) $ \q -> +                   return $ setStateQ i Free upd q -- set free and upd hb     -------------------------------------------------------------------------   -- Update heartbeat   --------------------------------------------------------------------------  updWorkerHb :: Identity -> IO ()-  updWorkerHb i = getCurrentTime >>= \now -> updWorker i (updHb updHim now)+  updWorkerHb :: Registry -> Identity -> IO ()+  updWorkerHb r i = getCurrentTime >>= \now -> +                      updWorker r i (updHb updHim now)    -------------------------------------------------------------------------   -- Generic worker update   --------------------------------------------------------------------------  updWorker :: Identity -> (WrkNode -> WrkNode) -> IO ()-  updWorker i f = do-    mbW <- lookupW i-    case mbW of-      Nothing -> return ()+  updWorker :: Registry -> Identity -> (WrkNode -> WrkNode) -> IO ()+  updWorker r i f = withReg r $ \reg ->+    case M.lookup i (rgWrk reg) of+      Nothing -> return ()        -- worker does not exist       Just sn -> modifyMVar_ (srvQ sn) $ \q -> -                   return $ updateQ i f q+                   return $ updateQ i f q -- update worker    -------------------------------------------------------------------------   -- Get service by worker identity   --------------------------------------------------------------------------  getServiceName :: Identity -> IO (Maybe B.ByteString)-  getServiceName i = do-    mbW <- lookupW i-    case mbW of+  getServiceName :: Registry -> Identity -> IO (Maybe B.ByteString)+  getServiceName r i = withReg r $ \reg -> +    case M.lookup i (rgWrk reg) of       Nothing -> return Nothing       Just s  -> return $ Just (srvName s)    -------------------------------------------------------------------------   -- Check whether service exists   --------------------------------------------------------------------------  lookupService :: B.ByteString -> IO Bool-  lookupService s = do-    mbS <- lookupS s+  lookupService :: Registry -> B.ByteString -> IO Bool+  lookupService r s = do+    mbS <- lookupS r s     case mbS of       Nothing -> return False       Just  _ -> return True@@ -235,16 +227,16 @@   --                          and update its heartbeat   --   - if free q is emtpy, take the first of the busy queue   --------------------------------------------------------------------------  getWorker :: B.ByteString -> IO (Maybe Identity)-  getWorker s = do+  getWorker :: Registry -> B.ByteString -> IO (Maybe Identity)+  getWorker r s = do     now <- getCurrentTime-    mbS <- lookupS s+    mbS <- lookupS r s     case mbS of-      Nothing -> return Nothing+      Nothing -> return Nothing -- service does not exist       Just sn -> do-        bury now sn -- remove non-responsive workers+        bury r now sn    -- remove non-responsive workers         modifyMVar (srvQ sn) $ \q ->-          case firstFreeQ q of+          case firstFreeQ q of -- get first of free             Just (i,_) ->               return (setStateQ i Busy (updHb updMe now) q, Just i)             Nothing    -> @@ -258,20 +250,20 @@   -- and heartbeat those that have been inactive    --     for at least one heartbeat period   --------------------------------------------------------------------------  checkWorker :: IO [Identity]-  checkWorker = do+  checkWorker :: Registry -> IO [Identity]+  checkWorker r = do     now <- getCurrentTime-    checkService >>= \mbS -> case mbS of-                               Nothing -> return ()-                               Just s  -> bury now s-    concat <$> withMVar _srv (mapM (getWorkers now) . M.elems)+    checkService r >>= \mbS -> case mbS of+                                 Nothing -> return ()+                                 Just s  -> bury r now s+    srv <- M.elems <$> withReg r (return . rgSrv) +    concat <$> mapM (getWorkers now) srv      where getWorkers now s = do             is <- withMVar (srvQ s) (                      return . map fst . takeIfQ Free tst)-            mapM_ (`updWorker` updSt) is+            mapM_ (\i -> updWorker r i updSt) is             return is-             where updSt (i,w) = let hb  = updMe now $ wrkHB w                                  in (i, w{wrkHB = hb})                    tst = checkMe now . wrkHB . snd@@ -284,59 +276,62 @@   --       if a lot of services have been removed since our last visit   --          we will go through this many times   --------------------------------------------------------------------------  checkService :: IO (Maybe Service)-  checkService = do-    mbS <- modifyMVar _s getS+  checkService :: Registry -> IO (Maybe Service)+  checkService r = do +    mbS <- modifyReg r $ \reg -> do+      let (s, mbS) = getS (rgS reg)+      return (reg {rgS = s}, mbS)     case mbS of        Nothing -> return Nothing       Just s  -> do-        mbSrv <- lookupS s+        mbSrv <- lookupS r s         case mbSrv of-          Nothing  -> rm >> checkService+          Nothing  -> rm >> checkService r           Just srv -> return $ Just srv     where getS s =              case S.viewl s of-              EmptyL  -> return (s, Nothing)-              x :< xs -> -                return (xs |> x, Just x)+              EmptyL  -> (s, Nothing)+              x :< xs -> (xs |> x, Just x)           rmTail s =              case S.viewr s of-              EmptyR  -> return s-              xs :> _ -> return xs-          rm = modifyMVar_ _s rmTail+              EmptyR  -> s+              xs :> _ -> xs+          rm = modifyReg_ r $ \reg -> +                 return reg {rgS = rmTail (rgS reg)}    -------------------------------------------------------------------------   -- Remove unresponsive workers   --------------------------------------------------------------------------  bury :: UTCTime -> Service -> IO ()-  bury now sn = do-    q <- readMVar $ srvQ sn-    mapM_ (remove . fst) $ findDeads $ qFree q-    mapM_ (remove . fst) $ findDeads $ qBusy q-    where findDeads = filter (not . alive now . wrkHB . snd) . toList+  bury :: Registry -> UTCTime -> Service -> IO ()+  bury r now sn = do +    q <- readMVar $ srvQ sn -- we use remove on registry, so don't block+    mapM_ (remove r . fst) $ findDead $ qFree q+    mapM_ (remove r . fst) $ findDead $ qBusy q+    where findDead = filter (not . alive now . wrkHB . snd) . toList    --------------------------------------------------------------------------  -- Clean up global variables+  -- Clean up registry   --------------------------------------------------------------------------  clean :: IO ()-  clean = do modifyMVar_ _wrk $ \_ -> return M.empty-             modifyMVar_ _srv $ \_ -> return M.empty-             modifyMVar_ _s   $ \_ -> return S.empty+  clean :: Registry -> IO ()+  clean r = modifyReg_ r $ \reg -> return reg {+                                     rgWrk = M.empty,+                                     rgSrv = M.empty,+                                     rgS   = S.empty}    -------------------------------------------------------------------------   -- Number of workers   --------------------------------------------------------------------------  size :: IO Int-  size = withMVar _wrk $ \t -> return $ M.size t+  size :: Registry -> IO Int+  size r = withReg r $ \reg -> return $ M.size (rgWrk reg)    -------------------------------------------------------------------------   -- Per service:   --     number of free workers   --     number of busy workers   --------------------------------------------------------------------------  statPerService :: B.ByteString -> IO (B.ByteString, Int, Int)-  statPerService s = do-    mbS <- lookupS s+  statPerService :: Registry -> B.ByteString -> IO (B.ByteString, Int, Int)+  statPerService r s = do+    mbS <- lookupS r s     case mbS of       Nothing -> return (s, 0, 0)       Just sn -> perService (s, sn)@@ -346,8 +341,9 @@   --     number of free workers   --     number of busy workers   --------------------------------------------------------------------------  stat :: IO [(B.ByteString, Int, Int)]-  stat = withMVar _srv $ \t -> mapM perService $ M.assocs t+  stat :: Registry -> IO [(B.ByteString, Int, Int)]+  stat r = withReg r $ \reg -> +             mapM perService $ M.assocs (rgSrv reg)    -------------------------------------------------------------------------   -- Get stats per service@@ -360,9 +356,9 @@   -------------------------------------------------------------------------   -- Debug: print q   --------------------------------------------------------------------------  printQ :: B.ByteString -> IO ()-  printQ s = do-    mbS <- lookupS s+  printQ :: Registry -> B.ByteString -> IO ()+  printQ r s = do+    mbS <- lookupS r s     case mbS of       Nothing -> return ()       Just x  -> do q <- readMVar (srvQ x)@@ -377,53 +373,20 @@   --        initialising heartbeat   --        inserting    into service queue   --------------------------------------------------------------------------  initW :: Identity -> Service -> IO ()-  initW i sn = modifyMVar_ (srvQ sn) $ \q -> do-                 hb <- withMVar _hb newHeartbeat -                 let w = Worker {-                           wrkId    = i,-                           wrkState = Free,-                           wrkHB    = hb,-                           wrkQ     = srvQ sn}-                 return $ insertQ (i,w) q+  initW :: Identity -> Heartbeat -> Service -> IO ()+  initW i hb sn = modifyMVar_ (srvQ sn) $ \q -> do+                    let w = Worker {+                              wrkId    = i,+                              wrkHB    = hb,+                              wrkQ     = srvQ sn}+                    return $ insertQ (i,w) q                        --------------------------------------------------------------------------  -- Lookup worker, returning service-  --------------------------------------------------------------------------  lookupW :: Identity -> IO (Maybe Service)-  lookupW i = withMVar _wrk $ \t -> return $ M.lookup i t--  -------------------------------------------------------------------------   -- Lookup service   --------------------------------------------------------------------------  lookupS :: B.ByteString -> IO (Maybe Service)-  lookupS sn = withMVar _srv $ \t -> return $ M.lookup sn t--  --------------------------------------------------------------------------  -- Insert worker-  --------------------------------------------------------------------------  insertW :: Identity -> Service -> IO ()-  insertW i sn = modifyMVar_ _wrk $ \t -> return $ M.insert i sn t--  --------------------------------------------------------------------------  -- Insert service-  --------------------------------------------------------------------------  insertS :: B.ByteString -> Service -> IO ()-  insertS s sn = do-    modifyMVar_ _srv $ \t  -> return $ M.insert s sn t-    modifyMVar_ _s   $ \ss -> return $ ss |> s-  -  --------------------------------------------------------------------------  -- Delete worker-  --------------------------------------------------------------------------  deleteW :: Identity -> IO ()-  deleteW i = modifyMVar_ _wrk $ \t -> return $ M.delete i t-  -  --------------------------------------------------------------------------  -- Delete service-  --------------------------------------------------------------------------  deleteS :: B.ByteString -> IO ()-  deleteS s = modifyMVar_ _srv $ \t -> return $ M.delete s t+  lookupS :: Registry -> B.ByteString -> IO (Maybe Service)+  lookupS r sn = withMVar r $ \reg -> +                   return $ M.lookup sn (rgSrv reg)    -------------------------------------------------------------------------   -- Queue:@@ -548,7 +511,7 @@                (w :< _) -> Just w    --------------------------------------------------------------------------  -- Take n that fulfil f with state s+  -- Take those that fulfil f with state s   -------------------------------------------------------------------------   takeIfQ :: State -> (WrkNode -> Bool) -> Queue -> [WrkNode]   takeIfQ s f q = case s of