calloop/
macros.rs

1//! Macros for helping with common operations in Calloop.
2
3/// Register a set of event sources. Effectively calls
4/// [`EventSource::register()`] for all the sources provided.
5///
6/// Usage:
7///
8/// ```none,actually-rust-but-see-https://github.com/rust-lang/rust/issues/63193
9/// calloop::batch_register!(
10///     poll, token_factory,
11///     self.source_one,
12///     self.source_two,
13///     self.source_three,
14///     self.source_four,
15/// )
16/// ```
17///
18/// Note that there is no scope for customisation; if you need to do special
19/// things with a particular source, you'll need to leave it off the list. Also
20/// note that this only does try-or-early-return error handling in the order
21/// that you list the sources; if you need anything else, don't use this macro.
22///
23/// [`EventSource::register()`]: crate::EventSource::register()
24#[macro_export]
25macro_rules! batch_register {
26    ($poll:ident, $token_fac:ident, $( $source:expr ),* $(,)?) => {
27        {
28            $(
29                $source.register($poll, $token_fac)?;
30            )*
31                $crate::Result::<_>::Ok(())
32        }
33    };
34}
35
36/// Reregister a set of event sources. Effectively calls
37/// [`EventSource::reregister()`] for all the sources provided.
38///
39/// Usage:
40///
41/// ```none,actually-rust-but-see-https://github.com/rust-lang/rust/issues/63193
42/// calloop::batch_reregister!(
43///     poll, token_factory,
44///     self.source_one,
45///     self.source_two,
46///     self.source_three,
47///     self.source_four,
48/// )
49/// ```
50///
51/// Note that there is no scope for customisation; if you need to do special
52/// things with a particular source, you'll need to leave it off the list. Also
53/// note that this only does try-or-early-return error handling in the order
54/// that you list the sources; if you need anything else, don't use this macro.
55///
56/// [`EventSource::reregister()`]: crate::EventSource::reregister()
57#[macro_export]
58macro_rules! batch_reregister {
59    ($poll:ident, $token_fac:ident, $( $source:expr ),* $(,)?) => {
60        {
61            $(
62                $source.reregister($poll, $token_fac)?;
63            )*
64                $crate::Result::<_>::Ok(())
65        }
66    };
67}
68
69/// Unregister a set of event sources. Effectively calls
70/// [`EventSource::unregister()`] for all the sources provided.
71///
72/// Usage:
73///
74/// ```none,actually-rust-but-see-https://github.com/rust-lang/rust/issues/63193
75/// calloop::batch_unregister!(
76///     poll,
77///     self.source_one,
78///     self.source_two,
79///     self.source_three,
80///     self.source_four,
81/// )
82/// ```
83///
84/// Note that there is no scope for customisation; if you need to do special
85/// things with a particular source, you'll need to leave it off the list. Also
86/// note that this only does try-or-early-return error handling in the order
87/// that you list the sources; if you need anything else, don't use this macro.
88///
89/// [`EventSource::unregister()`]: crate::EventSource::unregister()
90#[macro_export]
91macro_rules! batch_unregister {
92    ($poll:ident, $( $source:expr ),* $(,)?) => {
93        {
94            $(
95                $source.unregister($poll)?;
96            )*
97                $crate::Result::<_>::Ok(())
98        }
99    };
100}
101
102#[cfg(test)]
103mod tests {
104    use std::time::Duration;
105
106    use crate::{
107        ping::{make_ping, PingSource},
108        EventSource, PostAction,
109    };
110
111    struct BatchSource {
112        ping0: PingSource,
113        ping1: PingSource,
114        ping2: PingSource,
115    }
116
117    impl EventSource for BatchSource {
118        type Event = usize;
119        type Metadata = ();
120        type Ret = ();
121        type Error = Box<dyn std::error::Error + Sync + Send>;
122
123        fn process_events<F>(
124            &mut self,
125            readiness: crate::Readiness,
126            token: crate::Token,
127            mut callback: F,
128        ) -> Result<crate::PostAction, Self::Error>
129        where
130            F: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret,
131        {
132            self.ping0
133                .process_events(readiness, token, |_, m| callback(0, m))?;
134            self.ping1
135                .process_events(readiness, token, |_, m| callback(1, m))?;
136            self.ping2
137                .process_events(readiness, token, |_, m| callback(2, m))?;
138            Ok(PostAction::Continue)
139        }
140
141        fn register(
142            &mut self,
143            poll: &mut crate::Poll,
144            token_factory: &mut crate::TokenFactory,
145        ) -> crate::Result<()> {
146            crate::batch_register!(poll, token_factory, self.ping0, self.ping1, self.ping2)
147        }
148
149        fn reregister(
150            &mut self,
151            poll: &mut crate::Poll,
152            token_factory: &mut crate::TokenFactory,
153        ) -> crate::Result<()> {
154            crate::batch_reregister!(poll, token_factory, self.ping0, self.ping1, self.ping2)
155        }
156
157        fn unregister(&mut self, poll: &mut crate::Poll) -> crate::Result<()> {
158            crate::batch_unregister!(poll, self.ping0, self.ping1, self.ping2)
159        }
160    }
161
162    #[test]
163    fn test_batch_operations() {
164        let mut fired = [false; 3];
165
166        let (send0, ping0) = make_ping().unwrap();
167        let (send1, ping1) = make_ping().unwrap();
168        let (send2, ping2) = make_ping().unwrap();
169
170        let top = BatchSource {
171            ping0,
172            ping1,
173            ping2,
174        };
175
176        let mut event_loop = crate::EventLoop::<[bool; 3]>::try_new().unwrap();
177        let handle = event_loop.handle();
178
179        let token = handle
180            .insert_source(top, |idx, _, fired| {
181                fired[idx] = true;
182            })
183            .unwrap();
184
185        send0.ping();
186        send1.ping();
187        send2.ping();
188
189        event_loop
190            .dispatch(Duration::new(0, 0), &mut fired)
191            .unwrap();
192
193        assert_eq!(fired, [true; 3]);
194
195        fired = [false; 3];
196
197        handle.update(&token).unwrap();
198
199        send0.ping();
200        send1.ping();
201        send2.ping();
202
203        event_loop
204            .dispatch(Duration::new(0, 0), &mut fired)
205            .unwrap();
206
207        assert_eq!(fired, [true; 3]);
208
209        fired = [false; 3];
210
211        handle.remove(token);
212
213        send0.ping();
214        send1.ping();
215        send2.ping();
216
217        event_loop
218            .dispatch(Duration::new(0, 0), &mut fired)
219            .unwrap();
220
221        assert_eq!(fired, [false; 3]);
222    }
223}