tutorial/tutorial.rs
1//! The tutorial example from:
2//! https://docs.rs/stakker/*/stakker/#tutorial-example
3
4use stakker::*;
5use std::time::{Duration, Instant};
6
7// An actor is represented as a struct which holds the actor state
8struct Light {
9 start: Instant,
10 on: bool,
11}
12
13impl Light {
14 // This is a "Prep" method which is used to create a Self value
15 // for the actor. `cx` is the actor context and gives access to
16 // Stakker `Core`. (`CX![]` expands to `&mut Cx<'_, Self>`.)
17 //
18 // A "Prep" method doesn't have to return a Self value right away.
19 // For example it might asynchronously attempt a connection to a
20 // remote server first before arranging a call to another "Prep"
21 // function which returns the Self value. Once a value is
22 // returned, the actor is "Ready" and any queued-up operations on
23 // the actor will be executed.
24 pub fn init(cx: CX![]) -> Option<Self> {
25 // Use cx.now() instead of Instant::now() to allow execution
26 // in virtual time if supported by the environment.
27 let start = cx.now();
28 Some(Self { start, on: false })
29 }
30
31 // Methods that may be called once the actor is "Ready" have a
32 // `&mut self` or `&self` first argument.
33 pub fn set(&mut self, cx: CX![], on: bool) {
34 self.on = on;
35 let time = cx.now() - self.start;
36 println!(
37 "{:04}.{:03} Light on: {}",
38 time.as_secs(),
39 time.subsec_millis(),
40 on
41 );
42 }
43
44 // A `Fwd` or `Ret` allows passing data to arbitrary destinations,
45 // like an async callback. Here we use it to return a value.
46 pub fn query(&self, _cx: CX![], ret: Ret<bool>) {
47 ret!([ret], self.on);
48 }
49}
50
51// This is another actor that holds a reference to a Light actor.
52struct Flasher {
53 light: Actor<Light>,
54 interval: Duration,
55 count: usize,
56}
57
58impl Flasher {
59 pub fn init(cx: CX![], light: Actor<Light>, interval: Duration, count: usize) -> Option<Self> {
60 // Defer first switch to the queue
61 call!([cx], switch(true));
62 Some(Self {
63 light,
64 interval,
65 count,
66 })
67 }
68
69 pub fn switch(&mut self, cx: CX![], on: bool) {
70 // Change the light state
71 call!([self.light], set(on));
72
73 self.count -= 1;
74 if self.count != 0 {
75 // Call switch again after a delay
76 after!(self.interval, [cx], switch(!on));
77 } else {
78 // Terminate the actor successfully, causing StopCause handler to run
79 cx.stop();
80 }
81
82 // Query the light state, receiving the response in the method
83 // `recv_state`, which has both fixed and forwarded arguments.
84 let ret = ret_some_to!([cx], recv_state(self.count) as (bool));
85 call!([self.light], query(ret));
86 }
87
88 fn recv_state(&self, _: CX![], count: usize, state: bool) {
89 println!(" (at count {} received: {})", count, state);
90 }
91}
92
93fn main() {
94 // Contains all the queues and timers, and controls access to the
95 // state of all the actors.
96 let mut stakker0 = Stakker::new(Instant::now());
97 let stakker = &mut stakker0;
98
99 // Create and initialise the Light and Flasher actors. The
100 // Flasher actor is given a reference to the Light. Use a
101 // StopCause handler to shutdown when the Flasher terminates.
102 let light = actor!(stakker, Light::init(), ret_nop!());
103
104 let _flasher = actor!(
105 stakker,
106 Flasher::init(light.clone(), Duration::from_secs(1), 6),
107 ret_shutdown!(stakker)
108 );
109
110 // Since we're not in virtual time, we use `Instant::now()` in
111 // this loop, which is then passed on to all the actors as
112 // `cx.now()`. (If you want to run time faster or slower you
113 // could use another source of time.) So all calls in a batch of
114 // processing get the same `cx.now()` value. Also note that
115 // `Instant::now()` uses a Mutex on some platforms so it saves
116 // cycles to call it less often.
117 stakker.run(Instant::now(), false);
118 while stakker.not_shutdown() {
119 // Wait for next timer to expire. Here there's no I/O polling
120 // required to wait for external events, so just `sleep`
121 let maxdur = stakker.next_wait_max(Instant::now(), Duration::from_secs(60), false);
122 std::thread::sleep(maxdur);
123
124 // Run queue and timers
125 stakker.run(Instant::now(), false);
126 }
127}