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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
use std::{
  ops::ControlFlow,
  thread::{self, JoinHandle}
};

use super::{channel, Client, MsgType, ReplyContext, Server};

pub trait Handler<P, S, R, E, RV> {
  /// Optional initialization callback.
  ///
  /// This is called on the dispatcher thread before the main message
  /// processing loop is entered.
  #[allow(unused_variables)]
  fn init(&mut self, weak_client: ump_ng::WeakClient<P, S, R, E>) {}

  /// Post message processing callback.
  ///
  /// The callback must return `ControlFlow::Continue(())` to keep the
  /// dispatcher loop going.  Returning `ControlFlow::Break(RV)` will cause the
  /// dispatcher loop to abort and returns the value in `RV` from the thread.
  fn post(&mut self, msg: P) -> ControlFlow<RV, ()>;

  /// Request message processing callback.
  ///
  /// The callback must return `ControlFlow::Continue(())` to keep the
  /// dispatcher loop going.  Returning `ControlFlow::Break(RV)` will cause the
  /// dispatcher loop to abort and returns the value in `RV` from the thread.
  fn req(&mut self, msg: S, rctx: ReplyContext<R, E>) -> ControlFlow<RV, ()>;

  /// Optional termination callback.
  ///
  /// This is called on the dispatcher thread just after the main message
  /// processing loop has been terminbated.
  fn term(&mut self, rv: Option<RV>) -> Option<RV> {
    rv
  }
}

/// Launch a thread that will process incoming messages from an ump-ng server
/// end-point.
///
/// See top module's documentation for an overview of the [dispatch
/// loop](crate#dispatch-loop).
pub fn spawn<P, S, R, E, RV>(
  mut handler: impl Handler<P, S, R, E, RV> + Send + 'static
) -> (Client<P, S, R, E>, JoinHandle<Option<RV>>)
where
  P: 'static + Send,
  S: 'static + Send,
  R: 'static + Send,
  E: 'static + Send,
  RV: 'static + Send
{
  let (server, client) = channel();
  let weak_client = client.weak();
  let jh = thread::spawn(move || {
    handler.init(weak_client);
    let ret = loop {
      match server.wait() {
        Ok(msg) => match msg {
          MsgType::Put(m) => match handler.post(m) {
            ControlFlow::Continue(_) => {}
            ControlFlow::Break(rv) => break Some(rv)
          },
          MsgType::Request(m, rctx) => match handler.req(m, rctx) {
            ControlFlow::Continue(_) => {}
            ControlFlow::Break(rv) => break Some(rv)
          }
        },
        Err(_) => break None
      }
    };
    handler.term(ret)
  });

  (client, jh)
}


/// Spawn a thread to run a pre-initialized handler.
///
/// It is assumed that the caller has initialized the handler, thus its
/// `init()` method will not be called.
///
/// ```
/// use std::ops::ControlFlow;
/// use ump_ng_server::{ump_ng, spawn_thread_preinit, ThreadedHandler,
///   ReplyContext};
/// let (server, client) = ump_ng::channel::<(), (), (), ()>();
///
/// struct MyHandler {
///   wclnt: ump_ng::WeakClient<(), (), (), ()>
/// }
/// impl ThreadedHandler<(), (), (), (), ()> for MyHandler {
///   fn post(&mut self, _: ()) -> ControlFlow<(), ()> {
///     ControlFlow::Continue(())
///   }
///   fn req(&mut self, _: (), rctx: ReplyContext<(), ()>)
///     -> ControlFlow<(), ()> {
///     ControlFlow::Continue(())
///   }
/// }
/// let handler = MyHandler {
///   wclnt: client.weak()
/// };
/// let jh = spawn_thread_preinit(server, handler);
///
/// // drop client to force dispatch loop to terminate
/// drop(client);
///
/// jh.join();
/// ```
pub fn spawn_preinit<P, S, R, E, RV>(
  server: Server<P, S, R, E>,
  mut handler: impl Handler<P, S, R, E, RV> + Send + 'static
) -> JoinHandle<Option<RV>>
where
  P: 'static + Send,
  S: 'static + Send,
  R: 'static + Send,
  E: 'static + Send,
  RV: 'static + Send
{
  thread::spawn(move || {
    let ret = loop {
      match server.wait() {
        Ok(msg) => match msg {
          MsgType::Put(m) => match handler.post(m) {
            ControlFlow::Continue(_) => {}
            ControlFlow::Break(rv) => break Some(rv)
          },
          MsgType::Request(m, rctx) => match handler.req(m, rctx) {
            ControlFlow::Continue(_) => {}
            ControlFlow::Break(rv) => break Some(rv)
          }
        },
        Err(_) => break None
      }
    };
    handler.term(ret)
  })
}

// vim: set ft=rust et sw=2 ts=2 sts=2 cinoptions=2 tw=79 :