rtactor/lib.rs
1//! A framework to implement the reactive pattern on real-time systems.
2//!
3//! # Example of reactive actor executed by a dispatcher in its thread
4//!
5//! ```
6//! use rtactor::dispatcher;
7//! use rtactor::{spawn_dispatcher, ActiveMailbox, Addr, Behavior, Message, ProcessContext, send_notification};
8//! use std::time::Duration;
9//!
10//! // A very simple reactive actor that allows incrementing and querying an integer.
11//! struct TestReactive {
12//! pub val: i32,
13//! }
14//!
15//! enum Notification {
16//! Increment(i32),
17//! }
18//!
19//! enum Request {
20//! GetValue,
21//! ToString(String /*label*/),
22//! }
23//!
24//! enum Response {
25//! GetValue(i32),
26//! ToString(String),
27//! }
28//!
29//! impl Behavior for TestReactive {
30//! fn process_message<'a>(&mut self, context: &'a mut ProcessContext, msg: &Message) {
31//! match msg {
32//! Message::Notification(notif) => {
33//! if let Some(notif) = notif.data.downcast_ref::<Notification>() {
34//! match notif {
35//! Notification::Increment(increment) => self.val += increment,
36//! }
37//! }
38//! }
39//! Message::Request(request) => {
40//! if let Some(data) = request.data.downcast_ref::<Request>() {
41//! match data {
42//! Request::GetValue => {
43//! context.send_response(request, Response::GetValue(self.val));
44//! }
45//!
46//! Request::ToString(label) => context.send_response(
47//! &request,
48//! Response::ToString(format!("{label}: {}", self.val)),
49//! ),
50//! }
51//! }
52//! }
53//! _ => panic!(),
54//! }
55//! }
56//! }
57//!
58//! let initial_value = 0;
59//!
60//! // Start a dispatcher inside its own thread.
61//! // The active object is created with the closure called inside the dispatcher thread.
62//! // This allows to have reactive object that are not movable between threads.
63//! let (dispatcher_addr, join_handle, test_reactive_addr) = spawn_dispatcher(10, move |disp| {
64//! // Create a reactive object on the heap.
65//! let test_reactive = Box::new(TestReactive { val: initial_value });
66//! // Move it inside the dispatcher and return the reactive address as the return of `setup_func`
67//! disp.register_reactive(test_reactive)
68//! });
69//!
70//! send_notification(&test_reactive_addr, Notification::Increment(10))
71//! .unwrap();
72//!
73//! // Create an active object to interact with the reactive under test.
74//! let mut prober = ActiveMailbox::new(1);
75//!
76//! // Request the value.
77//! let result = prober.request_for::<_, Response>(
78//! &test_reactive_addr,
79//! Request::GetValue,
80//! Duration::from_secs(10),
81//! );
82//!
83//! if let Ok(Response::GetValue(val)) = result {
84//! assert_eq!(val, 10);
85//! } else {
86//! panic!();
87//! }
88//!
89//! // An other notification.
90//! send_notification(&test_reactive_addr, Notification::Increment(-3))
91//! .unwrap();
92//!
93//! // An other different request.
94//! let result = prober.request_for::<_, Response>(
95//! &test_reactive_addr,
96//! Request::ToString("the value".to_string()),
97//! Duration::from_secs(10),
98//! );
99//! if let Ok(Response::ToString(s)) = result {
100//! assert_eq!(s, "the value: 7");
101//! } else {
102//! panic!();
103//! }
104//!
105//! // Request to stop the dispatcher using its own address.
106//! let result = prober.request_for::<_, dispatcher::Response>(
107//! &dispatcher_addr,
108//! dispatcher::Request::StopDispatcher{},
109//! Duration::from_secs(10),
110//! );
111//! if let Ok(dispatcher::Response::StopDispatcher()) = result {
112//! } else {
113//! panic!();
114//! }
115//!
116//! // Wait that the dispatcher thread finishes.
117//! join_handle.join().unwrap();
118//! ```
119//!
120//! # Example of simulation of a reactive actor in a single threaded test
121//!
122//! ```
123//! use rtactor::simulation::SimulationDispatcher;
124//! use rtactor::{ActiveMailbox, Behavior, Message, ProcessContext, send_notification};
125//! use std::time::Duration;
126//!
127//! // A very simple reactive actor that allows incrementing and querying an integer.
128//! struct TestReactive {
129//! pub val: i32,
130//! }
131//!
132//! enum Notification {
133//! Increment(i32),
134//! }
135//!
136//! enum Request {
137//! GetValue,
138//! ToString(String /*label*/),
139//! }
140//!
141//! enum Response {
142//! GetValue(i32),
143//! ToString(String),
144//! }
145//!
146//! impl Behavior for TestReactive {
147//! fn process_message<'a>(&mut self, context: &'a mut ProcessContext, msg: &Message) {
148//! match msg {
149//! Message::Notification(notif) => {
150//! if let Some(notif) = notif.data.downcast_ref::<Notification>() {
151//! match notif {
152//! Notification::Increment(increment) => self.val += increment,
153//! }
154//! }
155//! }
156//! Message::Request(request) => {
157//! if let Some(data) = request.data.downcast_ref::<Request>() {
158//! match data {
159//! Request::GetValue => {
160//! context.send_response(request, Response::GetValue(self.val))
161//! }
162//!
163//! Request::ToString(label) => context.send_response(
164//! &request,
165//! Response::ToString(format!("{label}: {}", self.val)),
166//! ),
167//! }
168//! }
169//! }
170//! _ => panic!(),
171//! }
172//! }
173//! }
174//!
175//! // Create a simulation dispatcher.
176//! let mut disp = SimulationDispatcher::new(10);
177//!
178//! // Create a reactive object on the heap.
179//! let test_reactive = Box::new(TestReactive { val: 0 });
180//!
181//! // Move it inside the dispatcher. It starts the dispatch of messages for it.
182//! let test_reactive_addr = disp.register_reactive(test_reactive);
183//!
184//! // Send a notification to the reactive.
185//! send_notification(&test_reactive_addr, Notification::Increment(10))
186//! .unwrap();
187//!
188//! // Create an active object to interact with the reactive under test.
189//! let mut prober = ActiveMailbox::new(1);
190//!
191//! // Ask the simulation dispatcher to simulate a request by the active actor.
192//! let result = disp.active_request_for::<_, Response>(
193//! &mut prober,
194//! &test_reactive_addr,
195//! Request::GetValue,
196//! Duration::from_secs(10),
197//! );
198//! if let Ok(Response::GetValue(val)) = result {
199//! assert_eq!(val, 10);
200//! } else {
201//! panic!();
202//! }
203//!
204//! // An other notification.
205//! send_notification(&test_reactive_addr, Notification::Increment(-3))
206//! .unwrap();
207//!
208//! // An other different request.
209//! let result = disp.active_request_for::<_, Response>(
210//! &mut prober,
211//! &test_reactive_addr,
212//! Request::ToString("the value".to_string()),
213//! Duration::from_secs(10),
214//! );
215//! if let Ok(Response::ToString(s)) = result {
216//! assert_eq!(s, "the value: 7");
217//! } else {
218//! panic!();
219//! }
220//!
221//! // No need to stop the dispatcher, there is no thread, everything is single threaded.
222//! // The reactive actor will be dropped by the drop of the simulation dispatcher.
223//! ```
224//!
225//! # Doc about actors
226//! A good explanation why the actor pattern is good for multitask:
227//! <https://getakka.net/articles/intro/what-problems-does-actor-model-solve.html>
228//!
229//! A well designed rust actor framework (but not suitable for real-time):
230//! <https://docs.rs/axiom/latest/axiom/>
231
232pub extern crate rtactor_macros;
233
234extern crate self as rtactor;
235
236mod active;
237mod actor;
238mod reactive;
239
240#[cfg(feature = "mockall")]
241mod reactive_mocker;
242
243pub mod dispatcher;
244pub mod mpsc_dispatcher;
245pub mod profiled_actor;
246pub mod profiling_aggregator;
247pub mod simulation;
248
249pub use actor::send_notification;
250pub use actor::Addr;
251pub use actor::Error;
252pub use actor::Message;
253pub use actor::Notification;
254pub use actor::Request;
255pub use actor::RequestId;
256pub use actor::Response;
257
258#[deprecated = "use ActiveMailbox instead"]
259#[allow(deprecated)]
260pub use active::ActiveActor;
261
262pub use active::ActiveMailbox;
263pub use active::SyncAccessor;
264
265pub use mpsc_dispatcher::spawn_dispatcher;
266pub use mpsc_dispatcher::MpscDispatcher;
267pub use reactive::Behavior;
268pub use reactive::DummyBehavior;
269pub use reactive::Instant;
270pub use reactive::ProcessContext;
271pub use reactive::Timeout;
272pub use reactive::Timer;
273
274#[cfg(feature = "async-actor")]
275pub mod async_actor;
276
277#[cfg(feature = "async-actor")]
278pub use async_actor::{AsyncAccessor, AsyncMailbox};
279
280#[cfg(feature = "mockall")]
281pub use reactive::MockBehavior;
282
283// This crate used in the interface is reexported to allow user to build with the same version.
284// see https://doc.rust-lang.org/rustdoc/write-documentation/re-exports.html
285#[cfg(feature = "mockall")]
286pub use mockall;