-- Hoogle documentation, generated by Haddock -- See Hoogle, http://www.haskell.org/hoogle/ -- | The Cloud Haskell Application Platform -- -- Modelled after Erlang OTP's gen_server, this framework provides -- similar facilities for Cloud Haskell, grouping essential practices for -- client/server development into a set of modules and standards designed -- to help you build concurrent, distributed applications with relative -- ease. @package distributed-process-client-server @version 0.2.2 -- | This module provides a wrap around a simple Timer that can be -- started, stopped, reset, cleared, and read. A convenient function is -- provided for creating a Match expression for the timer. -- -- -- -- The timers defined in this module are based on a TVar Bool. -- When the client program is -threaded (i.e. -- rtsSupportsBoundThreads == True), then the timers are set -- using registerDelay, which is very efficient and relies only -- no the RTS IO Manager. When we're not -threaded, we fall back -- to using Control.Distributed.Process.Extras.Timer to set the -- TVar, which has much the same effect, but requires us to -- spawn a process to handle setting the TVar - a process which -- could theoretically die before setting the variable. module Control.Distributed.Process.ManagedProcess.Timer -- | We hold timers in 2 states, each described by a Delay. isActive = -- isJust . mtSignal the TimerRef is optional since we only use the Timer -- module from extras when we're unable to registerDelay (i.e. not -- running under -threaded) data Timer -- | A key for storing timers in prioritised process backing state. type TimerKey = Int -- | Creates a default Timer which is inactive. delayTimer :: Delay -> Timer -- | Starts a Timer Will use the GHC registerDelay API if -- rtsSupportsBoundThreads == True startTimer :: Delay -> Process Timer -- | Stops a previously started Timer. Has no effect if the -- Timer is inactive. stopTimer :: Timer -> Process Timer -- | Clears and restarts a Timer. resetTimer :: Timer -> Delay -> Process Timer -- | Clears/cancels a running timer. Has no effect if the Timer is -- inactive. clearTimer :: Maybe TimerRef -> Process () -- | Creates a Match for a given timer, for use with Cloud -- Haskell's messaging primitives for selective receives. matchTimeout :: Timer -> [Match (Either TimedOut Message)] -- | Create a match expression for a given Timer. When the timer -- expires (i.e. the "TVar Bool" is set to True), the -- Match will return Yield i, where i is the -- given TimerKey. matchKey :: TimerKey -> Timer -> [Match (Either TimedOut Message)] -- | As "matchKey", but instead of a returning Yield i, the -- generated Match handler evaluates the first argument - and -- expression from TimerKey to Process Message - to -- determine its result. matchRun :: (TimerKey -> Process Message) -> TimerKey -> Timer -> [Match Message] -- | True if a Timer is currently active. isActive :: Timer -> Bool -- | Reads a given TVar Bool for a timer, and returns STM -- TimedOut once the variable is set to true. Will retry in -- the meanwhile. readTimer :: TVar Bool -> STM TimedOut -- | Used during STM reads on Timers and to implement blocking. Since -- timers can be associated with a TimerKey, the second -- constructor for this type yields a key indicating whic Timer it -- refers to. Note that the user is responsible for establishing and -- maintaining the mapping between Timers and their keys. data TimedOut TimedOut :: TimedOut Yield :: TimerKey -> TimedOut instance GHC.Generics.Generic Control.Distributed.Process.ManagedProcess.Timer.TimedOut instance GHC.Show.Show Control.Distributed.Process.ManagedProcess.Timer.TimedOut instance GHC.Classes.Eq Control.Distributed.Process.ManagedProcess.Timer.TimedOut instance Data.Binary.Class.Binary Control.Distributed.Process.ManagedProcess.Timer.TimedOut -- | Types used throughout the ManagedProcess framework module Control.Distributed.Process.ManagedProcess.Internal.Types -- | Return type for and InitHandler expression. data InitResult s InitOk :: s -> Delay -> InitResult s InitStop :: String -> InitResult s InitIgnore :: InitResult s -- | StateT based monad for prioritised process loops. data GenProcess s a -- | Run an action in the GenProcess monad. runProcess :: State s -> GenProcess s a -> Process (a, State s) -- | Lift an action in the Process monad to GenProcess. lift :: Process a -> GenProcess s a -- | Lift an IO action directly into GenProcess, liftIO = lift -- . Process.LiftIO. liftIO :: IO a -> GenProcess s a -- | Internal state of a prioritised process loop. data ProcessState s ProcessState :: RecvTimeoutPolicy -> ProcessDefinition s -> [DispatchPriority s] -> [DispatchFilter s] -> Delay -> Timer -> TimerMap -> Queue -> s -> ProcessState s [timeoutSpec] :: ProcessState s -> RecvTimeoutPolicy [procDef] :: ProcessState s -> ProcessDefinition s [procPrio] :: ProcessState s -> [DispatchPriority s] [procFilters] :: ProcessState s -> [DispatchFilter s] [usrTimeout] :: ProcessState s -> Delay [sysTimeout] :: ProcessState s -> Timer [usrTimers] :: ProcessState s -> TimerMap [internalQ] :: ProcessState s -> Queue [procState] :: ProcessState s -> s -- | Prioritised process state, held as an IORef. type State s = IORef (ProcessState s) -- | Internal priority queue, used by prioritised processes. type Queue = PriorityQ Int Message -- | Represent a max-backlog from RecvTimeoutPolicy type Limit = Maybe Int -- | Wraps a predicate that is used to determine whether or not a handler -- is valid based on some combination of the current process state, the -- type and/or value of the input message or both. data Condition s m -- | predicated on the process state and the message Condition :: (s -> m -> Bool) -> Condition s m -- | predicated on the process state only State :: (s -> Bool) -> Condition s m -- | predicated on the input message only Input :: (m -> Bool) -> Condition s m -- | The action taken by a process after a handler has run and its updated -- state. See -- "Control.Distributed.Process.ManagedProcess.Server.continue" -- "Control.Distributed.Process.ManagedProcess.Server.timeoutAfter" -- "Control.Distributed.Process.ManagedProcess.Server.hibernate" -- "Control.Distributed.Process.ManagedProcess.Server.stop" -- "Control.Distributed.Process.ManagedProcess.Server.stopWith" -- -- Also see "Control.Distributed.Process.Management.Priority.act" and -- "Control.Distributed.Process.ManagedProcess.Priority.runAfter". -- -- And other actions. This type should not be used directly. data ProcessAction s ProcessSkip :: ProcessAction s -- | run the given activity ProcessActivity :: (GenProcess s ()) -> ProcessAction s -- | evaluate an expression ProcessExpression :: (GenProcess s (ProcessAction s)) -> ProcessAction s -- | continue with (possibly new) state ProcessContinue :: s -> ProcessAction s -- | timeout if no messages are received ProcessTimeout :: Delay -> s -> ProcessAction s -- | hibernate for delay ProcessHibernate :: TimeInterval -> s -> ProcessAction s -- | stop the process, giving ExitReason ProcessStop :: ExitReason -> ProcessAction s -- | stop the process with ExitReason, with updated state ProcessStopping :: s -> ExitReason -> ProcessAction s -- | changes the current process definition ProcessBecome :: (ProcessDefinition s) -> s -> ProcessAction s -- | Returned from handlers for the synchronous call protocol, -- encapsulates the reply data and the action to take after -- sending the reply. A handler can return NoReply if they wish -- to ignore the call. data ProcessReply r s ProcessReply :: r -> (ProcessAction s) -> ProcessReply r s ProcessReject :: String -> (ProcessAction s) -> ProcessReply r s NoReply :: (ProcessAction s) -> ProcessReply r s -- | An action (server state transition) in the Process monad type Action s = Process (ProcessAction s) -- | An action (server state transition) causing a reply to a caller, in -- the Process monad type Reply b s = Process (ProcessReply b s) -- | An expression used to handle a message type ActionHandler s a = s -> a -> Action s -- | An expression used to handle a message and providing a reply type CallHandler s a b = s -> a -> Reply b s -- | An expression used to handle a cast message type CastHandler s a = ActionHandler s a -- | An expression used to ignore server state during handling type StatelessHandler s a = a -> (s -> Action s) -- | An expression used to handle a call message where the reply is -- deferred via the CallRef type DeferredCallHandler s a b = CallRef b -> CallHandler s a b -- | An expression used to handle a call message ignoring server -- state type StatelessCallHandler s a b = CallRef b -> a -> Reply b s -- | An expression used to handle an info message type InfoHandler s a = ActionHandler s a -- | An expression used to handle a channel message type ChannelHandler s a b = SendPort b -> ActionHandler s a -- | An expression used to handle a channel message in a stateless -- process type StatelessChannelHandler s a b = SendPort b -> StatelessHandler s a -- | An expression used to initialise a process with its state type InitHandler a s = a -> Process (InitResult s) -- | An expression used to handle process termination type ShutdownHandler s = ExitState s -> ExitReason -> Process () -- | Informs a shutdown handler of whether it is running due to a -- clean shutdown, or in response to an unhandled exception. data ExitState s -- | given when an ordered shutdown is underway CleanShutdown :: s -> ExitState s LastKnown :: s -> ExitState s -- | True if the ExitState is CleanShutdown, -- otherwise False. isCleanShutdown :: ExitState s -> Bool -- | Evaluates to the s state datum in the given -- ExitState. exitState :: ExitState s -> s -- | An expression used to handle process timeouts type TimeoutHandler s = ActionHandler s Delay -- | Policy for handling unexpected messages, i.e., messages which are not -- sent using the call or cast APIs, and which are not -- handled by any of the handleInfo handlers. data UnhandledMessagePolicy -- | stop immediately, giving ExitOther UnhandledInput as -- the reason Terminate :: UnhandledMessagePolicy -- | forward the message to the given recipient DeadLetter :: ProcessId -> UnhandledMessagePolicy -- | log messages, then behave identically to Drop Log :: UnhandledMessagePolicy -- | dequeue and then drop/ignore the message Drop :: UnhandledMessagePolicy -- | Stores the functions that determine runtime behaviour in response to -- incoming messages and a policy for responding to unhandled messages. data ProcessDefinition s ProcessDefinition :: [Dispatcher s] -> [DeferredDispatcher s] -> [ExternDispatcher s] -> [ExitSignalDispatcher s] -> TimeoutHandler s -> ShutdownHandler s -> UnhandledMessagePolicy -> ProcessDefinition s -- | functions that handle call/cast messages [apiHandlers] :: ProcessDefinition s -> [Dispatcher s] -- | functions that handle non call/cast messages [infoHandlers] :: ProcessDefinition s -> [DeferredDispatcher s] -- | functions that handle control channel and STM inputs [externHandlers] :: ProcessDefinition s -> [ExternDispatcher s] -- | functions that handle exit signals [exitHandlers] :: ProcessDefinition s -> [ExitSignalDispatcher s] -- | a function that handles timeouts [timeoutHandler] :: ProcessDefinition s -> TimeoutHandler s -- | a function that is run just before the process exits [shutdownHandler] :: ProcessDefinition s -> ShutdownHandler s -- | how to deal with unhandled messages [unhandledMessagePolicy] :: ProcessDefinition s -> UnhandledMessagePolicy -- | Priority of a message, encoded as an Int newtype Priority a Priority :: Int -> Priority a [getPrio] :: Priority a -> Int -- | Dispatcher for prioritised handlers data DispatchPriority s PrioritiseCall :: (s -> Message -> Process (Maybe (Int, Message))) -> DispatchPriority s [prioritise] :: DispatchPriority s -> s -> Message -> Process (Maybe (Int, Message)) PrioritiseCast :: (s -> Message -> Process (Maybe (Int, Message))) -> DispatchPriority s [prioritise] :: DispatchPriority s -> s -> Message -> Process (Maybe (Int, Message)) PrioritiseInfo :: (s -> Message -> Process (Maybe (Int, Message))) -> DispatchPriority s [prioritise] :: DispatchPriority s -> s -> Message -> Process (Maybe (Int, Message)) -- | Provides dispatch from a variety of inputs to a typed filter handler. data DispatchFilter s FilterApi :: (s -> Message a b -> Process (Filter s)) -> DispatchFilter s [apiFilter] :: DispatchFilter s -> s -> Message a b -> Process (Filter s) FilterAny :: (s -> a -> Process (Filter s)) -> DispatchFilter s [anyFilter] :: DispatchFilter s -> s -> a -> Process (Filter s) FilterRaw :: (s -> Message -> Process (Maybe (Filter s))) -> DispatchFilter s [rawFilter] :: DispatchFilter s -> s -> Message -> Process (Maybe (Filter s)) FilterState :: (s -> Process (Maybe (Filter s))) -> DispatchFilter s [stateFilter] :: DispatchFilter s -> s -> Process (Maybe (Filter s)) -- | Given as the result of evaluating a DispatchFilter. This type -- is intended for internal use. For an API for working with filters, see -- Control.Distributed.Process.ManagedProcess.Priority. data Filter s FilterOk :: s -> Filter s FilterSafe :: s -> Filter s FilterReject :: m -> s -> Filter s FilterSkip :: s -> Filter s FilterStop :: s -> ExitReason -> Filter s -- | A ProcessDefinition decorated with DispatchPriority -- for certain input domains. data PrioritisedProcessDefinition s PrioritisedProcessDefinition :: ProcessDefinition s -> [DispatchPriority s] -> [DispatchFilter s] -> RecvTimeoutPolicy -> PrioritisedProcessDefinition s [processDef] :: PrioritisedProcessDefinition s -> ProcessDefinition s [priorities] :: PrioritisedProcessDefinition s -> [DispatchPriority s] [filters] :: PrioritisedProcessDefinition s -> [DispatchFilter s] [recvTimeout] :: PrioritisedProcessDefinition s -> RecvTimeoutPolicy -- | For a PrioritisedProcessDefinition, this policy determines for -- how long the receive loop should continue draining the process' -- mailbox before processing its received mail (in priority order). -- -- If a prioritised managed process is receiving a lot of messages -- (into its real mailbox), the server might never get around to -- actually processing its inputs. This (mandatory) policy provides a -- guarantee that eventually (i.e., after a specified number of received -- messages or time interval), the server will stop removing messages -- from its mailbox and process those it has already received. data RecvTimeoutPolicy RecvMaxBacklog :: Int -> RecvTimeoutPolicy RecvTimer :: TimeInterval -> RecvTimeoutPolicy -- | Provides a means for servers to listen on a separate, typed -- control channel, thereby segregating the channel from their -- regular (and potentially busy) mailbox. newtype ControlChannel m ControlChannel :: (SendPort (Message m ()), ReceivePort (Message m ())) -> ControlChannel m [unControl] :: ControlChannel m -> (SendPort (Message m ()), ReceivePort (Message m ())) -- | Creates a new ControlChannel. newControlChan :: (Serializable m) => Process (ControlChannel m) -- | The writable end of a ControlChannel. newtype ControlPort m ControlPort :: SendPort (Message m ()) -> ControlPort m [unPort] :: ControlPort m -> SendPort (Message m ()) -- | Obtain an opaque expression for communicating with a -- ControlChannel. channelControlPort :: ControlChannel m -> ControlPort m -- | Provides dispatch from cast and call messages to a typed handler. data Dispatcher s Dispatch :: (s -> Message a b -> Process (ProcessAction s)) -> Dispatcher s [dispatch] :: Dispatcher s -> s -> Message a b -> Process (ProcessAction s) DispatchIf :: (s -> Message a b -> Process (ProcessAction s)) -> (s -> Message a b -> Bool) -> Dispatcher s [dispatch] :: Dispatcher s -> s -> Message a b -> Process (ProcessAction s) [dispatchIf] :: Dispatcher s -> s -> Message a b -> Bool -- | Provides dispatch for channels and STM actions data ExternDispatcher s DispatchCC :: ReceivePort (Message a b) -> (s -> Message a b -> Process (ProcessAction s)) -> ExternDispatcher s [channel] :: ExternDispatcher s -> ReceivePort (Message a b) [dispatchChan] :: ExternDispatcher s -> s -> Message a b -> Process (ProcessAction s) DispatchSTM :: STM a -> (s -> a -> Process (ProcessAction s)) -> Match Message -> (forall m. (Message -> m) -> Match m) -> ExternDispatcher s [stmAction] :: ExternDispatcher s -> STM a [dispatchStm] :: ExternDispatcher s -> s -> a -> Process (ProcessAction s) [matchStm] :: ExternDispatcher s -> Match Message [matchAnyStm] :: ExternDispatcher s -> forall m. (Message -> m) -> Match m -- | Provides dispatch for any input, returns Nothing for unhandled -- messages. data DeferredDispatcher s DeferredDispatcher :: (s -> Message -> Process (Maybe (ProcessAction s))) -> DeferredDispatcher s [dispatchInfo] :: DeferredDispatcher s -> s -> Message -> Process (Maybe (ProcessAction s)) -- | Provides dispatch for any exit signal - returns Nothing for -- unhandled exceptions data ExitSignalDispatcher s ExitSignalDispatcher :: (s -> ProcessId -> Message -> Process (Maybe (ProcessAction s))) -> ExitSignalDispatcher s [dispatchExit] :: ExitSignalDispatcher s -> s -> ProcessId -> Message -> Process (Maybe (ProcessAction s)) -- | Defines the means of dispatching inbound messages to a handler class MessageMatcher d matchDispatch :: MessageMatcher d => UnhandledMessagePolicy -> s -> d s -> Match (ProcessAction s) -- | Defines the means of dispatching messages from external channels (e.g. -- those defined in terms of ControlChannel, and STM actions) to a -- handler. class ExternMatcher d matchExtern :: ExternMatcher d => UnhandledMessagePolicy -> s -> d s -> Match Message matchMapExtern :: forall m s. ExternMatcher d => UnhandledMessagePolicy -> s -> (Message -> m) -> d s -> Match m -- | Message type used internally by the call, cast, and rpcChan -- APIs. data Message a b CastMessage :: a -> Message a b CallMessage :: a -> (CallRef b) -> Message a b ChanMessage :: a -> (SendPort b) -> Message a b -- | Response type for the call API data CallResponse a CallResponse :: a -> CallId -> CallResponse a -- | wrapper for a MonitorRef type CallId = MonitorRef -- | Wraps a consumer of the call API newtype CallRef a CallRef :: (Recipient, CallId) -> CallRef a [unCaller] :: CallRef a -> (Recipient, CallId) -- | Sent to a consumer of the call API when a server filter -- expression explicitly rejects an incoming call message. data CallRejected CallRejected :: String -> CallId -> CallRejected -- | Creates a CallRef for the given Recipient and -- CallId makeRef :: Recipient -> CallId -> CallRef a -- | Retrieve the Recipient from a Message. If the -- supplied message is a cast or chan message will evaluate -- to Nothing, otherwise Just ref. caller :: forall a b. Message a b -> Maybe Recipient -- | Reject a call message with the supplied string. Sends -- CallRejected to the recipient if the input is a -- CallMessage, otherwise has no side effects. rejectToCaller :: forall a b. Message a b -> String -> Process () -- | Retrieve the Recipient for a CallRef. recipient :: CallRef a -> Recipient -- | Retrieve the CallId for a CallRef. tag :: CallRef a -> CallId -- | The send part of the call client-server interaction. The -- resulting CallRef can be used to identify the corrolary -- response message (if one is sent by the server), and is unique to this -- call-reply pair. initCall :: forall s a b. (Addressable s, Serializable a, Serializable b) => s -> a -> Process (CallRef b) -- | Version of initCall that utilises "unsafeSendTo". unsafeInitCall :: forall s a b. (Addressable s, NFSerializable a, NFSerializable b) => s -> a -> Process (CallRef b) -- | Wait on the server's response after an "initCall" has been previously -- been sent. -- -- This function does not trap asynchronous exceptions. waitResponse :: forall b. (Serializable b) => Maybe TimeInterval -> CallRef b -> Process (Maybe (Either ExitReason b)) instance GHC.Base.Applicative (Control.Distributed.Process.ManagedProcess.Internal.Types.GenProcess s) instance Control.Monad.Fix.MonadFix (Control.Distributed.Process.ManagedProcess.Internal.Types.GenProcess s) instance Control.Monad.IO.Class.MonadIO (Control.Distributed.Process.ManagedProcess.Internal.Types.GenProcess s) instance Control.Monad.State.Class.MonadState (Control.Distributed.Process.ManagedProcess.Internal.Types.State s) (Control.Distributed.Process.ManagedProcess.Internal.Types.GenProcess s) instance GHC.Base.Monad (Control.Distributed.Process.ManagedProcess.Internal.Types.GenProcess s) instance GHC.Base.Functor (Control.Distributed.Process.ManagedProcess.Internal.Types.GenProcess s) instance GHC.Classes.Eq Control.Distributed.Process.ManagedProcess.Internal.Types.UnhandledMessagePolicy instance GHC.Show.Show Control.Distributed.Process.ManagedProcess.Internal.Types.UnhandledMessagePolicy instance GHC.Show.Show (Control.Distributed.Process.ManagedProcess.Internal.Types.ControlPort m) instance GHC.Classes.Eq Control.Distributed.Process.ManagedProcess.Internal.Types.CallRejected instance GHC.Show.Show Control.Distributed.Process.ManagedProcess.Internal.Types.CallRejected instance GHC.Generics.Generic Control.Distributed.Process.ManagedProcess.Internal.Types.CallRejected instance GHC.Generics.Generic (Control.Distributed.Process.ManagedProcess.Internal.Types.CallResponse a) instance GHC.Generics.Generic (Control.Distributed.Process.ManagedProcess.Internal.Types.Message a b) instance GHC.Generics.Generic (Control.Distributed.Process.ManagedProcess.Internal.Types.CallRef a) instance GHC.Show.Show (Control.Distributed.Process.ManagedProcess.Internal.Types.CallRef a) instance GHC.Classes.Eq (Control.Distributed.Process.ManagedProcess.Internal.Types.CallRef a) instance (GHC.Classes.Eq a, GHC.Classes.Eq b) => GHC.Classes.Eq (Control.Distributed.Process.ManagedProcess.Internal.Types.Message a b) instance (GHC.Show.Show a, GHC.Show.Show b) => GHC.Show.Show (Control.Distributed.Process.ManagedProcess.Internal.Types.Message a b) instance GHC.Classes.Eq a => GHC.Classes.Eq (Control.Distributed.Process.ManagedProcess.Internal.Types.CallResponse a) instance GHC.Show.Show a => GHC.Show.Show (Control.Distributed.Process.ManagedProcess.Internal.Types.CallResponse a) instance Control.Distributed.Process.Serializable.Serializable m => Data.Binary.Class.Binary (Control.Distributed.Process.ManagedProcess.Internal.Types.ControlPort m) instance Data.Binary.Class.Binary (Control.Distributed.Process.ManagedProcess.Internal.Types.CallRef a) instance Control.DeepSeq.NFData (Control.Distributed.Process.ManagedProcess.Internal.Types.CallRef a) instance (Control.Distributed.Process.Serializable.Serializable a, Control.Distributed.Process.Serializable.Serializable b) => Data.Binary.Class.Binary (Control.Distributed.Process.ManagedProcess.Internal.Types.Message a b) instance (Control.Distributed.Process.Extras.Internal.Types.NFSerializable a, Control.Distributed.Process.Extras.Internal.Types.NFSerializable b) => Control.DeepSeq.NFData (Control.Distributed.Process.ManagedProcess.Internal.Types.Message a b) instance Control.Distributed.Process.Serializable.Serializable a => Data.Binary.Class.Binary (Control.Distributed.Process.ManagedProcess.Internal.Types.CallResponse a) instance Control.Distributed.Process.Extras.Internal.Types.NFSerializable a => Control.DeepSeq.NFData (Control.Distributed.Process.ManagedProcess.Internal.Types.CallResponse a) instance Data.Binary.Class.Binary Control.Distributed.Process.ManagedProcess.Internal.Types.CallRejected instance Control.DeepSeq.NFData Control.Distributed.Process.ManagedProcess.Internal.Types.CallRejected instance Control.Distributed.Process.Extras.Internal.Types.Resolvable (Control.Distributed.Process.ManagedProcess.Internal.Types.CallRef a) instance Control.Distributed.Process.Extras.Internal.Types.Routable (Control.Distributed.Process.ManagedProcess.Internal.Types.CallRef a) instance Control.Monad.Catch.MonadThrow (Control.Distributed.Process.ManagedProcess.Internal.Types.GenProcess s) instance Control.Monad.Catch.MonadCatch (Control.Distributed.Process.ManagedProcess.Internal.Types.GenProcess s) instance Control.Monad.Catch.MonadMask (Control.Distributed.Process.ManagedProcess.Internal.Types.GenProcess s) instance GHC.Classes.Eq (Control.Distributed.Process.ManagedProcess.Internal.Types.ControlPort m) instance Control.Distributed.Process.ManagedProcess.Internal.Types.MessageMatcher Control.Distributed.Process.ManagedProcess.Internal.Types.Dispatcher instance Control.Distributed.Process.ManagedProcess.Internal.Types.MessageMatcher Control.Distributed.Process.ManagedProcess.Internal.Types.ExternDispatcher instance Control.Distributed.Process.ManagedProcess.Internal.Types.ExternMatcher Control.Distributed.Process.ManagedProcess.Internal.Types.ExternDispatcher -- | The Server Portion of the Managed Process API. module Control.Distributed.Process.ManagedProcess.Server -- | Creates a Condition from a function that takes a process state -- a and an input message b and returns a Bool -- indicating whether the associated handler should run. condition :: forall a b. (Serializable a, Serializable b) => (a -> b -> Bool) -> Condition a b -- | Create a Condition from a function that takes a process state -- a and returns a Bool indicating whether the associated -- handler should run. state :: forall s m. (Serializable m) => (s -> Bool) -> Condition s m -- | Creates a Condition from a function that takes an input message -- m and returns a Bool indicating whether the associated -- handler should run. input :: forall s m. (Serializable m) => (m -> Bool) -> Condition s m -- | Instructs the process to send a reply and continue running. reply :: (Serializable r) => r -> s -> Reply r s -- | Instructs the process to send a reply and evaluate the -- ProcessAction. replyWith :: (Serializable r) => r -> ProcessAction s -> Reply r s -- | Instructs the process to skip sending a reply and evaluate a -- ProcessAction noReply :: (Serializable r) => ProcessAction s -> Reply r s -- | Instructs the process to continue running and receiving messages. continue :: s -> Action s -- | Instructs the process loop to wait for incoming messages until -- Delay is exceeded. If no messages are handled during this -- period, the timeout handler will be called. Note that this -- alters the process timeout permanently such that the given -- Delay will remain in use until changed. -- -- Note that timeoutAfter NoDelay will cause the timeout handler -- to execute immediately if no messages are present in the process' -- mailbox. timeoutAfter :: Delay -> s -> Action s -- | Instructs the process to hibernate for the given -- TimeInterval. Note that no messages will be removed from the -- mailbox until after hibernation has ceased. This is equivalent to -- calling threadDelay. hibernate :: TimeInterval -> s -> Process (ProcessAction s) -- | Instructs the process to terminate, giving the supplied reason. If a -- valid shutdownHandler is installed, it will be called with the -- ExitReason returned from this call, along with the process -- state. stop :: ExitReason -> Action s -- | As stop, but provides an updated state for the shutdown -- handler. stopWith :: s -> ExitReason -> Action s -- | Sends a reply explicitly to a caller. -- --
--   replyTo = sendTo
--   
replyTo :: (Serializable m) => CallRef m -> m -> Process () -- | Sends a reply to a SendPort (for use in handleRpcChan et -- al). -- --
--   replyChan = sendChan
--   
replyChan :: (Serializable m) => SendPort m -> m -> Process () -- | Reject the message we're currently handling. reject :: forall r s. s -> String -> Reply r s -- | Reject the message we're currently handling, giving an explicit -- reason. rejectWith :: forall r m s. (Show r) => s -> r -> Reply m s -- | The server loop will execute against the supplied -- ProcessDefinition, allowing the process to change its behaviour -- (in terms of message handlers, exit handling, termination, unhandled -- message policy, etc) become :: forall s. ProcessDefinition s -> s -> Action s -- | Continue without giving a reply to the caller - equivalent to -- continue, but usable in a callback passed to the -- handleCall family of functions. noReply_ :: forall s r. (Serializable r) => s -> Reply r s -- | Halt process execution during a call handler, without paying any -- attention to the expected return type. haltNoReply_ :: Serializable r => ExitReason -> Reply r s -- | Version of continue that can be used in handlers that ignore -- process state. continue_ :: s -> Action s -- | Version of timeoutAfter that can be used in handlers that -- ignore process state. -- --
--   action (\(TimeoutPlease duration) -> timeoutAfter_ duration)
--   
timeoutAfter_ :: StatelessHandler s Delay -- | Version of hibernate that can be used in handlers that ignore -- process state. -- --
--   action (\(HibernatePlease delay) -> hibernate_ delay)
--   
hibernate_ :: StatelessHandler s TimeInterval -- | Version of stop that can be used in handlers that ignore -- process state. -- --
--   action (\ClientError -> stop_ ExitNormal)
--   
stop_ :: StatelessHandler s ExitReason -- | Constructs a call handler from a function in the -- Process monad. > handleCall = handleCallIf (const True) handleCall :: (Serializable a, Serializable b) => CallHandler s a b -> Dispatcher s -- | Constructs a call handler from an ordinary function in the -- Process monad. Given a function f :: (s -> a -> -- Process (ProcessReply b s)), the expression handleCall f -- will yield a Dispatcher for inclusion in a Behaviour -- specification for the GenProcess. Messages are only dispatched -- to the handler if the supplied condition evaluates to True. handleCallIf :: forall s a b. (Serializable a, Serializable b) => Condition s a -> CallHandler s a b -> Dispatcher s -- | As handleCall but passes the CallRef to the handler -- function. This can be useful if you wish to reply later to the -- caller by, e.g., spawning a process to do some work and have it -- replyTo caller response out of band. In this case the -- callback can pass the CallRef to the worker (or stash it away -- itself) and return noReply. handleCallFrom :: forall s a b. (Serializable a, Serializable b) => DeferredCallHandler s a b -> Dispatcher s -- | As handleCallFrom but only runs the handler if the supplied -- Condition evaluates to True. handleCallFromIf :: forall s a b. (Serializable a, Serializable b) => Condition s a -> DeferredCallHandler s a b -> Dispatcher s -- | Creates a handler for a typed channel RPC style interaction. -- The handler takes a SendPort b to reply to, the initial input -- and evaluates to a ProcessAction. It is the handler code's -- responsibility to send the reply to the SendPort. handleRpcChan :: forall s a b. (Serializable a, Serializable b) => ChannelHandler s a b -> Dispatcher s -- | As handleRpcChan, but only evaluates the handler if the -- supplied condition is met. handleRpcChanIf :: forall s a b. (Serializable a, Serializable b) => Condition s a -> ChannelHandler s a b -> Dispatcher s -- | Constructs a cast handler from an ordinary function in the -- Process monad. > handleCast = handleCastIf (const True) handleCast :: (Serializable a) => CastHandler s a -> Dispatcher s -- | Constructs a cast handler from an ordinary function in the -- Process monad. Given a function f :: (s -> a -> -- Process (ProcessAction s)), the expression handleCall f -- will yield a Dispatcher for inclusion in a Behaviour -- specification for the GenProcess. handleCastIf :: forall s a. (Serializable a) => Condition s a -> CastHandler s a -> Dispatcher s -- | Creates a generic input handler (i.e., for received messages that are -- not sent using the cast or call APIs) from an -- ordinary function in the Process monad. handleInfo :: forall s a. (Serializable a) => ActionHandler s a -> DeferredDispatcher s -- | Handle completely raw input messages. handleRaw :: forall s. ActionHandler s Message -> DeferredDispatcher s -- | Constructs a handler for both call and cast messages. -- handleDispatch = handleDispatchIf (const True) handleDispatch :: forall s a. (Serializable a) => ActionHandler s a -> Dispatcher s -- | Constructs a handler for both call and cast messages. -- Messages are only dispatched to the handler if the supplied condition -- evaluates to True. Handlers defined in this way have no -- access to the call context (if one exists) and cannot therefore reply -- to calls. handleDispatchIf :: forall s a. (Serializable a) => Condition s a -> ActionHandler s a -> Dispatcher s -- | Creates an exit handler scoped to the execution of any and all -- the registered call, cast and info handlers for the process. handleExit :: forall s a. (Serializable a) => (ProcessId -> ActionHandler s a) -> ExitSignalDispatcher s -- | Conditional version of handleExit handleExitIf :: forall s a. (Serializable a) => (s -> a -> Bool) -> (ProcessId -> ActionHandler s a) -> ExitSignalDispatcher s -- | Constructs an action handler. Like handleDispatch this -- can handle both cast and call messages, but you -- won't know which you're dealing with. This can be useful where certain -- inputs require a definite action, such as stopping the server, without -- concern for the state (e.g., when stopping we need only decide to -- stop, as the terminate handler can deal with state cleanup etc). For -- example: -- --
--   action (MyCriticalSignal -> stop_ ExitNormal)
--   
action :: forall s a. (Serializable a) => StatelessHandler s a -> Dispatcher s -- | Constructs a call handler from a function in the -- Process monad. The handler expression returns the reply, and -- the action will be set to continue. -- --
--   handleCall_ = handleCallIf_ $ input (const True)
--   
handleCall_ :: (Serializable a, Serializable b) => (a -> Process b) -> Dispatcher s -- | Constructs a call handler from an ordinary function in the -- Process monad. This variant ignores the state argument present -- in handleCall and handleCallIf and is therefore useful -- in a stateless server. Messges are only dispatched to the handler if -- the supplied condition evaluates to True -- -- See handleCall handleCallIf_ :: forall s a b. (Serializable a, Serializable b) => Condition s a -> (a -> Process b) -> Dispatcher s -- | A variant of handleCallFrom_ that ignores the state argument. handleCallFrom_ :: forall s a b. (Serializable a, Serializable b) => StatelessCallHandler s a b -> Dispatcher s -- | A variant of handleCallFromIf that ignores the state argument. handleCallFromIf_ :: forall s a b. (Serializable a, Serializable b) => Condition s a -> StatelessCallHandler s a b -> Dispatcher s -- | A variant of handleRpcChan that ignores the state argument. handleRpcChan_ :: forall s a b. (Serializable a, Serializable b) => StatelessChannelHandler s a b -> Dispatcher s -- | A variant of handleRpcChanIf that ignores the state argument. handleRpcChanIf_ :: forall s a b. (Serializable a, Serializable b) => Condition s a -> StatelessChannelHandler s a b -> Dispatcher s -- | Version of handleCast that ignores the server state. handleCast_ :: (Serializable a) => StatelessHandler s a -> Dispatcher s -- | Version of handleCastIf that ignores the server state. handleCastIf_ :: forall s a. (Serializable a) => Condition s a -> StatelessHandler s a -> Dispatcher s -- | Constructs a control channel handler from a function in the -- Process monad. The handler expression returns no reply, and the -- control message is treated in the same fashion as a -- cast. -- --
--   handleControlChan = handleControlChanIf $ input (const True)
--   
handleControlChan :: forall s a. (Serializable a) => ControlChannel a -> ActionHandler s a -> ExternDispatcher s -- | Version of handleControlChan that ignores the server state. handleControlChan_ :: forall s a. (Serializable a) => ControlChannel a -> StatelessHandler s a -> ExternDispatcher s -- | Creates a generic input handler for STM actions, from an -- ordinary function in the Process monad. The STM a -- action tells the server how to read inputs, which when presented are -- passed to the handler in the same manner as handleInfo -- messages would be. -- -- Note that messages sent to the server's mailbox will never match this -- handler, only data arriving via the STM a action will. -- -- Notably, this kind of handler can be used to pass non-serialisable -- data to a server process. In such situations, the programmer is -- responsible for managing the underlying STM infrastructure, -- and the server simply composes the STM a action with the -- other reads on its mailbox, using the underlying matchSTM API -- from distributed-process. -- -- NB: this function cannot be used with a prioristised process -- definition. handleExternal :: forall s a. (Serializable a) => STM a -> ActionHandler s a -> ExternDispatcher s -- | Version of handleExternal that ignores state. handleExternal_ :: forall s a. (Serializable a) => STM a -> StatelessHandler s a -> ExternDispatcher s -- | Handle call style API interactions using arbitrary STM -- actions. -- -- The usual CallHandler is preceded by an stm action that, when -- evaluated, yields a value, and a second expression that is used to -- send a reply back to the caller. The corrolary client API is -- callSTM. handleCallExternal :: forall s r w. (Serializable r) => STM r -> (w -> STM ()) -> CallHandler s r w -> ExternDispatcher s -- | A safe variant of the Server Portion of the Managed -- Process API. Most of these operations have the same names as -- similar operations in the impure Server module (re-exported -- by the primary API in ManagedProcess). To remove the -- ambiguity, some combination of either qualification and/or the -- hiding clause will be required. -- -- -- -- The idea behind this module is to provide safe callbacks, i.e., -- server code that is free from side effects. This safety is enforced by -- the type system via the RestrictedProcess monad. A StateT -- interface is provided for code running in the -- RestrictedProcess monad, so that server side state can be -- managed safely without resorting to IO (or code running in the -- Process monad). module Control.Distributed.Process.ManagedProcess.Server.Restricted -- | Restricted (i.e., pure, free from side effects) execution environment -- for callcastinfo handlers to execute in. data RestrictedProcess s a -- | The result of a call handler's execution. data Result a -- | reply with the given term Reply :: a -> Result a -- | reply with the given term and enter timeout Timeout :: Delay -> a -> Result a -- | reply with the given term and hibernate Hibernate :: TimeInterval -> a -> Result a -- | stop the process with the given reason Stop :: ExitReason -> Result a -- | The result of a safe cast handler's execution. data RestrictedAction -- | continue executing RestrictedContinue :: RestrictedAction -- | timeout if no messages are received RestrictedTimeout :: Delay -> RestrictedAction -- | hibernate (i.e., sleep) RestrictedHibernate :: TimeInterval -> RestrictedAction -- | stop/terminate the server process RestrictedStop :: ExitReason -> RestrictedAction -- | A version of -- "Control.Distributed.Process.ManagedProcess.Server.handleCall" that -- takes a handler which executes in RestrictedProcess. handleCall :: forall s a b. (Serializable a, Serializable b) => (a -> RestrictedProcess s (Result b)) -> Dispatcher s -- | A version of -- "Control.Distributed.Process.ManagedProcess.Server.handleCallIf" that -- takes a handler which executes in RestrictedProcess. handleCallIf :: forall s a b. (Serializable a, Serializable b) => Condition s a -> (a -> RestrictedProcess s (Result b)) -> Dispatcher s -- | A version of -- "Control.Distributed.Process.ManagedProcess.Server.handleCast" that -- takes a handler which executes in RestrictedProcess. handleCast :: forall s a. (Serializable a) => (a -> RestrictedProcess s RestrictedAction) -> Dispatcher s -- | A version of -- "Control.Distributed.Process.ManagedProcess.Server.handleCastIf" that -- takes a handler which executes in RestrictedProcess. handleCastIf :: forall s a. (Serializable a) => Condition s a -> (a -> RestrictedProcess s RestrictedAction) -> Dispatcher s -- | A version of -- "Control.Distributed.Process.ManagedProcess.Server.handleInfo" that -- takes a handler which executes in RestrictedProcess. handleInfo :: forall s a. (Serializable a) => (a -> RestrictedProcess s RestrictedAction) -> DeferredDispatcher s -- | Handle exit signals handleExit :: forall s a. (Serializable a) => (a -> RestrictedProcess s RestrictedAction) -> ExitSignalDispatcher s -- | Handle timeouts handleTimeout :: forall s. (Delay -> RestrictedProcess s RestrictedAction) -> TimeoutHandler s -- | Put a new process state state putState :: s -> RestrictedProcess s () -- | Get the current process state getState :: RestrictedProcess s s -- | Apply the given expression to the current process state modifyState :: (s -> s) -> RestrictedProcess s () -- | Instructs the process to send a reply and continue running. reply :: forall s r. (Serializable r) => r -> RestrictedProcess s (Result r) -- | Continue without giving a reply to the caller - equivalent to -- continue, but usable in a callback passed to the -- handleCall family of functions. noReply :: forall s r. (Serializable r) => Result r -> RestrictedProcess s (Result r) -- | Halt process execution during a call handler, without paying any -- attention to the expected return type. haltNoReply :: forall s r. (Serializable r) => ExitReason -> RestrictedProcess s (Result r) -- | Instructs the process to continue running and receiving messages. continue :: forall s. RestrictedProcess s RestrictedAction -- | Instructs the process loop to wait for incoming messages until -- Delay is exceeded. If no messages are handled during this -- period, the timeout handler will be called. Note that this -- alters the process timeout permanently such that the given -- Delay will remain in use until changed. timeoutAfter :: forall s. Delay -> RestrictedProcess s RestrictedAction -- | Instructs the process to hibernate for the given -- TimeInterval. Note that no messages will be removed from the -- mailbox until after hibernation has ceased. This is equivalent to -- evaluating liftIO . threadDelay. hibernate :: forall s. TimeInterval -> RestrictedProcess s RestrictedAction -- | Instructs the process to terminate, giving the supplied reason. If a -- valid shutdownHandler is installed, it will be called with the -- ExitReason returned from this call, along with the process -- state. stop :: forall s. ExitReason -> RestrictedProcess s RestrictedAction -- | Log a trace message using the underlying Process's say say :: String -> RestrictedProcess s () instance GHC.Base.Applicative (Control.Distributed.Process.ManagedProcess.Server.Restricted.RestrictedProcess s) instance Control.Monad.IO.Class.MonadIO (Control.Distributed.Process.ManagedProcess.Server.Restricted.RestrictedProcess s) instance Control.Monad.State.Class.MonadState s (Control.Distributed.Process.ManagedProcess.Server.Restricted.RestrictedProcess s) instance GHC.Base.Monad (Control.Distributed.Process.ManagedProcess.Server.Restricted.RestrictedProcess s) instance GHC.Base.Functor (Control.Distributed.Process.ManagedProcess.Server.Restricted.RestrictedProcess s) -- | Unsafe variant of the Managed Process Client API. This module -- implements the client portion of a Managed Process using the unsafe -- variants of cloud haskell's messaging primitives. It relies on the -- -extras implementation of UnsafePrimitives, which forces -- evaluation for types that provide an NFData instance. Direct -- use of the underlying unsafe primitives (from the distributed-process -- library) without NFData instances is unsupported. -- -- IMPORTANT NOTE: As per the platform documentation, it is not possible -- to guarantee that an NFData instance will force -- evaluation in the same way that a Binary instance would (when -- encoding to a byte string). Please read the unsafe primitives -- documentation carefully and make sure you know what you're doing. You -- have been warned. -- -- See Control.Distributed.Process.Extras. See -- Control.Distributed.Process.Extras.UnsafePrimitives. See -- Control.Distributed.Process.UnsafePrimitives. module Control.Distributed.Process.ManagedProcess.UnsafeClient -- | Send a control message over a ControlPort. This version of -- shutdown uses unsafe primitives. sendControlMessage :: Serializable m => ControlPort m -> m -> Process () -- | Send a signal instructing the process to terminate. This version of -- shutdown uses unsafe primitives. shutdown :: ProcessId -> Process () -- | Make a synchronous call - uses unsafe primitives. call :: forall s a b. (Addressable s, NFSerializable a, NFSerializable b) => s -> a -> Process b -- | Safe version of call that returns information about the error -- if the operation fails - uses unsafe primitives. safeCall :: forall s a b. (Addressable s, NFSerializable a, NFSerializable b) => s -> a -> Process (Either ExitReason b) -- | Version of safeCall that returns Nothing if the -- operation fails. Uses unsafe primitives. tryCall :: forall s a b. (Addressable s, NFSerializable a, NFSerializable b) => s -> a -> Process (Maybe b) -- | Make a synchronous call, but timeout and return Nothing if a -- reply is not received within the specified time interval - uses -- unsafe primitives. callTimeout :: forall s a b. (Addressable s, NFSerializable a, NFSerializable b) => s -> a -> TimeInterval -> Process (Maybe b) -- | Block for TimeInterval waiting for any matching -- CallResponse flushPendingCalls :: forall b. (NFSerializable b) => TimeInterval -> (b -> Process b) -> Process (Maybe b) -- | Invokes call out of band, and returns an "async handle." -- Uses unsafe primitives. callAsync :: forall s a b. (Addressable s, NFSerializable a, NFSerializable b) => s -> a -> Process (Async b) -- | Sends a cast message to the server identified by -- server - uses unsafe primitives. cast :: forall a m. (Addressable a, NFSerializable m) => a -> m -> Process () -- | Sends a channel message to the server and returns a -- ReceivePort - uses unsafe primitives. callChan :: forall s a b. (Addressable s, NFSerializable a, NFSerializable b) => s -> a -> Process (ReceivePort b) -- | A synchronous version of callChan. syncCallChan :: forall s a b. (Addressable s, NFSerializable a, NFSerializable b) => s -> a -> Process b -- | A safe version of syncCallChan, which returns Left -- ExitReason if the call fails. syncSafeCallChan :: forall s a b. (Addressable s, NFSerializable a, NFSerializable b) => s -> a -> Process (Either ExitReason b) -- | This is the Process implementation of a managed -- process module Control.Distributed.Process.ManagedProcess.Internal.GenProcess -- | Managed process loop. -- -- Evaluating this function will cause the caller to enter a server loop, -- constantly reading messages from its mailbox (and/or other supplied -- control planes) and passing these to handler functions in the supplied -- process definition. Only when it is determined that the server process -- should terminate - either by the handlers deciding to stop the -- process, or by an unhandled exit signal or other form of failure -- condition (e.g. synchronous or asynchronous exceptions). recvLoop :: ProcessDefinition s -> s -> Delay -> Process ExitReason -- | Prioritised process loop. -- -- Evaluating this function will cause the caller to enter a server loop, -- constantly reading messages from its mailbox (and/or other supplied -- control planes) and passing these to handler functions in the supplied -- process definition. Only when it is determined that the server process -- should terminate - either by the handlers deciding to stop the -- process, or by an unhandled exit signal or other form of failure -- condition (e.g. synchronous or asynchronous exceptions). -- -- ensureIOManagerIsRunning before evaluating this loop... precvLoop :: PrioritisedProcessDefinition s -> s -> Delay -> Process ExitReason -- | The current (user supplied) timeout. currentTimeout :: GenProcess s Delay -- | The Timer for the system timeout. See drainTimeout. systemTimeout :: GenProcess s Timer -- | The Delay for the drainTimeout. drainTimeout :: GenProcess s Delay -- | Evaluates to the user defined state for the currently executing server -- loop. processState :: GenProcess s s -- | The ProcessDefinition for the current loop. processDefinition :: GenProcess s (ProcessDefinition s) -- | The list of filters for the current loop. processFilters :: GenProcess s ([DispatchFilter s]) -- | Evaluates to the UnhandledMessagePolicy for the current loop. processUnhandledMsgPolicy :: GenProcess s UnhandledMessagePolicy -- | Returns a read only view on the internal priority queue. processQueue :: GenProcess s [Message] -- | Evaluate the given function over the ProcessState s for the -- caller, and return the result. gets :: forall s a. (ProcessState s -> a) -> GenProcess s a -- | Modify our state and return a value (potentially from it). getAndModifyState :: (ProcessState s -> (ProcessState s, a)) -> GenProcess s a -- | Modify our state. modifyState :: (ProcessState s -> ProcessState s) -> GenProcess s () -- | Set the user timeout applied whilst a prioritised process loop is in a -- blocking receive. setUserTimeout :: Delay -> GenProcess s () -- | Set the current process state. setProcessState :: s -> GenProcess s () -- | StateT based monad for prioritised process loops. data GenProcess s a -- | Peek at the next available message in the internal priority queue, -- without removing it. peek :: GenProcess s (Maybe Message) -- | Push a message to the head of the internal priority queue. push :: forall s. Message -> GenProcess s () -- | Enqueue a message to the back of the internal priority queue. enqueue :: forall s. Message -> GenProcess s () -- | Dequeue a message from the internal priority queue. dequeue :: GenProcess s (Maybe Message) -- | Add a user timer, bound to the given datum. addUserTimer :: Timer -> Message -> GenProcess s TimerKey -- | Remove a user timer, for the given key. removeUserTimer :: TimerKey -> GenProcess s () -- | Evaluate an expression in the GenProcess monad. eval :: forall s. GenProcess s (ProcessAction s) -> Action s -- | Produce an Action s that, if it is the result of a handler, -- will cause the server loop to evaluate the supplied expression. This -- is given in the GenProcess monad, which is intended for -- internal use only. -- | Warning: This interface is intended for internal use only act :: forall s. GenProcess s () -> Action s -- | Starts a timer and adds it as a user timeout. -- | Warning: This interface is intended for internal use only runAfter :: forall s m. (Serializable m) => TimeInterval -> m -> GenProcess s () -- | Evaluate any matching info handler with the supplied datum -- after waiting for at least TimeInterval. The process state -- (for the resulting Action s) is also given and the process -- loop will go on as per Server.continue. -- -- Informally, evaluating this expression (such that the Action -- is given as the result of a handler or filter) will ensure that the -- supplied message (datum) is availble for processing no sooner than -- TimeInterval. -- -- Currently, this expression creates an Action that triggers -- immediate evaluation in the process loop before continuing with the -- given state. The process loop stores a user timeout for the -- given time interval, which is trigerred like a wait/drain timeout. -- This implementation is subject to change. evalAfter :: forall s m. (Serializable m) => TimeInterval -> m -> s -> Action s instance Control.Distributed.Process.ManagedProcess.Internal.GenProcess.DynMessageHandler Control.Distributed.Process.ManagedProcess.Internal.Types.Dispatcher instance Control.Distributed.Process.ManagedProcess.Internal.GenProcess.DynMessageHandler Control.Distributed.Process.ManagedProcess.Internal.Types.ExternDispatcher instance Control.Distributed.Process.ManagedProcess.Internal.GenProcess.DynMessageHandler Control.Distributed.Process.ManagedProcess.Internal.Types.DeferredDispatcher instance Control.Distributed.Process.ManagedProcess.Internal.GenProcess.DynFilterHandler Control.Distributed.Process.ManagedProcess.Internal.Types.DispatchFilter -- | The Server Portion of the Managed Process API, as presented by -- the GenProcess monad. These functions are generally intended -- for internal use, but the API is relatively stable and therefore they -- have been re-exported here for general use. Note that if you modify a -- process' internal state (especially that of the internal priority -- queue) then you are responsible for any alteratoin that makes to the -- semantics of your processes behaviour. -- -- See -- Control.Distributed.Process.ManagedProcess.Internal.GenProcess module Control.Distributed.Process.ManagedProcess.Server.Gen -- | Instructs the process to send a reply and continue running. reply :: forall r s. (Serializable r) => r -> GenProcess s (ProcessReply r s) -- | Instructs the process to send a reply and evaluate the -- ProcessAction. replyWith :: forall r s. (Serializable r) => r -> ProcessAction s -> GenProcess s (ProcessReply r s) -- | Instructs the process to skip sending a reply and evaluate a -- ProcessAction noReply :: (Serializable r) => ProcessAction s -> GenProcess s (ProcessReply r s) -- | Instructs the process to continue running and receiving messages. continue :: GenProcess s (ProcessAction s) -- | Instructs the process loop to wait for incoming messages until -- Delay is exceeded. If no messages are handled during this -- period, the timeout handler will be called. Note that this -- alters the process timeout permanently such that the given -- Delay will remain in use until changed. -- -- Note that timeoutAfter NoDelay will cause the timeout handler -- to execute immediately if no messages are present in the process' -- mailbox. timeoutAfter :: Delay -> GenProcess s (ProcessAction s) -- | Instructs the process to hibernate for the given -- TimeInterval. Note that no messages will be removed from the -- mailbox until after hibernation has ceased. This is equivalent to -- calling threadDelay. hibernate :: TimeInterval -> GenProcess s (ProcessAction s) -- | Instructs the process to terminate, giving the supplied reason. If a -- valid shutdownHandler is installed, it will be called with -- the ExitReason returned from this call, along with the process -- state. stop :: ExitReason -> GenProcess s (ProcessAction s) -- | Reject the message we're currently handling. reject :: forall r s. String -> GenProcess s (ProcessReply r s) -- | Reject the message we're currently handling, giving an explicit -- reason. rejectWith :: forall r m s. (Show r) => r -> GenProcess s (ProcessReply m s) -- | The server loop will execute against the supplied -- ProcessDefinition, allowing the process to change its behaviour -- (in terms of message handlers, exit handling, termination, unhandled -- message policy, etc) become :: forall s. ProcessDefinition s -> GenProcess s (ProcessAction s) -- | Halt process execution during a call handler, without paying any -- attention to the expected return type. haltNoReply :: forall s r. Serializable r => ExitReason -> GenProcess s (ProcessReply r s) -- | Lift an action in the Process monad to GenProcess. lift :: Process a -> GenProcess s a -- | Managed process loop. -- -- Evaluating this function will cause the caller to enter a server loop, -- constantly reading messages from its mailbox (and/or other supplied -- control planes) and passing these to handler functions in the supplied -- process definition. Only when it is determined that the server process -- should terminate - either by the handlers deciding to stop the -- process, or by an unhandled exit signal or other form of failure -- condition (e.g. synchronous or asynchronous exceptions). recvLoop :: ProcessDefinition s -> s -> Delay -> Process ExitReason -- | Prioritised process loop. -- -- Evaluating this function will cause the caller to enter a server loop, -- constantly reading messages from its mailbox (and/or other supplied -- control planes) and passing these to handler functions in the supplied -- process definition. Only when it is determined that the server process -- should terminate - either by the handlers deciding to stop the -- process, or by an unhandled exit signal or other form of failure -- condition (e.g. synchronous or asynchronous exceptions). -- -- ensureIOManagerIsRunning before evaluating this loop... precvLoop :: PrioritisedProcessDefinition s -> s -> Delay -> Process ExitReason -- | The current (user supplied) timeout. currentTimeout :: GenProcess s Delay -- | The Timer for the system timeout. See drainTimeout. systemTimeout :: GenProcess s Timer -- | The Delay for the drainTimeout. drainTimeout :: GenProcess s Delay -- | Evaluates to the user defined state for the currently executing server -- loop. processState :: GenProcess s s -- | The ProcessDefinition for the current loop. processDefinition :: GenProcess s (ProcessDefinition s) -- | The list of filters for the current loop. processFilters :: GenProcess s ([DispatchFilter s]) -- | Evaluates to the UnhandledMessagePolicy for the current loop. processUnhandledMsgPolicy :: GenProcess s UnhandledMessagePolicy -- | Returns a read only view on the internal priority queue. processQueue :: GenProcess s [Message] -- | Evaluate the given function over the ProcessState s for the -- caller, and return the result. gets :: forall s a. (ProcessState s -> a) -> GenProcess s a -- | Modify our state and return a value (potentially from it). getAndModifyState :: (ProcessState s -> (ProcessState s, a)) -> GenProcess s a -- | Modify our state. modifyState :: (ProcessState s -> ProcessState s) -> GenProcess s () -- | Set the user timeout applied whilst a prioritised process loop is in a -- blocking receive. setUserTimeout :: Delay -> GenProcess s () -- | Set the current process state. setProcessState :: s -> GenProcess s () -- | StateT based monad for prioritised process loops. data GenProcess s a -- | Peek at the next available message in the internal priority queue, -- without removing it. peek :: GenProcess s (Maybe Message) -- | Push a message to the head of the internal priority queue. push :: forall s. Message -> GenProcess s () -- | Enqueue a message to the back of the internal priority queue. enqueue :: forall s. Message -> GenProcess s () -- | Dequeue a message from the internal priority queue. dequeue :: GenProcess s (Maybe Message) -- | Add a user timer, bound to the given datum. addUserTimer :: Timer -> Message -> GenProcess s TimerKey -- | Remove a user timer, for the given key. removeUserTimer :: TimerKey -> GenProcess s () -- | Evaluate an expression in the GenProcess monad. eval :: forall s. GenProcess s (ProcessAction s) -> Action s -- | Produce an Action s that, if it is the result of a handler, -- will cause the server loop to evaluate the supplied expression. This -- is given in the GenProcess monad, which is intended for -- internal use only. -- | Warning: This interface is intended for internal use only act :: forall s. GenProcess s () -> Action s -- | Starts a timer and adds it as a user timeout. -- | Warning: This interface is intended for internal use only runAfter :: forall s m. (Serializable m) => TimeInterval -> m -> GenProcess s () -- | Evaluate any matching info handler with the supplied datum -- after waiting for at least TimeInterval. The process state -- (for the resulting Action s) is also given and the process -- loop will go on as per Server.continue. -- -- Informally, evaluating this expression (such that the Action -- is given as the result of a handler or filter) will ensure that the -- supplied message (datum) is availble for processing no sooner than -- TimeInterval. -- -- Currently, this expression creates an Action that triggers -- immediate evaluation in the process loop before continuing with the -- given state. The process loop stores a user timeout for the -- given time interval, which is trigerred like a wait/drain timeout. -- This implementation is subject to change. evalAfter :: forall s m. (Serializable m) => TimeInterval -> m -> s -> Action s -- | The Prioritised Server portion of the Managed Process API. module Control.Distributed.Process.ManagedProcess.Server.Priority -- | Prioritise a call handler prioritiseCall :: forall s a b. (Serializable a, Serializable b) => (s -> a -> Priority b) -> DispatchPriority s -- | Prioritise a call handler, ignoring the server's state prioritiseCall_ :: forall s a b. (Serializable a, Serializable b) => (a -> Priority b) -> DispatchPriority s -- | Prioritise a cast handler prioritiseCast :: forall s a. (Serializable a) => (s -> a -> Priority ()) -> DispatchPriority s -- | Prioritise a cast handler, ignoring the server's state prioritiseCast_ :: forall s a. (Serializable a) => (a -> Priority ()) -> DispatchPriority s -- | Prioritise an info handler prioritiseInfo :: forall s a. (Serializable a) => (s -> a -> Priority ()) -> DispatchPriority s -- | Prioritise an info handler, ignoring the server's state prioritiseInfo_ :: forall s a. (Serializable a) => (a -> Priority ()) -> DispatchPriority s -- | Sets an explicit priority from 1..100. Values > 100 are rounded to -- 100, and values < 1 are set to 0. setPriority :: Int -> Priority m -- | Create a filter from a FilterHandler. check :: forall s. FilterHandler s -> DispatchFilter s -- | A raw filter (targetting raw messages). raw :: forall s. (s -> Message -> Process Bool) -> (s -> Message -> Process (Maybe (Filter s))) -> FilterHandler s -- | A raw filter that ignores the server state in its condition -- expression. raw_ :: forall s. (Message -> Process Bool) -> (s -> Message -> Process (Maybe (Filter s))) -> FilterHandler s -- | An API filter (targetting call, cast, and chan -- messages). api :: forall s m b. (Serializable m, Serializable b) => (s -> m -> Process Bool) -> (s -> Message m b -> Process (Filter s)) -> FilterHandler s -- | An API filter that ignores the server state in its condition -- expression. api_ :: forall m b s. (Serializable m, Serializable b) => (m -> Process Bool) -> (s -> Message m b -> Process (Filter s)) -> FilterHandler s -- | An info filter (targetting info messages of a specific type) info :: forall s m. (Serializable m) => (s -> m -> Process Bool) -> (s -> m -> Process (Filter s)) -> FilterHandler s -- | An info filter that ignores the server state in its condition -- expression. info_ :: forall s m. (Serializable m) => (m -> Process Bool) -> (s -> m -> Process (Filter s)) -> FilterHandler s -- | Refuse messages for which the given expression evaluates to -- True. refuse :: forall s m. (Serializable m) => (m -> Bool) -> DispatchFilter s -- | Create a filter expression that will reject all messages of a specific -- type. reject :: forall s m r. (Show r) => r -> s -> m -> Process (Filter s) -- | A version of reject that deals with API messages (i.e. -- call, cast, etc) and in the case of a call -- interaction, will reject the messages and reply to the sender -- accordingly (with CallRejected). rejectApi :: forall s m b r. (Show r, Serializable m, Serializable b) => r -> s -> Message m b -> Process (Filter s) -- | Modify the server state every time a message is recieved. store :: (s -> s) -> DispatchFilter s -- | Motify the server state when messages of a certain type arrive... storeM :: forall s m. (Serializable m) => (s -> m -> Process s) -> DispatchFilter s -- | Create a filter expression that will crash (i.e. stop) the server. crash :: forall s. s -> ExitReason -> Process (Filter s) -- | Ensure that the server state is consistent with the given expression -- each time a message arrives/is processed. If the expression evaluates -- to True then the filter will evaluate to FilterOk, -- otherwise FilterStop (which will cause the server loop to stop -- with ExitOther filterFail). ensure :: forall s. (s -> Bool) -> DispatchFilter s -- | As ensure but runs in the Process monad, and matches -- only inputs of type m. ensureM :: forall s m. (Serializable m) => (s -> m -> Process Bool) -> DispatchFilter s -- | Given as the result of evaluating a DispatchFilter. This type -- is intended for internal use. For an API for working with filters, see -- Control.Distributed.Process.ManagedProcess.Priority. data Filter s -- | Provides dispatch from a variety of inputs to a typed filter handler. data DispatchFilter s -- | Given a check expression, if it evaluates to True for some -- input, then do not dequeue the message until after any matching -- handlers have successfully run, or the the unhandled message policy is -- chosen if none match. Thus, if an exit signal (async exception) -- terminates execution of a handler, and we have an installed exit -- handler which allows the process to continue running, we will retry -- the input in question since it has not been fully dequeued prior to -- the exit signal arriving. safe :: forall s m. (Serializable m) => (s -> m -> Bool) -> DispatchFilter s -- | As safe, but as applied to api messages (i.e. those originating -- from call as cast client interactions). apiSafe :: forall s m b. (Serializable m, Serializable b) => (s -> m -> Maybe b -> Bool) -> DispatchFilter s -- | As safe, but matches on a raw message. safely :: forall s. (s -> Message -> Bool) -> DispatchFilter s -- | Message type used internally by the call, cast, and rpcChan -- APIs. data Message a b -- | Evaluate any matching info handler with the supplied datum -- after waiting for at least TimeInterval. The process state -- (for the resulting Action s) is also given and the process -- loop will go on as per Server.continue. -- -- Informally, evaluating this expression (such that the Action -- is given as the result of a handler or filter) will ensure that the -- supplied message (datum) is availble for processing no sooner than -- TimeInterval. -- -- Currently, this expression creates an Action that triggers -- immediate evaluation in the process loop before continuing with the -- given state. The process loop stores a user timeout for the -- given time interval, which is trigerred like a wait/drain timeout. -- This implementation is subject to change. evalAfter :: forall s m. (Serializable m) => TimeInterval -> m -> s -> Action s -- | The current (user supplied) timeout. currentTimeout :: GenProcess s Delay -- | Evaluates to the user defined state for the currently executing server -- loop. processState :: GenProcess s s -- | The ProcessDefinition for the current loop. processDefinition :: GenProcess s (ProcessDefinition s) -- | The list of filters for the current loop. processFilters :: GenProcess s ([DispatchFilter s]) -- | Evaluates to the UnhandledMessagePolicy for the current loop. processUnhandledMsgPolicy :: GenProcess s UnhandledMessagePolicy -- | Set the user timeout applied whilst a prioritised process loop is in a -- blocking receive. setUserTimeout :: Delay -> GenProcess s () -- | Set the current process state. setProcessState :: s -> GenProcess s () -- | StateT based monad for prioritised process loops. data GenProcess s a -- | Peek at the next available message in the internal priority queue, -- without removing it. peek :: GenProcess s (Maybe Message) -- | Push a message to the head of the internal priority queue. push :: forall s. Message -> GenProcess s () -- | Add a user timer, bound to the given datum. addUserTimer :: Timer -> Message -> GenProcess s TimerKey -- | Produce an Action s that, if it is the result of a handler, -- will cause the server loop to evaluate the supplied expression. This -- is given in the GenProcess monad, which is intended for -- internal use only. -- | Warning: This interface is intended for internal use only act :: forall s. GenProcess s () -> Action s -- | Starts a timer and adds it as a user timeout. -- | Warning: This interface is intended for internal use only runAfter :: forall s m. (Serializable m) => TimeInterval -> m -> GenProcess s () instance GHC.Show.Show Control.Distributed.Process.ManagedProcess.Server.Priority.RejectedByServer -- | The Client Portion of the Managed Process API. module Control.Distributed.Process.ManagedProcess.Client -- | Send a control message over a ControlPort. sendControlMessage :: Serializable m => ControlPort m -> m -> Process () -- | Send a signal instructing the process to terminate. The receive -- loop which manages the process mailbox will prioritise -- Shutdown signals higher than any other incoming messages, but -- the server might be busy (i.e., still in the process of excuting a -- handler) at the time of sending however, so the caller should not make -- any assumptions about the timeliness with which the shutdown signal -- will be handled. If responsiveness is important, a better approach -- might be to send an exit signal with Shutdown as the -- reason. An exit signal will interrupt any operation currently underway -- and force the running process to clean up and terminate. shutdown :: ProcessId -> Process () -- | Make a synchronous call - will block until a reply is received. The -- calling process will exit with ExitReason if the calls fails. -- -- NOTE: this function does not catch exceptions! call :: forall s a b. (Addressable s, Serializable a, Serializable b) => s -> a -> Process b -- | Safe version of call that returns information about the error -- if the operation fails. If the calling process dies (that is, forces -- itself to exit such that an exit signal arises with ExitOther -- String) then evaluation will return Left exitReason and -- the explanation will be stashed away as (ExitOther String). -- -- NOTE: this function does not catch exceptions! -- -- The safety of the name, comes from carefully handling -- situations in which the server dies while we're waiting for a reply. -- Notably, exit signals from other processes, kill signals, and both -- synchronous and asynchronous exceptions can still terminate the caller -- abruptly. To avoid this consider masking or evaluating within your own -- exception handling code. safeCall :: forall s a b. (Addressable s, Serializable a, Serializable b) => s -> a -> Process (Either ExitReason b) -- | Version of safeCall that returns Nothing if the -- operation fails. If you need information about *why* a call has failed -- then you should use safeCall or combine catchExit and -- call instead. -- -- NOTE: this function does not catch exceptions! -- -- In fact, this API handles fewer exceptions than it's relative, -- "safeCall". Notably, exit signals, kill signals, and both synchronous -- and asynchronous exceptions can still terminate the caller abruptly. -- To avoid this consider masking or evaluating within your own exception -- handling code (as mentioned above). tryCall :: forall s a b. (Addressable s, Serializable a, Serializable b) => s -> a -> Process (Maybe b) -- | Make a synchronous call, but timeout and return Nothing if a -- reply is not received within the specified time interval. -- -- If the result of the call is a failure (or the call was cancelled) -- then the calling process will exit, with the ExitReason given -- as the reason. If the call times out however, the semantics on the -- server side are undefined, i.e., the server may or may not -- successfully process the request and may (or may not) send a response -- at a later time. From the callers perspective, this is somewhat -- troublesome, since the call result cannot be decoded directly. In this -- case, the "flushPendingCalls" API may be used to attempt to -- receive the message later on, however this makes no attempt -- whatsoever to guarantee which call response will in fact be -- returned to the caller. In those semantics are unsuited to your -- application, you might choose to exit or die in case -- of a timeout, or alternatively, use the callAsync API and -- associated waitTimeout function (in the Async API), -- which takes a re-usable handle on which to wait (with timeouts) -- multiple times. callTimeout :: forall s a b. (Addressable s, Serializable a, Serializable b) => s -> a -> TimeInterval -> Process (Maybe b) -- | Attempt to flush out any pending call responses. flushPendingCalls :: forall b. (Serializable b) => TimeInterval -> (b -> Process b) -> Process (Maybe b) -- | Invokes call out of band, and returns an async -- handle. callAsync :: forall s a b. (Addressable s, Serializable a, Serializable b) => s -> a -> Process (Async b) -- | Sends a cast message to the server identified by -- server. The server will not send a response. Like Cloud -- Haskell's send primitive, cast is fully asynchronous and -- never fails - therefore casting to a non-existent (e.g., -- dead) server process will not generate an error. cast :: forall a m. (Addressable a, Serializable m) => a -> m -> Process () -- | Sends a channel message to the server and returns a -- ReceivePort on which the reponse can be delivered, if the -- server so chooses (i.e., the might ignore the request or crash). callChan :: forall s a b. (Addressable s, Serializable a, Serializable b) => s -> a -> Process (ReceivePort b) -- | A synchronous version of callChan. syncCallChan :: forall s a b. (Addressable s, Serializable a, Serializable b) => s -> a -> Process b -- | A safe version of syncCallChan, which returns Left -- ExitReason if the call fails. syncSafeCallChan :: forall s a b. (Addressable s, Serializable a, Serializable b) => s -> a -> Process (Either ExitReason b) -- | Manages an rpc-style interaction with a server process, using -- STM actions to read/write data. The server process is -- monitored for the duration of the call. The stm write -- expression is passed the input, and the read expression is evaluated -- and the result given as Right b or Left ExitReason -- if a monitor signal is detected whilst waiting. -- -- Note that the caller will exit (with ExitOther String) if the -- server address is un-resolvable. -- -- A note about scheduling and timing guarantees (or lack thereof): It is -- not possibly to guarantee the contents of ExitReason in cases -- where this API fails due to server exits/crashes. We establish a -- monitor prior to evaluating the stm writer action, however -- monitor is asychronous and we've no way to know whether or -- not the scheduler will allow monitor establishment to proceed first, -- or the stm transaction. As a result, assuming that your server process -- can diefailexit on evaluating the read end of the STM write we -- perform here (and we assume this is very likely, since we apply no -- safety rules and do not even worry about serializing thunks passed -- from the client's thread), it is just as likely that in the case of -- failure you will see a reason such as ExitOther -- DiedUnknownId due to the server process crashing before -- the node controller can establish a monitor. -- -- As unpleasant as this is, there's little we can do about it without -- making false assumptions about the runtime. Cloud Haskell's semantics -- guarantee us only that we will see some monitor signal in the -- event of a failure here. To provide a more robust error handling, you -- can catch/trap failures in the server process and return a wrapper -- reponse datum here instead. This will still be subject to the -- failure modes described above in cases where the server process exits -- abnormally, but that will at least allow the caller to differentiate -- between expected and exceptional failure conditions. callSTM :: forall s a b. (Addressable s) => s -> (a -> STM ()) -> STM b -> a -> Process (Either ExitReason b) -- | This module provides a high(er) level API for building complex -- Process implementations by abstracting out the management of -- the process' mailbox, reply/response handling, timeouts, process -- hiberation, error handling and shutdown/stop procedures. It is -- modelled along similar lines to OTP's gen_server API - -- http://www.erlang.org/doc/man/gen_server.html. -- -- In particular, a managed process will interoperate cleanly with -- the supervisor API in distributed-process-supervision. -- -- -- -- Once started, a managed process will consume messages from its -- mailbox and pass them on to user defined handlers based on the -- types received (mapped to those accepted by the handlers) and -- optionally by also evaluating user supplied predicates to determine -- which handler(s) should run. Each handler returns a -- ProcessAction which specifies how we should proceed. If none of -- the handlers is able to process a message (because their types are -- incompatible), then the unhandledMessagePolicy will be applied. -- -- The ProcessAction type defines the ways in which our process -- can respond to its inputs, whether by continuing to read incoming -- messages, setting an optional timeout, sleeping for a while, or -- stopping. The optional timeout behaves a little differently to the -- other process actions: If no messages are received within the -- specified time span, a user defined timeoutHandler will be -- called in order to determine the next action. -- -- The ProcessDefinition type also defines a -- shutdownHandler, which is called whenever the process exits, -- whether because a callback has returned stop as the next -- action, or as the result of unhandled exit signal or similar -- asynchronous exceptions thrown in (or to) the process itself. -- -- The handlers are split into groups: apiHandlers, -- infoHandlers, and extHandlers. -- -- -- -- Use serve for a process that sits reading its mailbox and -- generally behaves as you'd expect. Use pserve and -- PrioritisedProcessDefinition for a server that manages its -- mailbox more comprehensively and handles errors a bit differently. -- Both use the same client API. -- -- DO NOT mask in handler code, unless you can guarantee it won't be long -- running and absolutely won't block kill signals from a supervisor. -- -- Do look at the various API offerings, as there are several, at -- different levels of abstraction. -- -- -- -- Managed processes come in two flavours, with different runtime -- characteristics and (to some extent) semantics. These flavours are -- differentiated by the way in which they handle the server process -- mailbox - all client interactions remain the same. -- -- The vanilla managed process mailbox, provided by the -- serve API, is roughly akin to a tail recursive listen -- function that calls a list of passed in matchers. We might naively -- implement it roughly like this: -- --
--   loop :: stateT -> [(stateT -> Message -> Maybe stateT)] -> Process ()
--   loop state handlers = do
--     st2 <- receiveWait $ map (\d -> handleMessage (d state)) handlers
--     case st2 of
--       Nothing -> {- we're done serving -} return ()
--       Just s2 -> loop s2 handlers
--   
-- -- Obviously all the details have been ellided, but this is the essential -- premise behind a managed process loop. The process keeps -- reading from its mailbox indefinitely, until either a handler -- instructs it to stop, or an asynchronous exception (or exit signal - -- in the form of an async ProcessExitException) terminates it. -- This kind of mailbox has fairly intuitive runtime characteristics -- compared to a plain server process (i.e. one implemented -- without the use of this library): messages will pile up in its mailbox -- whilst handlers are running, and each handler will be checked against -- the mailbox based on the type of messages it recognises. We can -- potentially end up scanning a very large mailbox trying to match each -- handler, which can be a performance bottleneck depending on expected -- traffic patterns. -- -- For most simple server processes, this technique works well and is -- easy to reason about a use. See the sections on error and exit -- handling later on for more details about serve based managed -- processes. -- -- -- -- A prioritised mailbox serves two purposes. The first of these is to -- allow a managed process author to specify that certain classes of -- message should be prioritised by the server loop. This is achieved by -- draining the real process mailbox into an internal priority -- queue, and running the server's handlers repeatedly over its contents, -- which are dequeued in priority order. The obvious consequence of this -- approach leads to the second purpose (or the accidental side effect, -- depending on your point of view) of a prioritised mailbox, which is -- that we avoid scanning a large mailbox when searching for messages -- that match the handlers we anticipate running most frequently (or -- those messages that we deem most important). -- -- There are several consequences to this approach. One is that we do -- quite a bit more work to manage the process mailbox behind the scenes, -- therefore we have additional space overhead to consider (although we -- are also reducing the size of the mailbox, so there is some counter -- balance here). The other is that if we do not see the anticipated -- traffic patterns at runtime, then we might spend more time attempting -- to prioritise infrequent messages than we would have done simply -- receiving them! We do however, gain a degree of safety with regards -- message loss that the serve based vanilla mailbox cannot -- offer. See the sections on error and exit handling later on for more -- details about these. -- -- A Prioritised pserve loop maintains its internal state - -- including the user defined server state - in an IORef, -- ensuring it is held consistently between executions, even in the face -- of unhandled exceptions. -- -- -- -- A PrioritisedProcessDefintion combines the usual -- ProcessDefintion - containing the cast/call API, error, -- termination and info handlers - with a list of Priority -- entries, which are used at runtime to prioritise the server's inputs. -- Note that it is only messages which are prioritised; The server's -- various handlers are still evaluated in the order in which they are -- specified in the ProcessDefinition. -- -- Prioritisation does not guarantee that a prioritised message/type will -- be processed before other traffic - indeed doing so in a -- multi-threaded runtime would be very hard - but in the absence of -- races between multiple processes, if two messages are both present in -- the process' own mailbox, they will be applied to the -- ProcessDefinition's handlers in priority order. -- -- A prioritised process should probably be configured with a -- Priority list to be useful. Creating a prioritised process -- without any priorities could be a potential waste of computational -- resources, and it is worth thinking carefully about whether or not -- prioritisation is truly necessary in your design before choosing to -- use it. -- -- Using a prioritised process is as simple as calling pserve -- instead of serve, and passing an initialised -- PrioritisedProcessDefinition. -- -- -- -- Deliberate interactions with a managed process usually falls -- into one of two categories. A cast interaction involves a -- client sending a message asynchronously and the server handling this -- input. No reply is sent to the client. On the other hand, a -- call is a remote procedure call, where the client sends -- a message and waits for a reply from the server. -- -- All expressions given to apiHandlers have to conform to the -- cast or call protocol. The protocol (messaging) implementation -- is hidden from the user; API functions for creating user defined -- apiHandlers are given instead, which take expressions (i.e., -- a function or lambda expression) and create the appropriate -- Dispatcher for handling the cast (or call). -- -- These cast and call protocols are for dealing with expected -- inputs. They will usually form the explicit public API for the -- process, and be exposed by providing module level functions that defer -- to the cast or call client API, giving the process author an -- opportunity to enforce the correct input and response types. For -- example: -- --
--   {- Ask the server to add two numbers -}
--   add :: ProcessId -> Double -> Double -> Double
--   add pid x y = call pid (Add x y)
--   
-- -- Note here that the return type from the call is inferred and -- will not be enforced by the type system. If the server sent a -- different type back in the reply, then the caller might be blocked -- indefinitely! In fact, the result of mis-matching the expected return -- type (in the client facing API) with the actual type returned by the -- server is more severe in practise. The underlying types that implement -- the call protocol carry information about the expected return -- type. If there is a mismatch between the input and output types that -- the client API uses and those which the server declares it can handle, -- then the message will be considered unroutable - no handler will be -- executed against it and the unhandled message policy will be applied. -- You should, therefore, take great care to align these types since the -- default unhandled message policy is to terminate the server! That -- might seem pretty extreme, but you can alter the unhandled message -- policy and/or use the various overloaded versions of the call API in -- order to detect errors on the server such as this. -- -- The cost of potential type mismatches between the client and server is -- the main disadvantage of this looser coupling between them. This -- mechanism does however, allow servers to handle a variety of messages -- without specifying the entire protocol to be supported in excruciating -- detail. For that, we would want session types, which are beyond -- the scope of this library. -- -- -- -- An explicit protocol for communicating with the process can be -- configured using cast and call, but it is not possible -- to prevent other kinds of messages from being sent to the process -- mailbox. When any message arrives for which there are no handlers able -- to process its content, the UnhandledMessagePolicy will be -- applied. Sometimes it is desirable to process incoming messages which -- aren't part of the protocol, rather than let the policy deal with -- them. This is particularly true when incoming messages are important -- to the process, but their point of origin is outside the author's -- control. Handling signals such as -- ProcessMonitorNotification is a typical example of this: -- --
--   handleInfo_ (\(ProcessMonitorNotification _ _ r) -> say $ show r >> continue_)
--   
-- -- -- -- The ProcessDefinition is parameterised by the type of state it -- maintains. A process that has no state will have the type -- ProcessDefinition () and can be bootstrapped by evaluating -- statelessProcess. -- -- All call/cast handlers come in two flavours, those which take the -- process state as an input and those which do not. Handlers that ignore -- the process state have to return a function that takes the state and -- returns the required action. Versions of the various action generating -- functions ending in an underscore are provided to simplify this: -- --
--   statelessProcess {
--       apiHandlers = [
--         handleCall_   (\(n :: Int) -> return (n * 2))
--       , handleCastIf_ (\(c :: String, _ :: Delay) -> c == "timeout")
--                       (\("timeout", (d :: Delay)) -> timeoutAfter_ d)
--       ]
--     , timeoutHandler = \_ _ -> stop $ ExitOther "timeout"
--   }
--   
-- -- -- -- If you wish to only write side-effect free code in your server -- definition, then there is an explicit API for doing so. Instead of -- using the handler definition functions in this module, import the -- pure server module instead, which provides a StateT based monad -- for building referentially transparent callbacks. -- -- See -- Control.Distributed.Process.ManagedProcess.Server.Restricted -- for details and API documentation. -- -- -- -- Error handling appears in several contexts and process definitions can -- hook into these with relative ease. Catching exceptions inside handle -- functions is no different to ordinary exception handling in monadic -- code. -- --
--   handleCall (\x y ->
--                catch (hereBeDragons x y)
--                      (\(e :: SmaugTheTerribleException) ->
--                           return (Left (show e))))
--   
-- -- The caveats mentioned in Control.Distributed.Process.Extras -- about exit signal handling are very important here - it is strongly -- advised that you do not catch exceptions of type -- ProcessExitException unless you plan to re-throw them again. -- -- -- -- Because Control.Distributed.Process.ProcessExitException is a -- ubiquitous signalling mechanism in Cloud Haskell, it is treated unlike -- other asynchronous exceptions. The ProcessDefinition -- exitHandlers field accepts a list of handlers that, for a -- specific exit reason, can decide how the process should respond. If -- none of these handlers matches the type of reason then the -- process will exit. with DiedException why. In addition, a -- private exit handler is installed for exit signals where -- (reason :: ExitReason) == ExitShutdown, which is an of -- exit signal used explicitly by supervision APIs. This -- behaviour, which cannot be overriden, is to gracefully shut down the -- process, calling the shutdownHandler as usual, before -- stopping with reason given as the final outcome. -- -- Example: handling custom data is ProcessExitException -- --
--   handleExit  (\state from (sigExit :: SomeExitData) -> continue s)
--   
-- -- Under some circumstances, handling exit signals is perfectly -- legitimate. Handling of other forms of asynchronous exception -- (e.g., exceptions not generated by an exit signal) is not -- supported by this API. Cloud Haskell's primitives for exception -- handling will work normally in managed process callbacks, but -- you are strongly advised against swallowing exceptions in general, or -- masking, unless you have carefully considered the consequences. -- -- -- -- Neither the vanilla nor the prioritised mailbox -- implementations will allow you to handle arbitrary asynchronous -- exceptions outside of your handler code. The way in which the two -- mailboxes handle unexpected asynchronous exceptions differs -- significantly however. The first consideration pertains to potential -- message loss. -- -- Consider a plain Cloud Haskell expression such as the following: -- --
--   catch (receiveWait [ match ((m :: SomeType) -> doSomething m) ])
--         ((e :: SomeCustomAsyncException) -> handleExFrom e pid)
--   
-- -- It is entirely possible that receiveWait will succeed in -- matching a message of type SomeType from the mailbox and -- removing it, to be handed to the supplied expression -- doSomething. Should an asynchronous exception arrive at this -- moment in time, though the handler might run and allow the server to -- recover, the message will be permanently lost. -- -- The mailbox exposed by serve operates in exactly this way, and -- as such it is advisible to avoid swallowing asynchronous exceptions, -- since doing so can introduce the possibility of unexpected message -- loss. -- -- The prioritised mailbox exposed by pserve on the other hand, -- does not suffer this scenario. Whilst the mailbox is drained into the -- internal priority queue, asynchronous exceptions are masked, and only -- once the queue has been updated are they removed. In addition, it is -- possible to peek at the priority queue without removing a -- message, thereby ensuring that should the handler fail or an -- asynchronous exception arrive whilst processing the message, we can -- resume handling our message immediately upon recovering from the -- exception. This behaviour allows the process to guarantee against -- message loss, whilst avoiding masking within handlers, which is -- generally bad form (and can potentially lead to zombie processes, when -- supervised servers refuse to respond to kill signals whilst -- stuck in a long running handler). -- -- Also note that a process' internal state is subject to the same -- semantics, such that the arrival of an asynchronous exception -- (including exit signals!) can lead to handlers (especially exit and -- shutdown handlers) running with a stale version of their state. For -- this reason - since we cannot guarantee an up to date state in the -- presence of these semantics - a shutdown handler for a serve -- loop will always have its state passed as LastKnown stateT. -- -- -- -- If any asynchronous exception goes unhandled by a vanilla -- process, the server will immediately exit without running the user -- supplied shutdownHandler. It is very important to note that -- in Cloud Haskell, link failures generate asynchronous exceptions in -- the target and these will NOT be caught by the serve API and -- will therefore cause the process to exit /without running the -- termination handler/ callback. If your termination handler is set up -- to do important work (such as resource cleanup) then you should avoid -- linking you process and use monitors instead. If your code absolutely -- must run its termination handlers in the face of any unhandled (async) -- exception, consider using a prioritised mailbox, which handles this. -- Alternatively, consider arranging your processes in a supervision -- tree, and using a shutdown strategy to ensure that siblings terminate -- cleanly (based off a supervisor's ordered shutdown signal) in order to -- ensure cleanup code can run reliably. -- -- As mentioned above, a prioritised mailbox behaves differently in the -- face of unhandled asynchronous exceptions. Whilst pserve still -- offers no means for handling arbitrary async exceptions outside your -- handlers - and you should avoid handling them within, to the maximum -- extent possible - it does execute its receiving process in such a way -- that any unhandled exception will be caught and rethrown. Because of -- this, and the fact that a prioritised process manages its internal -- state in an IORef, shutdown handlers are guaranteed to run -- even in the face of async exceptions. These are run with the latest -- version of the server state available, given as CleanShutdown -- stateT when the process is terminating normally (i.e. for reasons -- ExitNormal or ExitShutdown), and LastKnown -- stateT when an exception terminated the server process abruptly. -- The latter acknowledges that we cannot guarantee the exception did not -- interrupt us after the last handler ran and returned an updated state, -- but prior to storing the update. -- -- Although shutdown handlers are run even in the face of unhandled -- exceptions (and prior to re-throwing, when there is one present), they -- are not run in a masked state. In fact, exceptions are explicitly -- unmasked prior to executing a handler, therefore it is possible for a -- shutdown handler to terminate abruptly. Once again, supervision -- hierarchies are a better way to ensure consistent cleanup occurs when -- valued resources are held by a process. -- -- -- -- A prioritised process can take advantage of filters, which enable the -- server to pre-process messages, reject them (based on the message -- itself, or the server's state), and mark classes of message as -- requiring safe handling. -- -- Assuming a PrioritisedProcessDefinition that holds its state as -- an Int, here are some simple applications of filters: -- --
--   let rejectUnchecked =
--        rejectApi Foo :: Int -> P.Message String String -> Process (Filter Int)
--   
--     filters = [
--       store  (+1)
--     , ensure (>0)
--   
--     , check $ api_ (\(s :: String) -> return $ "checked-" `isInfixOf` s) rejectUnchecked
--     , check $ info (\_ (_ :: MonitorRef, _ :: ProcessId) -> return False) $ reject Foo
--     , refuse ((> 10) :: Int -> Bool)
--     ]
--   
-- -- We can store/update our state, ensure our state is in a valid -- condition, check api and info messages, and refuse messages using -- simple predicates. Messages cannot be modified by filters, not can -- reply data. -- -- A safe filter is a means to instruct the prioritised managed -- process loop not to dequeue the current message from the internal -- priority queue until a handler has successfully matched and run -- against it (without an exception, either synchronous or asynchronous) -- to completion. Messages marked thus, will remain in the priority queue -- even in the face of exit signals, which means that if the server -- process code handles and swallows them, it will begin re-processing -- the last message a second time. -- -- It is important to recognise that the safe filter does not -- act like a transaction. There are no checkpoints, nor facilities for -- rolling back actions on failure. If an exit signal terminates a -- handler for a message marked as safe and an exit handler -- catches and swallows it, the handler (and all prior filters too) will -- be re-run in its entireity. -- -- -- -- For advanced users and those requiring very low latency, a prioritised -- process definition might not be suitable, since it performs -- considerable work behind the scenes. There are also designs -- that need to segregate a process' control plane from other -- kinds of traffic it is expected to receive. For such use cases, a -- control channel may prove a better choice, since typed channels -- are already prioritised during the mailbox scans that the base -- receiveWait and receiveTimeout primitives from -- distribute-process provides. -- -- In order to utilise a control channel in a server, it must be -- passed to the corresponding handleControlChan function (or its -- stateless variant). The control channel is created by evaluating -- newControlChan, in the same way that we create regular typed -- channels. -- -- In order for clients to communicate with a server via its control -- channel however, they must pass a handle to a ControlPort, -- which can be obtained by evaluating channelControlPort on the -- ControlChannel. A ControlPort is Serializable, -- so they can alternatively be sent to other processes. -- -- Control channel traffic will only be prioritised over other -- traffic if the handlers using it are present before others (e.g., -- handleInfo, handleCast, etc) in the process definition. It is -- not possible to combine prioritised processes with control -- channels. Attempting to do so will satisfy the compiler, but crash -- with a runtime error once you attempt to evaluate the prioritised -- server loop (i.e., pserve). -- -- Since the primary purpose of control channels is to simplify and -- optimise client-server communication over a single channel, this -- module provides an alternate server loop in the form of -- chanServe. Instead of passing an initialised -- ProcessDefinition, this API takes an expression from a -- ControlChannel to ProcessDefinition, operating in the -- Process monad. Providing the opaque reference in this fashion -- is useful, since the type of messages the control channel carries will -- not correlate directly to the inter-process traffic we use internally. -- -- Although control channels are intended for use as a single control -- plane (via chanServe), it is possible to use them as a -- more strictly typed communications backbone, since they do enforce -- absolute type safety in client code, being bound to a particular type -- on creation. For rpc (i.e., call) interaction however, it is -- not possible to have the server reply to a control channel, since -- they're a one way pipe. It is possible to alleviate this -- situation by passing a request type than contains a typed channel -- bound to the expected reply type, enabling client and server to match -- on both the input and output types as specifically as possible. Note -- that this still does not guarantee an agreement on types between all -- parties at runtime however. -- -- An example of how to do this follows: -- --
--   data Request = Request String (SendPort String)
--     deriving (Typeable, Generic)
--   instance Binary Request where
--   
--   -- note that our initial caller needs an mvar to obtain the control port...
--   echoServer :: MVar (ControlPort Request) -> Process ()
--   echoServer mv = do
--     cc <- newControlChan :: Process (ControlChannel Request)
--     liftIO $ putMVar mv $ channelControlPort cc
--     let s = statelessProcess {
--         apiHandlers = [
--              handleControlChan_ cc (\(Request m sp) -> sendChan sp m >> continue_)
--            ]
--       }
--     serve () (statelessInit Infinity) s
--   
--   echoClient :: String -> ControlPort Request -> Process String
--   echoClient str cp = do
--     (sp, rp) <- newChan
--     sendControlMessage cp $ Request str sp
--     receiveChan rp
--   
-- -- -- -- Both client and server APIs provide a mechanism for interacting with a -- running server process via STM. This is primarily intended for code -- that runs outside of Cloud Haskell's Process monad, but can -- also be used as a channel for sending and/or receiving -- non-serializable data to or from a managed process. Obviously if you -- attempt to do this across a remote boundary, things will go -- spectacularly wrong. The APIs provided do not attempt to restrain -- this, or to impose any particular scheme on the programmer, therefore -- you're on your own when it comes to writing the STM code for -- reading and writing data between client and server. -- -- For code running inside the Process monad and passing -- Serializable thunks, there is no real advantage to this approach, and -- indeed there are several serious disadvantages - none of Cloud -- Haskell's ordering guarantees will hold when passing data to and from -- server processes in this fashion, nor are there any guarantees the -- runtime system can make with regards interleaving between messages -- passed across Cloud Haskell's communication fabric vs. data shared via -- STM. This is true even when client(s) and server(s) reside on the same -- local node. -- -- A server wishing to receive data via STM can do so using the -- handleExternal API. By way of example, here is a simple echo -- server implemented using STM: -- --
--   demoExternal = do
--     inChan <- liftIO newTQueueIO
--     replyQ <- liftIO newTQueueIO
--     let procDef = statelessProcess {
--                       apiHandlers = [
--                         handleExternal
--                           (readTQueue inChan)
--                           (\s (m :: String) -> do
--                               liftIO $ atomically $ writeTQueue replyQ m
--                               continue s)
--                       ]
--                       }
--     let txt = "hello 2-way stm foo"
--     pid <- spawnLocal $ serve () (statelessInit Infinity) procDef
--     echoTxt <- liftIO $ do
--       -- firstly we write something that the server can receive
--       atomically $ writeTQueue inChan txt
--       -- then sit and wait for it to write something back to us
--       atomically $ readTQueue replyQ
--   
--     say (show $ echoTxt == txt)
--   
-- -- For request/reply channels such as this, a convenience based on the -- call API is also provided, which allows the server author to write an -- ordinary call handler, and the client author to utilise an API that -- monitors the server and does the usual stuff you'd expect an RPC style -- client to do. Here is another example of this in use, demonstrating -- the callSTM and handleCallExternal APIs in practise. -- --
--   data StmServer = StmServer { serverPid  :: ProcessId
--                              , writerChan :: TQueue String
--                              , readerChan :: TQueue String
--                              }
--   
--   instance Resolvable StmServer where
--     resolve = return . Just . serverPid
--   
--   echoStm :: StmServer -> String -> Process (Either ExitReason String)
--   echoStm StmServer{..} = callSTM serverPid
--                                   (writeTQueue writerChan)
--                                   (readTQueue  readerChan)
--   
--   launchEchoServer :: CallHandler () String String -> Process StmServer
--   launchEchoServer handler = do
--     (inQ, replyQ) <- liftIO $ do
--       cIn <- newTQueueIO
--       cOut <- newTQueueIO
--       return (cIn, cOut)
--   
--     let procDef = statelessProcess {
--                     apiHandlers = [
--                       handleCallExternal
--                         (readTQueue inQ)
--                         (writeTQueue replyQ)
--                         handler
--                     ]
--                   }
--   
--     pid <- spawnLocal $ serve () (statelessInit Infinity) procDef
--     return $ StmServer pid inQ replyQ
--   
--   testExternalCall :: TestResult Bool -> Process ()
--   testExternalCall result = do
--     let txt = "hello stm-call foo"
--     srv <- launchEchoServer (\st (msg :: String) -> reply msg st)
--     echoStm srv txt >>= stash result . (== Right txt)
--   
-- -- -- -- The various server loops are fairly optimised, but there is a -- definite cost associated with scanning the mailbox to match on -- protocol messages, plus additional costs in space and time due to -- mapping over all available info handlers for non-protocol -- (i.e., neither call nor cast) messages. These are -- exacerbated significantly when using prioritisation, whilst using a -- single control channel is very fast and carries little overhead. -- -- From the client perspective, it's important to remember that the -- call protocol will wait for a reply in most cases, triggering a -- full O(n) scan of the caller's mailbox. If the mailbox is extremely -- full and calls are regularly made, this may have a significant impact -- on the caller. The callChan family of client API functions -- can alleviate this, by using (and matching on) a private typed channel -- instead, but the server must be written to accomodate this. Similar -- gains can be had using a control channel and providing a typed -- reply channel in the request data, however the call mechanism -- does not support this notion, so not only are we unable to use the -- various reply functions, client code should also consider -- monitoring the server's pid and handling server failures whilst -- waiting on module Control.Distributed.Process.ManagedProcess -- | Return type for and InitHandler expression. data InitResult s InitOk :: s -> Delay -> InitResult s InitStop :: String -> InitResult s InitIgnore :: InitResult s -- | An expression used to initialise a process with its state type InitHandler a s = a -> Process (InitResult s) -- | Starts the message handling loop for a managed process -- configured with the supplied process definition, after calling the -- init handler with its initial arguments. Note that this function does -- not return until the server exits. serve :: a -> InitHandler a s -> ProcessDefinition s -> Process () -- | Starts the message handling loop for a prioritised managed -- process, configured with the supplied process definition, after -- calling the init handler with its initial arguments. Note that this -- function does not return until the server exits. pserve :: a -> InitHandler a s -> PrioritisedProcessDefinition s -> Process () -- | Starts the message handling loop for a managed process, -- configured with a typed control channel. The caller supplied -- expression is evaluated with an opaque reference to the channel, which -- must be passed when calling handleControlChan. The meaning -- and behaviour of the init handler and initial arguments are the same -- as those given to serve. Note that this function does not -- return until the server exits. chanServe :: (Serializable b) => a -> InitHandler a s -> (ControlChannel b -> Process (ProcessDefinition s)) -> Process () -- | Wraps any process loop and ensures that it adheres to the -- managed process start/stop semantics, i.e., evaluating the -- InitHandler with an initial state and delay will either -- die due to InitStop, exit silently (due to -- InitIgnore) or evaluate the process' loop. The -- supplied loop must evaluate to ExitNormal, otherwise -- the calling processing will die with whatever -- ExitReason is given. runProcess :: (s -> Delay -> Process ExitReason) -> a -> InitHandler a s -> Process () -- | Turns a standard ProcessDefinition into a -- PrioritisedProcessDefinition, by virtue of the supplied list of -- DispatchPriority expressions. prioritised :: ProcessDefinition s -> [DispatchPriority s] -> PrioritisedProcessDefinition s -- | Stores the functions that determine runtime behaviour in response to -- incoming messages and a policy for responding to unhandled messages. data ProcessDefinition s ProcessDefinition :: [Dispatcher s] -> [DeferredDispatcher s] -> [ExternDispatcher s] -> [ExitSignalDispatcher s] -> TimeoutHandler s -> ShutdownHandler s -> UnhandledMessagePolicy -> ProcessDefinition s -- | functions that handle call/cast messages [apiHandlers] :: ProcessDefinition s -> [Dispatcher s] -- | functions that handle non call/cast messages [infoHandlers] :: ProcessDefinition s -> [DeferredDispatcher s] -- | functions that handle control channel and STM inputs [externHandlers] :: ProcessDefinition s -> [ExternDispatcher s] -- | functions that handle exit signals [exitHandlers] :: ProcessDefinition s -> [ExitSignalDispatcher s] -- | a function that handles timeouts [timeoutHandler] :: ProcessDefinition s -> TimeoutHandler s -- | a function that is run just before the process exits [shutdownHandler] :: ProcessDefinition s -> ShutdownHandler s -- | how to deal with unhandled messages [unhandledMessagePolicy] :: ProcessDefinition s -> UnhandledMessagePolicy -- | A ProcessDefinition decorated with DispatchPriority -- for certain input domains. data PrioritisedProcessDefinition s PrioritisedProcessDefinition :: ProcessDefinition s -> [DispatchPriority s] -> [DispatchFilter s] -> RecvTimeoutPolicy -> PrioritisedProcessDefinition s [processDef] :: PrioritisedProcessDefinition s -> ProcessDefinition s [priorities] :: PrioritisedProcessDefinition s -> [DispatchPriority s] [filters] :: PrioritisedProcessDefinition s -> [DispatchFilter s] [recvTimeout] :: PrioritisedProcessDefinition s -> RecvTimeoutPolicy -- | For a PrioritisedProcessDefinition, this policy determines for -- how long the receive loop should continue draining the process' -- mailbox before processing its received mail (in priority order). -- -- If a prioritised managed process is receiving a lot of messages -- (into its real mailbox), the server might never get around to -- actually processing its inputs. This (mandatory) policy provides a -- guarantee that eventually (i.e., after a specified number of received -- messages or time interval), the server will stop removing messages -- from its mailbox and process those it has already received. data RecvTimeoutPolicy RecvMaxBacklog :: Int -> RecvTimeoutPolicy RecvTimer :: TimeInterval -> RecvTimeoutPolicy -- | Priority of a message, encoded as an Int data Priority a -- | Dispatcher for prioritised handlers data DispatchPriority s -- | An expression used to handle process termination type ShutdownHandler s = ExitState s -> ExitReason -> Process () -- | An expression used to handle process timeouts type TimeoutHandler s = ActionHandler s Delay -- | Wraps a predicate that is used to determine whether or not a handler -- is valid based on some combination of the current process state, the -- type and/or value of the input message or both. data Condition s m -- | An action (server state transition) in the Process monad type Action s = Process (ProcessAction s) -- | The action taken by a process after a handler has run and its updated -- state. See -- "Control.Distributed.Process.ManagedProcess.Server.continue" -- "Control.Distributed.Process.ManagedProcess.Server.timeoutAfter" -- "Control.Distributed.Process.ManagedProcess.Server.hibernate" -- "Control.Distributed.Process.ManagedProcess.Server.stop" -- "Control.Distributed.Process.ManagedProcess.Server.stopWith" -- -- Also see "Control.Distributed.Process.Management.Priority.act" and -- "Control.Distributed.Process.ManagedProcess.Priority.runAfter". -- -- And other actions. This type should not be used directly. data ProcessAction s -- | An action (server state transition) causing a reply to a caller, in -- the Process monad type Reply b s = Process (ProcessReply b s) -- | Returned from handlers for the synchronous call protocol, -- encapsulates the reply data and the action to take after -- sending the reply. A handler can return NoReply if they wish -- to ignore the call. data ProcessReply r s -- | An expression used to handle a message type ActionHandler s a = s -> a -> Action s -- | An expression used to handle a message and providing a reply type CallHandler s a b = s -> a -> Reply b s -- | An expression used to handle a cast message type CastHandler s a = ActionHandler s a -- | An expression used to ignore server state during handling type StatelessHandler s a = a -> (s -> Action s) -- | An expression used to handle a call message where the reply is -- deferred via the CallRef type DeferredCallHandler s a b = CallRef b -> CallHandler s a b -- | An expression used to handle a call message ignoring server -- state type StatelessCallHandler s a b = CallRef b -> a -> Reply b s -- | An expression used to handle an info message type InfoHandler s a = ActionHandler s a -- | An expression used to handle a channel message type ChannelHandler s a b = SendPort b -> ActionHandler s a -- | An expression used to handle a channel message in a stateless -- process type StatelessChannelHandler s a b = SendPort b -> StatelessHandler s a -- | Policy for handling unexpected messages, i.e., messages which are not -- sent using the call or cast APIs, and which are not -- handled by any of the handleInfo handlers. data UnhandledMessagePolicy -- | stop immediately, giving ExitOther UnhandledInput as -- the reason Terminate :: UnhandledMessagePolicy -- | forward the message to the given recipient DeadLetter :: ProcessId -> UnhandledMessagePolicy -- | log messages, then behave identically to Drop Log :: UnhandledMessagePolicy -- | dequeue and then drop/ignore the message Drop :: UnhandledMessagePolicy -- | Wraps a consumer of the call API data CallRef a -- | Informs a shutdown handler of whether it is running due to a -- clean shutdown, or in response to an unhandled exception. data ExitState s -- | given when an ordered shutdown is underway CleanShutdown :: s -> ExitState s LastKnown :: s -> ExitState s -- | True if the ExitState is CleanShutdown, -- otherwise False. isCleanShutdown :: ExitState s -> Bool -- | Evaluates to the s state datum in the given -- ExitState. exitState :: ExitState s -> s -- | A default ProcessDefinition, with no api, info or exit handler. -- The default timeoutHandler simply continues, the -- shutdownHandler is a no-op and the -- unhandledMessagePolicy is Terminate. defaultProcess :: ProcessDefinition s -- | Creates a default PrioritisedProcessDefinition from a list of -- DispatchPriority. See defaultProcess for the underlying -- definition. defaultProcessWithPriorities :: [DispatchPriority s] -> PrioritisedProcessDefinition s -- | A basic, stateless ProcessDefinition. See defaultProcess -- for the default field values. statelessProcess :: ProcessDefinition () -- | A default, state unaware InitHandler that can be used -- with statelessProcess. This simply returns InitOk with -- the empty state (i.e., unit) and the given Delay. statelessInit :: Delay -> InitHandler () () -- | Provides a means for servers to listen on a separate, typed -- control channel, thereby segregating the channel from their -- regular (and potentially busy) mailbox. data ControlChannel m -- | The writable end of a ControlChannel. data ControlPort m -- | Creates a new ControlChannel. newControlChan :: (Serializable m) => Process (ControlChannel m) -- | Obtain an opaque expression for communicating with a -- ControlChannel. channelControlPort :: ControlChannel m -> ControlPort m