distributed-process-client-server-0.2.5.1: The Cloud Haskell Application Platform

Copyright(c) Tim Watson 2012 - 2017
LicenseBSD3 (see the file LICENSE)
MaintainerTim Watson <watson.timothy@gmail.com>
Stabilityexperimental
Portabilitynon-portable (requires concurrency)
Safe HaskellNone
LanguageHaskell98

Control.Distributed.Process.ManagedProcess.Client

Contents

Description

The Client Portion of the Managed Process API.

Synopsis

API for client interactions with the process

sendControlMessage :: Serializable m => ControlPort m -> m -> Process () Source #

Send a control message over a ControlPort.

shutdown :: ProcessId -> Process () Source #

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.

call :: forall s a b. (Addressable s, Serializable a, Serializable b) => s -> a -> Process b Source #

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!

safeCall :: forall s a b. (Addressable s, Serializable a, Serializable b) => s -> a -> Process (Either ExitReason b) Source #

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.

tryCall :: forall s a b. (Addressable s, Serializable a, Serializable b) => s -> a -> Process (Maybe b) Source #

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).

callTimeout :: forall s a b. (Addressable s, Serializable a, Serializable b) => s -> a -> TimeInterval -> Process (Maybe b) Source #

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.

flushPendingCalls :: forall b. Serializable b => TimeInterval -> (b -> Process b) -> Process (Maybe b) Source #

Attempt to flush out any pending call responses.

callAsync :: forall s a b. (Addressable s, Serializable a, Serializable b) => s -> a -> Process (Async b) Source #

Invokes call out of band, and returns an async handle.

cast :: forall a m. (Addressable a, Serializable m) => a -> m -> Process () Source #

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.

callChan :: forall s a b. (Addressable s, Serializable a, Serializable b) => s -> a -> Process (ReceivePort b) Source #

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).

syncCallChan :: forall s a b. (Addressable s, Serializable a, Serializable b) => s -> a -> Process b Source #

A synchronous version of callChan.

syncSafeCallChan :: forall s a b. (Addressable s, Serializable a, Serializable b) => s -> a -> Process (Either ExitReason b) Source #

A safe version of syncCallChan, which returns Left ExitReason if the call fails.

callSTM :: forall s a b. Addressable s => s -> (a -> STM ()) -> STM b -> a -> Process (Either ExitReason b) Source #

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.