Skip to main content

ump_server/
lib.rs

1//! _ump-server_ is an abstraction on top of [`ump`] that is used to hide
2//! boilerplate code used to implement intra-process message passing servers.
3//!
4//! # Dispatch loop
5//! The core functionality of _ump-server_ is a dispatch loop, whose role it
6//! is to pull messages off the message queue and pass them to the
7//! application-supplied message handler.
8//!
9//! There are two different ways to run the dispatcher loop: On a non-async
10//! thread or as an async task.  The former is launched using
11//! [`thread::spawn()`] and the latter [`task::spawn()`] (only available using
12//! the `tokio` feature).
13//!
14//! The `spawn()` functions return a tuple containing a `Client` (that can be
15//! used to send pass messages to the server) and a `JoinHandle` that can be
16//! used to wait for the dispatch loop thread/task to terminate (and retreive
17//! its return value).
18//!
19//! The returned `JoinHandle` will, once joined, return an `Option<RV>`.
20//! If this is `None` the server was terminated because all the `Client`
21//! endpoints were dropped.  If this is `Some(RV)` the server was terminated
22//! because a callback requested its termination by returning
23//! `ControlFlow::Break(RV)`.
24//!
25//! There are two ways to terminate the dispatch loop:
26//! - The message processing handler returns `ControlFlow::Break(RV)` (rather
27//!   than `ControlFlow::Continue(())`).  This would cause the thread/task to
28//!   return `Some(RV)`.
29//! - The message queue is empty and all the associated [`Client`]s have been
30//!   released.  This would cause the thread to return `None`.
31//!
32//! # Application message handlers
33//! Message handlers are implemented using the [`thread::Handler`] trait (for
34//! the threaded dispatch loop) and [`task::Handler`] (for the async dispatch
35//! loop).
36//!
37//! There are cases where the handler needs to store a clone of the client
38//! end-point of the message passing channel used to issue requests to the
39//! server (so that message handlers can issue new requests).  In order to
40//! facilitate this, the application must pass a `Handler`-construction closure
41//! to `spawn()`.  The closure will be called after the message passing channel
42//! has been created so it can be passed a reference to the client end-point.
43//!
44//! If the dispatch loop should terminate once all the application's client
45//! end-points have been dropped, then the handler can store a [`WeakClient`]
46//! instead (as storing a cloned [`Client`] object will preventing the dispatch
47//! loop from terminating due to all clients being lost).  The examples in the
48//! [`task`] and [`thread`] modules illustrate how to do this.
49//!
50//! ## Generics
51//! When a new handler is created, it needs to define the generic type
52//! parameters:
53//!
54//! - `S` - sent from the client to the server handler when making requests.
55//! - `R` - replies sent back to the client from the server handler using.
56//!   [`ReplyContext::reply()`].
57//! - `E` - errors returned from server handler to requestor through
58//!   [`ReplyContext::fail()`] (and also returned by the server handler
59//!   construction closure).
60//! - `RV` - return type the server handler can use to pass termination data
61//!   back to the dispatch loop creator once it terminates.
62
63#![cfg_attr(docsrs, feature(doc_cfg))]
64
65#[cfg(feature = "watchdog")]
66mod wdog;
67
68#[cfg(feature = "tokio")]
69#[cfg_attr(docsrs, doc(cfg(feature = "tokio")))]
70pub mod task;
71
72pub mod thread;
73
74pub use ump::{self, Client, Error, ReplyContext, WeakClient, channel};
75
76pub use thread::{Handler as ThreadedHandler, spawn as spawn_thread};
77
78#[cfg(feature = "tokio")]
79#[cfg_attr(docsrs, doc(cfg(feature = "tokio")))]
80pub use task::{Handler as AsyncHandler, spawn as spawn_task};
81
82#[cfg(feature = "tokio")]
83#[cfg_attr(docsrs, doc(cfg(feature = "tokio")))]
84pub use async_trait::async_trait;
85
86// vim: set ft=rust et sw=2 ts=2 sts=2 cinoptions=2 tw=79 :