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