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).
1112// 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.
1920#[cfg(target_os = "linux")]
21mod eventfd;
22#[cfg(target_os = "linux")]
23use eventfd as platform;
2425#[cfg(windows)]
26mod iocp;
27#[cfg(windows)]
28use iocp as platform;
2930#[cfg(not(any(target_os = "linux", windows)))]
31mod pipe;
32#[cfg(not(any(target_os = "linux", windows)))]
33use pipe as platform;
3435use std::fmt;
3637/// 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}
4546/// 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;
5354/// 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;
5960/// An error arising from processing events for a ping.
61#[derive(Debug)]
62pub struct PingError(Box<dyn std::error::Error + Sync + Send>);
6364impl fmt::Display for PingError {
65#[cfg_attr(feature = "nightly_coverage", coverage(off))]
66fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
67 fmt::Display::fmt(&self.0, f)
68 }
69}
7071impl std::error::Error for PingError {
72#[cfg_attr(feature = "nightly_coverage", coverage(off))]
73fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
74Some(&*self.0)
75 }
76}
7778#[cfg(test)]
79mod tests {
80use crate::transient::TransientSource;
81use std::time::Duration;
8283use super::*;
8485#[test]
86fn ping() {
87let mut event_loop = crate::EventLoop::<bool>::try_new().unwrap();
8889let (ping, source) = make_ping().unwrap();
9091 event_loop
92 .handle()
93 .insert_source(source, |(), &mut (), dispatched| *dispatched = true)
94 .unwrap();
9596 ping.ping();
9798let mut dispatched = false;
99 event_loop
100 .dispatch(std::time::Duration::ZERO, &mut dispatched)
101 .unwrap();
102assert!(dispatched);
103104// Ping has been drained an no longer generates events
105let mut dispatched = false;
106 event_loop
107 .dispatch(std::time::Duration::ZERO, &mut dispatched)
108 .unwrap();
109assert!(!dispatched);
110 }
111112#[test]
113fn ping_closed() {
114let mut event_loop = crate::EventLoop::<bool>::try_new().unwrap();
115116let (_, source) = make_ping().unwrap();
117 event_loop
118 .handle()
119 .insert_source(source, |(), &mut (), dispatched| *dispatched = true)
120 .unwrap();
121122let mut dispatched = false;
123124// If the sender is closed from the start, the ping should first trigger
125 // once, disabling itself but not invoking the callback
126event_loop
127 .dispatch(std::time::Duration::ZERO, &mut dispatched)
128 .unwrap();
129assert!(!dispatched);
130131// Then it should not trigger any more, so this dispatch should wait the whole 100ms
132let now = std::time::Instant::now();
133 event_loop
134 .dispatch(std::time::Duration::from_millis(100), &mut dispatched)
135 .unwrap();
136assert!(now.elapsed() >= std::time::Duration::from_millis(100));
137 }
138139#[test]
140fn ping_removed() {
141// This keeps track of whether the event fired.
142let mut dispatched = false;
143144let mut event_loop = crate::EventLoop::<bool>::try_new().unwrap();
145146let (sender, source) = make_ping().unwrap();
147let wrapper = TransientSource::from(source);
148149// Check that the source starts off in the wrapper.
150assert!(!wrapper.is_none());
151152// Put the source in the loop.
153154let dispatcher =
155crate::Dispatcher::new(wrapper, |(), &mut (), dispatched| *dispatched = true);
156157let token = event_loop
158 .handle()
159 .register_dispatcher(dispatcher.clone())
160 .unwrap();
161162// Drop the sender and check that it's actually removed.
163drop(sender);
164165// 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).
167event_loop
168 .dispatch(Duration::ZERO, &mut dispatched)
169 .unwrap();
170assert!(!dispatched);
171172// Pull the source wrapper out.
173174event_loop.handle().remove(token);
175let wrapper = dispatcher.into_source_inner();
176177// Check that the inner source is now gone.
178assert!(wrapper.is_none());
179 }
180181#[test]
182fn 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.
185186 // This keeps track of whether the event fired.
187let mut dispatched = false;
188189let mut event_loop = crate::EventLoop::<bool>::try_new().unwrap();
190191let (sender, source) = make_ping().unwrap();
192let wrapper = TransientSource::from(source);
193194// Check that the source starts off in the wrapper.
195assert!(!wrapper.is_none());
196197// Put the source in the loop.
198199let dispatcher =
200crate::Dispatcher::new(wrapper, |(), &mut (), dispatched| *dispatched = true);
201202let token = event_loop
203 .handle()
204 .register_dispatcher(dispatcher.clone())
205 .unwrap();
206207// Send a ping AND drop the sender and check that it's actually removed.
208sender.ping();
209 drop(sender);
210211// There should be an event, but the source should be removed from the
212 // loop immediately after.
213event_loop
214 .dispatch(Duration::ZERO, &mut dispatched)
215 .unwrap();
216assert!(dispatched);
217218// Pull the source wrapper out.
219220event_loop.handle().remove(token);
221let wrapper = dispatcher.into_source_inner();
222223// Check that the inner source is now gone.
224assert!(wrapper.is_none());
225 }
226227#[test]
228fn ping_multiple_senders() {
229// This is like ping_removed() but for testing the behaviour of multiple
230 // senders.
231232 // This keeps track of whether the event fired.
233let mut dispatched = false;
234235let mut event_loop = crate::EventLoop::<bool>::try_new().unwrap();
236237let (sender0, source) = make_ping().unwrap();
238let wrapper = TransientSource::from(source);
239let sender1 = sender0.clone();
240let sender2 = sender1.clone();
241242// Check that the source starts off in the wrapper.
243assert!(!wrapper.is_none());
244245// Put the source in the loop.
246247let dispatcher =
248crate::Dispatcher::new(wrapper, |(), &mut (), dispatched| *dispatched = true);
249250let token = event_loop
251 .handle()
252 .register_dispatcher(dispatcher.clone())
253 .unwrap();
254255// Send a ping AND drop the sender and check that it's actually removed.
256sender0.ping();
257 drop(sender0);
258259// There should be an event, and the source should remain in the loop.
260event_loop
261 .dispatch(Duration::ZERO, &mut dispatched)
262 .unwrap();
263assert!(dispatched);
264265// Now test that the clones still work. Drop after the dispatch loop
266 // instead of before, this time.
267dispatched = false;
268269 sender1.ping();
270271 event_loop
272 .dispatch(Duration::ZERO, &mut dispatched)
273 .unwrap();
274assert!(dispatched);
275276// Finally, drop all of them without sending anything.
277278dispatched = false;
279280 drop(sender1);
281 drop(sender2);
282283 event_loop
284 .dispatch(Duration::ZERO, &mut dispatched)
285 .unwrap();
286assert!(!dispatched);
287288// Pull the source wrapper out.
289290event_loop.handle().remove(token);
291let wrapper = dispatcher.into_source_inner();
292293// Check that the inner source is now gone.
294assert!(wrapper.is_none());
295 }
296}