1use std::{
5 any::TypeId,
6 sync::mpsc::{channel, Receiver, RecvError, Sender, TryRecvError},
7 thread::spawn,
8};
9
10use log::{error, warn};
11use tokio::runtime::Builder;
12use tokio::sync::mpsc as tokio_mpsc;
13
14use crate::{
15 BoundedMailbox, BoundedTaskMailbox, Envelope, Mailbox, Service, ShutdownServiceMessage,
16 StatsAggregator, TaskService,
17};
18
19pub struct ServiceThread<S: Service> {
21 pub join_handle: ServiceJoinHandle,
22 pub mailbox: Mailbox<S>,
23}
24
25impl<S: Service + Send + 'static> ServiceThread<S> {
26 pub fn spawn_with(service: S) -> Self {
27 let (mailbox, receiver) = Mailbox::create();
28 let (handle_tx, handle_rx) = channel();
29 let join_handle = ServiceJoinHandle::new(handle_rx);
30
31 spawn(move || run(service, receiver, handle_tx));
32
33 ServiceThread {
34 join_handle,
35 mailbox,
36 }
37 }
38
39 pub fn mbox(&self) -> Mailbox<S> {
40 self.mailbox.clone()
41 }
42}
43
44impl<S: Service + 'static> ServiceThread<S> {
45 pub fn spawn_with_init_fn<F: FnOnce() -> S + Send + 'static>(init_fn: F) -> Self {
46 let (mailbox, receiver) = Mailbox::create();
47 let (handle_tx, handle_rx) = channel();
48 let join_handle = ServiceJoinHandle::new(handle_rx);
49
50 spawn(move || {
51 let service = init_fn();
52 run(service, receiver, handle_tx)
53 });
54
55 ServiceThread {
56 join_handle,
57 mailbox,
58 }
59 }
60}
61
62pub struct BoundedServiceThread<S: Service> {
63 pub join_handle: ServiceJoinHandle,
64 pub mailbox: BoundedMailbox<S>,
65}
66
67impl<S: Service + Send + 'static> BoundedServiceThread<S> {
68 pub fn spawn_with(service: S, channel_size: usize) -> Self {
69 let (mailbox, receiver) = BoundedMailbox::create(channel_size);
70 let (handle_tx, handle_rx) = channel();
71 let join_handle = ServiceJoinHandle::new(handle_rx);
72
73 spawn(move || run(service, receiver, handle_tx));
74
75 BoundedServiceThread {
76 join_handle,
77 mailbox,
78 }
79 }
80
81 pub fn mbox(&self) -> BoundedMailbox<S> {
82 self.mailbox.clone()
83 }
84}
85
86impl<S: Service + 'static> BoundedServiceThread<S> {
87 pub fn spawn_with_init_fn<F: FnOnce() -> S + Send + 'static>(
88 init_fn: F,
89 channel_size: usize,
90 ) -> Self {
91 let (mailbox, receiver) = BoundedMailbox::create(channel_size);
92 let (handle_tx, handle_rx) = channel();
93 let join_handle = ServiceJoinHandle::new(handle_rx);
94
95 spawn(move || {
96 let service = init_fn();
97 run(service, receiver, handle_tx)
98 });
99
100 BoundedServiceThread {
101 join_handle,
102 mailbox,
103 }
104 }
105}
106
107fn run<S: Service>(
108 mut service: S,
109 receiver: Receiver<Envelope<S>>,
110 join_handle_tx: Sender<Result<StatsAggregator, &'static str>>,
111) {
112 let mut stats_aggregator = StatsAggregator::new();
113 for mut envelope in receiver {
114 let type_id = envelope.message_type_id();
115 match envelope.deliver_to(&mut service) {
116 Err(_e) => {
117 if let Err(e) = join_handle_tx.send(Err("Message delivery failed")) {
119 error!("ssf delivery failed: {e}");
120 }
121 return;
122 }
123 Ok(stats) => {
124 stats_aggregator.add(&stats);
125 }
126 }
127 if type_id == Some(TypeId::of::<ShutdownServiceMessage>()) {
128 break;
129 }
130 }
131
132 drop(service);
134 if let Err(e) = join_handle_tx.send(Ok(stats_aggregator)) {
135 error!("ssf delivery failed: {e}");
136 }
137}
138
139pub struct BoundedTaskServiceThread<S: TaskService> {
140 pub join_handle: ServiceJoinHandle,
141 pub mailbox: BoundedTaskMailbox<S>,
142}
143
144impl<S: TaskService + Send + 'static> BoundedTaskServiceThread<S> {
145 pub fn spawn_with(service: S, channel_size: usize) -> Self {
146 let (mailbox, receiver) = BoundedTaskMailbox::create(channel_size);
147 let (handle_tx, handle_rx) = channel();
148 let join_handle = ServiceJoinHandle::new(handle_rx);
149
150 spawn(move || {
151 let runtime = match Builder::new_current_thread()
152 .enable_io()
153 .enable_time()
154 .build()
155 {
156 Ok(runtime) => runtime,
157 Err(e) => {
158 error!("Failed to build task service runtime: {}", e);
159 if let Err(send_err) =
160 handle_tx.send(Err("Failed to start task service runtime"))
161 {
162 error!(
163 "Failed to send task service failure notification: {}",
164 send_err
165 );
166 }
167 return;
168 }
169 };
170 runtime.block_on(async_run(service, receiver, handle_tx));
171 });
172
173 BoundedTaskServiceThread {
174 join_handle,
175 mailbox,
176 }
177 }
178
179 pub fn mbox(&self) -> BoundedTaskMailbox<S> {
180 self.mailbox.clone()
181 }
182}
183
184impl<S: TaskService + 'static> BoundedTaskServiceThread<S> {
185 pub fn spawn_with_init_fn<I>(init_fn: I, channel_size: usize) -> Self
186 where
187 I: FnOnce() -> S + Send + 'static,
188 {
189 let (mailbox, receiver) = BoundedTaskMailbox::create(channel_size);
190 let (handle_tx, handle_rx) = channel();
191 let join_handle = ServiceJoinHandle::new(handle_rx);
192
193 spawn(move || {
194 let service = init_fn();
195 let runtime = Builder::new_current_thread()
196 .enable_io()
197 .enable_time()
198 .build();
199 match runtime {
200 Ok(runtime) => runtime.block_on(async_run(service, receiver, handle_tx)),
201 Err(e) => error!("Failed to spawn service: {}", e),
202 }
203 });
204
205 BoundedTaskServiceThread {
206 join_handle,
207 mailbox,
208 }
209 }
210}
211
212async fn async_run<S>(
213 mut service: S,
214 mut receiver: tokio_mpsc::Receiver<Envelope<S>>,
215 join_handle_tx: Sender<Result<StatsAggregator, &'static str>>,
216) where
217 S: TaskService,
218{
219 let mut stats_aggregator = StatsAggregator::new();
220
221 if let Err(e) = service.init().await {
222 error!("Failed to initialize task: {}", e);
223 return;
224 }
225
226 loop {
227 tokio::select! {
228 Some(mut envelope) = receiver.recv() => {
229 let type_id = envelope.message_type_id();
230 match envelope.deliver_to(&mut service) {
231 Err(_e) => {
232 if let Err(e) = join_handle_tx.send(Err("Message delivery failed")) {
234 error!("ssf delivery failed: {e}");
235 }
236 return;
237 }
238 Ok(stats) => {
239 stats_aggregator.add(&stats);
240 }
241 }
242 if type_id == Some(TypeId::of::<ShutdownServiceMessage>()) {
243 break;
244 }
245 },
246 result = service.run_task() => {
247 if let Err(e) = result {
248 warn!("Service task failed: {}", e);
249 }
250 }
251 };
252 }
253
254 drop(service);
256 if let Err(e) = join_handle_tx.send(Ok(stats_aggregator)) {
257 error!("ssf delivery failed: {e}");
258 }
259}
260
261pub struct ServiceJoinHandle {
262 rx: Receiver<Result<StatsAggregator, &'static str>>,
263}
264
265impl ServiceJoinHandle {
266 pub fn new(rx: Receiver<Result<StatsAggregator, &'static str>>) -> Self {
267 Self { rx }
268 }
269
270 pub fn join(&mut self) -> Result<StatsAggregator, ServiceJoinHandleError> {
271 self.rx
272 .recv()?
273 .map_err(ServiceJoinHandleError::ServiceFailed)
274 }
275
276 pub fn try_join(&mut self) -> Result<StatsAggregator, ServiceJoinHandleError> {
277 self.rx
278 .try_recv()?
279 .map_err(ServiceJoinHandleError::ServiceFailed)
280 }
281}
282
283#[derive(Debug, PartialEq, Eq)]
284pub enum ServiceJoinHandleError {
285 ServiceStopped,
286 ServiceRunning,
287 ServiceFailed(&'static str),
288}
289
290impl From<RecvError> for ServiceJoinHandleError {
291 fn from(_value: RecvError) -> Self {
292 Self::ServiceStopped
294 }
295}
296
297impl From<TryRecvError> for ServiceJoinHandleError {
298 fn from(value: TryRecvError) -> Self {
299 match value {
300 TryRecvError::Empty => Self::ServiceRunning,
301 TryRecvError::Disconnected => Self::ServiceStopped,
302 }
303 }
304}
305
306#[cfg(test)]
307mod test {
308 use super::*;
309
310 #[test]
311 fn test_join_handle_error_conversion() {
312 assert_eq!(
313 ServiceJoinHandleError::from(RecvError),
314 ServiceJoinHandleError::ServiceStopped
315 );
316 assert_eq!(
317 ServiceJoinHandleError::from(TryRecvError::Empty),
318 ServiceJoinHandleError::ServiceRunning
319 );
320 assert_eq!(
321 ServiceJoinHandleError::from(TryRecvError::Disconnected),
322 ServiceJoinHandleError::ServiceStopped
323 );
324 }
325
326 #[test]
327 fn test_try_join() {
328 let (tx, rx) = channel();
329 let mut join_handle = ServiceJoinHandle::new(rx);
330
331 assert!(matches!(
332 join_handle.try_join(),
333 Err(ServiceJoinHandleError::ServiceRunning)
334 ));
335
336 tx.send(Ok(StatsAggregator::new())).unwrap();
337 assert!(join_handle.try_join().is_ok());
338 }
339
340 #[test]
341 fn test_join() {
342 let (tx, rx) = channel();
343 let mut join_handle = ServiceJoinHandle::new(rx);
344
345 tx.send(Ok(StatsAggregator::new())).unwrap();
346
347 assert!(join_handle.join().is_ok());
348 }
349
350 #[test]
351 fn test_join_dropped() {
352 let (tx, rx) = channel();
353 let mut join_handle = ServiceJoinHandle::new(rx);
354
355 drop(tx);
356 assert!(matches!(
357 join_handle.join(),
358 Err(ServiceJoinHandleError::ServiceStopped)
359 ));
360 }
361}