Skip to main content

memfault_ssf/
lib.rs

1//
2// Copyright (c) Memfault, Inc.
3// See License.txt for details
4//! Simple Service Framework
5//!
6//! This library provides a simple abstraction to implement a service oriented
7//! architecture in a non-async Rust program.
8//!
9//! Each service runs in its own thread with a processing loop waiting on a
10//! channel.
11//!
12//! This library provides a few essential traits:
13//!
14//! - `Service`: a trait implemented by services
15//! - `Message`: a trait implemented by messages
16//! - `Handler<M: Message>`: a trait implemented by services that can handle
17//!   messages of type `M`.
18//!
19//! We provide some important structs to deploy the services:
20//! - `ServiceThread`: will start and run a service inside a dedicated thread.
21//!   It returns a `Mailbox`.
22//! - `Mailbox<S: Service>`: a lightweight (cheap to `clone()`) handle to send
23//!   messages to a thread.
24//! - `Scheduler`: a utility thread which keeps a schedule of messages that need
25//!   to be sent at fixed intervals.
26//!
27//! As well as important testing utilities that are a big part of the value
28//! provided by this framework:
29//! - `ServiceJig`: a way to run a service inside a test without using threads.
30//!   The test can precisely decide when messages should be delivered and inspect
31//!   the state of the service at any time.
32//! - `ServiceMock`: a service mock. Use this when you just need a place where
33//!   to send messages. Your test can then verify that the right messages were
34//!   sent to the mock.
35//!
36
37use std::{any::Any, future::pending};
38
39mod envelope;
40mod mailbox;
41mod msg_mailbox;
42mod scheduler;
43mod service_jig;
44mod service_manager;
45mod service_mock;
46mod service_thread;
47mod shared_service_thread;
48mod stats;
49mod system_messages;
50
51pub use envelope::*;
52use futures::future::LocalBoxFuture;
53pub use mailbox::*;
54pub use msg_mailbox::*;
55pub use scheduler::*;
56pub use service_jig::*;
57pub use service_manager::*;
58pub use service_mock::*;
59pub use service_thread::*;
60pub use shared_service_thread::*;
61pub use stats::*;
62pub use system_messages::*;
63
64/// All services should implement this trait. It guarantees that we will be able
65/// to run the service inside a thread (it is `Send`).
66pub trait Service {
67    fn name(&self) -> &str;
68}
69
70/// Implement this trait if your service needs to run a task in the background.
71///
72/// The implementation here does not need to be `Send` as we only run the
73/// task on a single thread executor.
74pub trait TaskService: Service {
75    fn init(&mut self) -> LocalBoxFuture<'_, Result<(), String>> {
76        Box::pin(async { Ok(()) })
77    }
78    fn run_task(&mut self) -> LocalBoxFuture<'_, Result<(), String>> {
79        Box::pin(pending())
80    }
81}
82
83/// Any type that will be sent between services needs to implement this trait.
84///
85/// Sending a Reply is optional (use `type Reply = ()` if you will not send a reply).
86pub trait Message: Send + Sync + Any + 'static {
87    type Reply: Send;
88}
89
90/// Implement this trait to indicate that your service can process a specific message.
91///
92/// The `deliver()` method is passed a `&mut self` reference to the service,
93/// making it very easy to update your state. You can optionally include a
94/// reply.
95pub trait Handler<M: Message> {
96    fn deliver(&mut self, m: M) -> M::Reply;
97}
98
99pub trait AsyncHandler<M: Message> {
100    fn deliver_async(&mut self, m: M) -> impl std::future::Future<Output = M::Reply> + '_;
101}
102
103/// Blanket implementation of Message for any Vec<M>. You lose the return value.
104impl<M: Message> Message for Vec<M> {
105    type Reply = ();
106}
107
108/// Blanket implementation of delivering a `Vec<Message>` to a `Handler<Message>`.
109impl<M: Message, S: Handler<M>> Handler<Vec<M>> for S {
110    fn deliver(&mut self, messages: Vec<M>) {
111        for m in messages {
112            self.deliver(m);
113        }
114    }
115}
116
117/// Blanket implementation of delivering a `Vec<Message>` to an `AsyncHandler<Message>`.
118impl<M: Message, S: AsyncHandler<M>> AsyncHandler<Vec<M>> for S {
119    async fn deliver_async(&mut self, messages: Vec<M>) {
120        for m in messages {
121            self.deliver_async(m).await;
122        }
123    }
124}