vex_rt/macros/
select.rs

1#[macro_export]
2/// Selects over a range of possible future events, processing exactly one.
3/// Inspired by equivalent behaviours in other programming languages such as Go
4/// and Kotlin, and ultimately the `select` system call from POSIX.
5///
6/// Which event gets processed is a case of bounded non-determinism: the
7/// implementation makes no guarantee about which event gets processed if
8/// multiple become possible around the same time, only that it will process one
9/// of them if at least one can be processed.
10///
11/// # Examples
12///
13/// ```
14/// fn foo(ctx: Context) {
15///     let mut x = 0;
16///     let mut l = Loop::new(Duration::from_secs(1));
17///     loop {
18///         println!("x = {}", x);
19///         x += 1;
20///         select! {
21///             _ = l.next() => continue,
22///             _ = ctx.done() => break,
23///         }
24///     }
25/// }
26/// ```
27macro_rules! select {
28    { $( $var:pat = $event:expr $(; $sub:pat = $dep:expr)* => $body:expr ),+ $(,)? } => {{
29        let mut events = $crate::select_head!($($event $(; $sub = $dep)* ;;)+);
30        $crate::select_body!{loop {
31            $crate::rtos::GenericSleep::sleep($crate::select_sleep!(events; $($event,)+));
32            events = $crate::select_match!{events; |r| r; $($event,)+};
33        }; $($var => {$body},)+}
34    }};
35}
36
37#[macro_export]
38#[doc(hidden)]
39macro_rules! select_head {
40    ($event:expr $(; $sub:pat = $dep:expr)* ;;) => {
41        $crate::select_init!($event $(; $sub = $dep)*)
42    };
43    ($event:expr $(; $sub1:pat = $dep1:expr)* ;; $($rest:expr $(; $sub:pat = $dep:expr)* ;;)+) => {
44        ($crate::select_init!($event $(; $sub1 = $dep1)*), $crate::select_head!($($rest $(; $sub = $dep)* ;;)*))
45    };
46}
47
48#[macro_export]
49#[doc(hidden)]
50macro_rules! select_init {
51    ($(@NEXT)? $event:expr) => {
52        $event
53    };
54    ($event:expr $(; $sub:pat = $dep:expr)+) => {
55        $crate::rtos::select_option($crate::select_init!(@NEXT ::core::option::Option::Some($event) $(; $sub = $dep)+))
56    };
57    (@NEXT $event:expr; $sub1:pat = $dep1:expr $(; $sub:pat = $dep:expr)*) => {
58        $crate::select_init!(@NEXT if let $sub1 = $dep1 { $event } else { ::core::option::Option::None } $(; $sub = $dep)*)
59    };
60}
61
62#[macro_export]
63#[doc(hidden)]
64macro_rules! select_match {
65    { $event:expr; $cons:expr; $_:expr, } => {
66        match $crate::rtos::Selectable::poll($event) {
67            ::core::result::Result::Ok(r) => break $cons(r),
68            ::core::result::Result::Err(s) => s,
69        }
70    };
71    { $events:expr; $cons:expr; $_:expr, $($rest:expr,)+ } => {
72        match $crate::rtos::Selectable::poll($events.0) {
73            ::core::result::Result::Ok(r) => break $cons(::core::result::Result::Ok(r)),
74            ::core::result::Result::Err(s) => (
75                s,
76                $crate::select_match!{$events.1; |r| $cons(::core::result::Result::Err(r)); $($rest,)*}
77            ),
78        }
79    };
80}
81
82#[macro_export]
83#[doc(hidden)]
84macro_rules! select_body {
85    { $result:expr; $var:pat => $body:expr, } => {
86        match $result {
87            $var => $body,
88        }
89    };
90    { $result:expr; $var:pat => $body:expr, $($vars:pat => $bodys:expr,)+ } => {
91        match $result {
92            ::core::result::Result::Ok($var) => $body,
93            ::core::result::Result::Err(r) => $crate::select_body!{r; $($vars => $bodys,)*},
94        }
95    };
96}
97
98#[macro_export]
99#[doc(hidden)]
100macro_rules! select_sleep {
101    ($event:expr; $_:expr,) => {$crate::rtos::Selectable::sleep(&$event)};
102    ($events:expr; $_:expr, $($rest:expr,)+) => {
103        $crate::rtos::Selectable::sleep(&$events.0).combine($crate::select_sleep!($events.1; $($rest,)+))
104    };
105}
106
107#[macro_export]
108/// Generates a future event (i.e. one which implements
109/// [`crate::rtos::Selectable`]) from a similar recipe as the [`select!`]
110/// macro, combining the behaviour of [`select_map`](crate::rtos::select_map)
111/// and [`select_any!`](crate::select_any!).
112///
113/// There is one important difference to note between this macro and
114/// [`select!`]: since this macro needs to generate an object containing the
115/// event processing recipe, the body expressions are placed inside lambdas, and
116/// therefore contextual expressions such as `break`, `continue` and `return`
117/// are not valid.
118macro_rules! select_merge {
119    { $( $var:pat = $event:expr $(; $sub:pat = $dep:expr)* => $body:expr ),+ $(,)? } => {{
120        #[allow(clippy::redundant_closure)]
121        let r = $crate::select_any!($($crate::rtos::select_map($event, |$var| $body) $(; $sub = $dep)*),+);
122        r
123    }};
124}
125
126#[macro_export]
127/// Generates a future event (i.e. one which implements
128/// [`crate::rtos::Selectable`]) from a set of events which all have the same
129/// result type, by repeated application of [`crate::rtos::select_either`].
130macro_rules! select_any {
131    ($event:expr $(; $sub:pat = $dep:expr)* $(,)?) => {
132        $crate::select_init!($event $(; $sub = $dep)*)
133    };
134    ($event:expr $(; $sub1:pat = $dep1:expr)*, $($rest:expr $(; $sub:pat = $dep:expr)*),+ $(,)?) => {
135        $crate::rtos::select_either($crate::select_init!($event $(; $sub1 = $dep1)*), $crate::select_any!($($rest $(; $sub = $dep)*),+))
136    };
137}