1//! Ping to the event loop
2//!
3//! This is an event source that just produces `()` events whevener the associated
4//! [`Ping::ping`](Ping#method.ping) method is called. If the event source is pinged multiple times
5//! between a single dispatching, it'll only generate one event.
6//!
7//! This event source is a simple way of waking up the event loop from an other part of your program
8//! (and is what backs the [`LoopSignal`](crate::LoopSignal)). It can also be used as a building
9//! block to construct event sources whose source of event is not file descriptor, but rather an
10//! userspace source (like an other thread).
11
12// The ping source has platform-dependent implementations provided by modules
13// under this one. These modules should expose:
14// - a make_ping() function
15// - a Ping type
16// - a PingSource type
17//
18// See eg. the pipe implementation for these items' specific requirements.
19
20#[cfg(target_os = "linux")]
21mod eventfd;
22#[cfg(target_os = "linux")]
23use eventfd as platform;
24
25#[cfg(windows)]
26mod iocp;
27#[cfg(windows)]
28use iocp as platform;
29
30#[cfg(not(any(target_os = "linux", windows)))]
31mod pipe;
32#[cfg(not(any(target_os = "linux", windows)))]
33use pipe as platform;
34
35use std::fmt;
36
37/// Create a new ping event source
38///
39/// you are given a [`Ping`] instance, which can be cloned and used to ping the
40/// event loop, and a [`PingSource`], which you can insert in your event loop to
41/// receive the pings.
42pub fn make_ping() -> std::io::Result<(Ping, PingSource)> {
43 platform::make_ping()
44}
45
46/// The ping event source
47///
48/// You can insert it in your event loop to receive pings.
49///
50/// If you use it directly, it will automatically remove itself from the event loop
51/// once all [`Ping`] instances are dropped.
52pub type Ping = platform::Ping;
53
54/// The Ping handle
55///
56/// This handle can be cloned and sent accross threads. It can be used to
57/// send pings to the `PingSource`.
58pub type PingSource = platform::PingSource;
59
60/// An error arising from processing events for a ping.
61#[derive(Debug)]
62pub struct PingError(Box<dyn std::error::Error + Sync + Send>);
63
64impl fmt::Display for PingError {
65 #[cfg_attr(feature = "nightly_coverage", coverage(off))]
66 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
67 fmt::Display::fmt(&self.0, f)
68 }
69}
70
71impl std::error::Error for PingError {
72 #[cfg_attr(feature = "nightly_coverage", coverage(off))]
73 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
74 Some(&*self.0)
75 }
76}
77
78#[cfg(test)]
79mod tests {
80 use crate::transient::TransientSource;
81 use std::time::Duration;
82
83 use super::*;
84
85 #[test]
86 fn ping() {
87 let mut event_loop = crate::EventLoop::<bool>::try_new().unwrap();
88
89 let (ping, source) = make_ping().unwrap();
90
91 event_loop
92 .handle()
93 .insert_source(source, |(), &mut (), dispatched| *dispatched = true)
94 .unwrap();
95
96 ping.ping();
97
98 let mut dispatched = false;
99 event_loop
100 .dispatch(std::time::Duration::ZERO, &mut dispatched)
101 .unwrap();
102 assert!(dispatched);
103
104 // Ping has been drained an no longer generates events
105 let mut dispatched = false;
106 event_loop
107 .dispatch(std::time::Duration::ZERO, &mut dispatched)
108 .unwrap();
109 assert!(!dispatched);
110 }
111
112 #[test]
113 fn ping_closed() {
114 let mut event_loop = crate::EventLoop::<bool>::try_new().unwrap();
115
116 let (_, source) = make_ping().unwrap();
117 event_loop
118 .handle()
119 .insert_source(source, |(), &mut (), dispatched| *dispatched = true)
120 .unwrap();
121
122 let mut dispatched = false;
123
124 // If the sender is closed from the start, the ping should first trigger
125 // once, disabling itself but not invoking the callback
126 event_loop
127 .dispatch(std::time::Duration::ZERO, &mut dispatched)
128 .unwrap();
129 assert!(!dispatched);
130
131 // Then it should not trigger any more, so this dispatch should wait the whole 100ms
132 let now = std::time::Instant::now();
133 event_loop
134 .dispatch(std::time::Duration::from_millis(100), &mut dispatched)
135 .unwrap();
136 assert!(now.elapsed() >= std::time::Duration::from_millis(100));
137 }
138
139 #[test]
140 fn ping_removed() {
141 // This keeps track of whether the event fired.
142 let mut dispatched = false;
143
144 let mut event_loop = crate::EventLoop::<bool>::try_new().unwrap();
145
146 let (sender, source) = make_ping().unwrap();
147 let wrapper = TransientSource::from(source);
148
149 // Check that the source starts off in the wrapper.
150 assert!(!wrapper.is_none());
151
152 // Put the source in the loop.
153
154 let dispatcher =
155 crate::Dispatcher::new(wrapper, |(), &mut (), dispatched| *dispatched = true);
156
157 let token = event_loop
158 .handle()
159 .register_dispatcher(dispatcher.clone())
160 .unwrap();
161
162 // Drop the sender and check that it's actually removed.
163 drop(sender);
164
165 // There should be no event, but the loop still needs to wake up to
166 // process the close event (just like in the ping_closed() test).
167 event_loop
168 .dispatch(Duration::ZERO, &mut dispatched)
169 .unwrap();
170 assert!(!dispatched);
171
172 // Pull the source wrapper out.
173
174 event_loop.handle().remove(token);
175 let wrapper = dispatcher.into_source_inner();
176
177 // Check that the inner source is now gone.
178 assert!(wrapper.is_none());
179 }
180
181 #[test]
182 fn ping_fired_and_removed() {
183 // This is like ping_removed() with the single difference that we fire a
184 // ping and drop it between two successive dispatches of the loop.
185
186 // This keeps track of whether the event fired.
187 let mut dispatched = false;
188
189 let mut event_loop = crate::EventLoop::<bool>::try_new().unwrap();
190
191 let (sender, source) = make_ping().unwrap();
192 let wrapper = TransientSource::from(source);
193
194 // Check that the source starts off in the wrapper.
195 assert!(!wrapper.is_none());
196
197 // Put the source in the loop.
198
199 let dispatcher =
200 crate::Dispatcher::new(wrapper, |(), &mut (), dispatched| *dispatched = true);
201
202 let token = event_loop
203 .handle()
204 .register_dispatcher(dispatcher.clone())
205 .unwrap();
206
207 // Send a ping AND drop the sender and check that it's actually removed.
208 sender.ping();
209 drop(sender);
210
211 // There should be an event, but the source should be removed from the
212 // loop immediately after.
213 event_loop
214 .dispatch(Duration::ZERO, &mut dispatched)
215 .unwrap();
216 assert!(dispatched);
217
218 // Pull the source wrapper out.
219
220 event_loop.handle().remove(token);
221 let wrapper = dispatcher.into_source_inner();
222
223 // Check that the inner source is now gone.
224 assert!(wrapper.is_none());
225 }
226
227 #[test]
228 fn ping_multiple_senders() {
229 // This is like ping_removed() but for testing the behaviour of multiple
230 // senders.
231
232 // This keeps track of whether the event fired.
233 let mut dispatched = false;
234
235 let mut event_loop = crate::EventLoop::<bool>::try_new().unwrap();
236
237 let (sender0, source) = make_ping().unwrap();
238 let wrapper = TransientSource::from(source);
239 let sender1 = sender0.clone();
240 let sender2 = sender1.clone();
241
242 // Check that the source starts off in the wrapper.
243 assert!(!wrapper.is_none());
244
245 // Put the source in the loop.
246
247 let dispatcher =
248 crate::Dispatcher::new(wrapper, |(), &mut (), dispatched| *dispatched = true);
249
250 let token = event_loop
251 .handle()
252 .register_dispatcher(dispatcher.clone())
253 .unwrap();
254
255 // Send a ping AND drop the sender and check that it's actually removed.
256 sender0.ping();
257 drop(sender0);
258
259 // There should be an event, and the source should remain in the loop.
260 event_loop
261 .dispatch(Duration::ZERO, &mut dispatched)
262 .unwrap();
263 assert!(dispatched);
264
265 // Now test that the clones still work. Drop after the dispatch loop
266 // instead of before, this time.
267 dispatched = false;
268
269 sender1.ping();
270
271 event_loop
272 .dispatch(Duration::ZERO, &mut dispatched)
273 .unwrap();
274 assert!(dispatched);
275
276 // Finally, drop all of them without sending anything.
277
278 dispatched = false;
279
280 drop(sender1);
281 drop(sender2);
282
283 event_loop
284 .dispatch(Duration::ZERO, &mut dispatched)
285 .unwrap();
286 assert!(!dispatched);
287
288 // Pull the source wrapper out.
289
290 event_loop.handle().remove(token);
291 let wrapper = dispatcher.into_source_inner();
292
293 // Check that the inner source is now gone.
294 assert!(wrapper.is_none());
295 }
296}
297