Safe Haskell | None |
---|---|
Language | Haskell98 |
- Cloud Haskell Extras
- Evaluation Strategies and Support for NFData
When sending messages to a local process (i.e., intra-node), the default
approach is to encode (i.e., serialise) the message anyway, just to
ensure that no unevaluated thunks are passed to the receiver.
In distributed-process, you must explicitly choose to use unsafe primitives
that do nothing to ensure evaluation, since this might cause an error in the
receiver which would be difficult to debug. Using NFData
, it is possible
to force evaluation, but there is no way to ensure that both the NFData
and Binary
instances do so in the same way (i.e., to the same depth, etc)
therefore automatic use of NFData
is not possible in distributed-process.
By contrast, distributed-process-platform makes extensive use of NFData
to force evaluation (and avoid serialisation overheads during intra-node
communication), via the NFSerializable
type class. This does nothing to
fix the potential disparity between NFData
and Binary
instances, so you
should verify that your data is being handled as expected (e.g., by sticking
to strict fields, or some such) and bear in mind that things could go wrong.
The UnsafePrimitives
module in this library will force evaluation before
calling the UnsafePrimitives
in distributed-process, which - if you've
vetted everything correctly - should provide a bit more safety, whilst still
keeping performance at an acceptable level.
Users of the various service and utility models (such as ManagedProcess
and
the Service
and Task
APIs) should consult the sub-system specific
documentation for instructions on how to utilise these features.
IMPORTANT NOTICE: Despite the apparent safety of forcing evaluation before sending, we still cannot make any actual guarantees about the evaluation semantics of these operations, and therefore the unsafe moniker will remain in place, in one form or another, for all functions and modules that use them.
- Addressing/Interaction Tools
The various type classes exposed here, along with some common data types (such
as Shutdown
, ServerDisconnected
, etc.) are intended to simplify your CH
programs, and facilitate easily plugging code into higher level libraries such
as distributed-process-client-server and distributed-process-supervisor.
- Error/Exception Handling
It is important not to be too general when catching exceptions in
cloud haskell application, because asynchonous exceptions provide cloud haskell
with its process termination mechanism. Two exception types in particular,
signal the instigator's intention to stop a process immediately, which are
raised (i.e., thrown) in response to the kill
and exit
primitives provided
by the base distributed-process package.
You should generally try to keep exception handling code to the lowest (i.e.,
most specific) scope possible. If you wish to trap exit
signals, use the
various flavours of catchExit
primitive from distributed-process.
- class (Resolvable a, Routable a) => Addressable a
- class Resolvable a where
- class Routable a where
- class Linkable a where
- class Killable p where
- class Monitored a r m | a r -> m where
- class (NFData a, Serializable a) => NFSerializable a
- data Recipient
- = Pid !ProcessId
- | Registered !String
- | RemoteRegistered !String !NodeId
- data Shutdown = Shutdown
- data ExitReason
- data CancelWait = CancelWait
- newtype ServerDisconnected = ServerDisconnected DiedReason
- type Channel a = (SendPort a, ReceivePort a)
- type Tag = Int
- type TagPool = MVar Tag
- monitor :: Resolvable a => a -> Process (Maybe MonitorRef)
- module Control.Distributed.Process.Extras.UnsafePrimitives
- spawnSignalled :: Process a -> (a -> Process ()) -> Process ProcessId
- spawnLinkLocal :: Process () -> Process ProcessId
- spawnMonitorLocal :: Process () -> Process (ProcessId, MonitorRef)
- linkOnFailure :: ProcessId -> Process ()
- times :: Int -> Process () -> Process ()
- isProcessAlive :: ProcessId -> Process Bool
- matchCond :: Serializable a => (a -> Maybe (Process b)) -> Match b
- deliver :: (Addressable a, Serializable m) => m -> a -> Process ()
- awaitExit :: Resolvable a => a -> Process ()
- awaitResponse :: Addressable a => a -> [Match (Either ExitReason b)] -> Process (Either ExitReason b)
- newTagPool :: Process TagPool
- getTag :: TagPool -> Process Tag
- whereisOrStart :: String -> Process () -> Process ProcessId
- whereisOrStartRemote :: NodeId -> String -> Closure (Process ()) -> Process (Maybe ProcessId)
- __remoteTable :: RemoteTable -> RemoteTable
Exported Types
class (Resolvable a, Routable a) => Addressable a Source #
class Resolvable a where Source #
Class of things that can be resolved to a ProcessId
.
class Routable a where Source #
Class of things that you can route/send serializable message to
sendTo :: (Serializable m, Resolvable a) => a -> m -> Process () Source #
Send a message to the target asynchronously
unsafeSendTo :: (NFSerializable m, Resolvable a) => a -> m -> Process () Source #
Send some NFData
message to the target asynchronously,
forcing evaluation (i.e., deepseq
) beforehand.
class Linkable a where Source #
Class of things to which a Process
can link itself.
linkTo :: Resolvable a => a -> Process () Source #
Create a link with the supplied object.
class Killable p where Source #
Class of things that can be killed (or instructed to exit).
killProc :: Resolvable p => p -> String -> Process () Source #
Kill (instruct to exit) generic process, using kill
primitive.
exitProc :: (Resolvable p, Serializable m) => p -> m -> Process () Source #
Kill (instruct to exit) generic process, using exit
primitive.
Resolvable p => Killable p Source # | |
class (NFData a, Serializable a) => NFSerializable a Source #
Introduces a class that brings NFData into scope along with Serializable,
such that we can force evaluation. Intended for use with the UnsafePrimitives
module (which wraps Control.Distributed.Process.UnsafePrimitives), and
guarantees evaluatedness in terms of NFData
. Please note that we cannot
guarantee that an NFData
instance will behave the same way as a Binary
one with regards evaluation, so it is still possible to introduce unexpected
behaviour by using unsafe primitives in this way.
(NFData a, Serializable a) => NFSerializable a Source # | |
NFSerializable a => NFSerializable (SendPort a) Source # | |
A simple means of mapping to a receiver.
A ubiquitous shutdown signal that can be used to maintain a consistent shutdown/stop protocol for any process that wishes to handle it.
data ExitReason Source #
Provides a reason for process termination.
ExitNormal | indicates normal exit |
ExitShutdown | normal response to a |
ExitOther !String | abnormal (error) shutdown |
data CancelWait Source #
Wait cancellation message.
newtype ServerDisconnected Source #
Given when a server is unobtainable.
type Channel a = (SendPort a, ReceivePort a) Source #
Simple representation of a channel.
Tags provide uniqueness for messages, so that they can be matched with their response.
type TagPool = MVar Tag Source #
Generates unique Tag
for messages and response pairs.
Each process that depends, directly or indirectly, on
the call mechanisms in Control.Distributed.Process.Global.Call
should have at most one TagPool on which to draw unique message
tags.
Primitives overriding those in distributed-process
monitor :: Resolvable a => a -> Process (Maybe MonitorRef) Source #
Monitor any Resolvable
object.
Utilities and Extended Primitives
spawnSignalled :: Process a -> (a -> Process ()) -> Process ProcessId Source #
Spawn a new (local) process. This variant takes an initialisation
action and a secondary expression from the result of the initialisation
to Process ()
. The spawn operation synchronises on the completion of the
before
action, such that the calling process is guaranteed to only see
the newly spawned ProcessId
once the initialisation has successfully
completed.
spawnMonitorLocal :: Process () -> Process (ProcessId, MonitorRef) Source #
Like spawnLinkLocal
, but monitors the spawned process.
linkOnFailure :: ProcessId -> Process () Source #
CH's link
primitive, unlike Erlang's, will trigger when the target
process dies for any reason. This function has semantics like Erlang's:
it will trigger ProcessLinkException
only when the target dies abnormally.
times :: Int -> Process () -> Process () Source #
Deprecated: use replicateM_ instead
Apply the supplied expression n times
isProcessAlive :: ProcessId -> Process Bool Source #
Check if specified process is alive. Information may be outdated.
matchCond :: Serializable a => (a -> Maybe (Process b)) -> Match b Source #
An alternative to matchIf
that allows both predicate and action
to be expressed in one parameter.
deliver :: (Addressable a, Serializable m) => m -> a -> Process () Source #
Send message to Addressable
object.
awaitExit :: Resolvable a => a -> Process () Source #
Wait until Resolvable
object will exit. Return immediately
if object can't be resolved.
awaitResponse :: Addressable a => a -> [Match (Either ExitReason b)] -> Process (Either ExitReason b) Source #
Safe (i.e., monitored) waiting on an expected response/message.
Call/Tagging support
newTagPool :: Process TagPool Source #
Create a new per-process source of unique message identifiers.
Registration and Process Lookup
whereisOrStart :: String -> Process () -> Process ProcessId Source #
Returns the pid of the process that has been registered
under the given name. This refers to a local, per-node registration,
not global
registration. If that name is unregistered, a process
is started. This is a handy way to start per-node named servers.
whereisOrStartRemote :: NodeId -> String -> Closure (Process ()) -> Process (Maybe ProcessId) Source #
A remote equivalent of whereisOrStart
. It deals with the
node registry on the given node, and the process, if it needs to be started,
will run on that node. If the node is inaccessible, Nothing will be returned.