1{-# LANGUAGE RecordWildCards #-}
3module Control.Distributed.Process.Management.Agent where
5import Control.Applicative ((<$>))
6import Control.Concurrent.MVar (MVar, newEmptyMVar, putMVar, takeMVar)
7import Control.Concurrent.STM (atomically)
8import Control.Concurrent.STM.TChan
15import Control.Distributed.Process.Internal.Primitives
22import Control.Distributed.Process.Internal.CQueue
26import Control.Distributed.Process.Internal.Trace.Tracer
29import Control.Distributed.Process.Internal.Types
40import Control.Exception (AsyncException(ThreadKilled), SomeException)
41import qualified Control.Exception as Ex (catch, finally)
42import Control.Monad (void)
43import Control.Monad.IO.Class (liftIO)
44import Control.Monad.Reader (ask)
45import GHC.Weak (Weak(Weak), deRefWeak)
46import System.Environment (getEnv)
48-- | Gross though it is, this synonym represents a function
49-- used to forking new processes, which has to be passed as a HOF
50-- when calling mxAgentController, since there's no other way to
51-- avoid a circular dependency with Node.hs
52type Fork = (Process () -> IO ProcessId)
54-- | A triple containing a configured tracer, weak pointer to the
55-- agent controller's mailbox (CQueue) and an expression used to
56-- instantiate new agents on the current node.
58 (Tracer, Weak (CQueue Message), ((Message -> Process ()) -> IO ProcessId))
60-- | Starts a management agent for the current node. The agent process
61-- must not crash or be killed, so we generally avoid publishing its
62-- 'ProcessId' where possible.
64-- Our process is also responsible for forwarding messages to the trace
65-- controller, since having two /special processes/ handled via the
66-- @LocalNode@ would be inelegant. We forward messages directly to the
67-- trace controller's message queue, just as the @MxEventBus@ that's
68-- set up on the @LocalNode@ forwards messages directly to us. This
69-- optimises the code path for tracing and avoids overloading the node
70-- node controller's internal control plane with additional routing, at the
71-- cost of a little more complexity and two cases where we break
74mxAgentController :: Fork
77mxAgentController forkProcess mv = do
78 node <- processNode <$> ask
79 trc <- liftIO $ startTracing forkProcess
80 sigbus <- liftIO $ newBroadcastTChanIO
81 weakQueue <- processWeakQ <$> ask
82 liftIO $ putMVar mv (trc, weakQueue, mxAgent forkProcess sigbus)
83 go sigbus trc forkProcess
85 go bus tracer fork = forever' $ do
87 -- This is exactly what it appears to be: a "catch all" handler.
88 -- Since mxNotify can potentially pass an unevaluated thunk to
89 -- our mailbox, the dequeue (i.e., matchMessage) can fail and
90 -- crash this process, which we DO NOT want. Alternatively,
91 -- we handle IO exceptions here explicitly, since we don't want
92 -- this process to ever crash, and the assumption we therefore
95 -- 1. only ThreadKilled can tell this process to terminate
96 -- 2. all other exceptions are invalid and should be ignored
98 -- The outcome of course, is that /bad/ calls to mxNotify
99 -- (e.g., passing unevaluated thunks that will crash when
100 -- they're eventually forced) are thus silently ignored.
102 matchAny (\msg -> liftIO $ broadcast bus tracer msg)
103 ] `catches` [Handler (\ThreadKilled -> die "Killed"),
104 Handler (\(_ :: SomeException) -> return ())]
106 broadcast :: TChan Message -> Tracer -> Message -> IO ()
107 broadcast ch tr msg = do
108 tmQueue <- tracerQueue tr
109 atomicBroadcast ch tmQueue msg
111 tracerQueue :: Tracer -> IO (Maybe (CQueue Message))
112 tracerQueue (Tracer _ wQ) = deRefWeak wQ
114 atomicBroadcast :: TChan Message
115 -> Maybe (CQueue Message)
117 atomicBroadcast ch Nothing msg = liftIO $ atomically $ writeTChan ch msg
118 atomicBroadcast ch (Just q) msg = do
119 liftIO $ atomically $ enqueueSTM q msg >> writeTChan ch msg
121-- | Forks a new process in which an mxAgent is run.
122mxAgent :: Fork -> TChan Message -> (Message -> Process ()) -> IO ProcessId
123mxAgent fork chan handler = (atomically (dupTChan chan)) >>= fork . run handler
125 run :: (Message -> Process ()) -> TChan Message -> Process ()
126 run handler' chan' = do
127 (liftIO $ atomically $ readTChan chan') >>= handler' >> run handler' chan'
129startTracing :: Fork -> IO Tracer
130startTracing forkProcess = do
132 pid <- forkProcess $ traceController mv
133 wQ <- liftIO $ takeMVar mv
134 return $ Tracer pid wQ
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list