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;
3435/// Create a new ping event source
36///
37/// you are given a [`Ping`] instance, which can be cloned and used to ping the
38/// event loop, and a [`PingSource`], which you can insert in your event loop to
39/// receive the pings.
40pub fn make_ping() -> std::io::Result<(Ping, PingSource)> {
41 platform::make_ping()
42}
4344/// The ping event source
45///
46/// You can insert it in your event loop to receive pings.
47///
48/// If you use it directly, it will automatically remove itself from the event loop
49/// once all [`Ping`] instances are dropped.
50pub type Ping = platform::Ping;
5152/// The Ping handle
53///
54/// This handle can be cloned and sent accross threads. It can be used to
55/// send pings to the `PingSource`.
56pub type PingSource = platform::PingSource;
5758/// An error arising from processing events for a ping.
59#[derive(thiserror::Error, Debug)]
60#[error(transparent)]
61pub struct PingError(Box<dyn std::error::Error + Sync + Send>);
6263#[cfg(test)]
64mod tests {
65use crate::transient::TransientSource;
66use std::time::Duration;
6768use super::*;
6970#[test]
71fn ping() {
72let mut event_loop = crate::EventLoop::<bool>::try_new().unwrap();
7374let (ping, source) = make_ping().unwrap();
7576 event_loop
77 .handle()
78 .insert_source(source, |(), &mut (), dispatched| *dispatched = true)
79 .unwrap();
8081 ping.ping();
8283let mut dispatched = false;
84 event_loop
85 .dispatch(std::time::Duration::ZERO, &mut dispatched)
86 .unwrap();
87assert!(dispatched);
8889// Ping has been drained an no longer generates events
90let mut dispatched = false;
91 event_loop
92 .dispatch(std::time::Duration::ZERO, &mut dispatched)
93 .unwrap();
94assert!(!dispatched);
95 }
9697#[test]
98fn ping_closed() {
99let mut event_loop = crate::EventLoop::<bool>::try_new().unwrap();
100101let (_, source) = make_ping().unwrap();
102 event_loop
103 .handle()
104 .insert_source(source, |(), &mut (), dispatched| *dispatched = true)
105 .unwrap();
106107let mut dispatched = false;
108109// If the sender is closed from the start, the ping should first trigger
110 // once, disabling itself but not invoking the callback
111event_loop
112 .dispatch(std::time::Duration::ZERO, &mut dispatched)
113 .unwrap();
114assert!(!dispatched);
115116// Then it should not trigger any more, so this dispatch should wait the whole 100ms
117let now = std::time::Instant::now();
118 event_loop
119 .dispatch(std::time::Duration::from_millis(100), &mut dispatched)
120 .unwrap();
121assert!(now.elapsed() >= std::time::Duration::from_millis(100));
122 }
123124#[test]
125fn ping_removed() {
126// This keeps track of whether the event fired.
127let mut dispatched = false;
128129let mut event_loop = crate::EventLoop::<bool>::try_new().unwrap();
130131let (sender, source) = make_ping().unwrap();
132let wrapper = TransientSource::from(source);
133134// Check that the source starts off in the wrapper.
135assert!(!wrapper.is_none());
136137// Put the source in the loop.
138139let dispatcher =
140crate::Dispatcher::new(wrapper, |(), &mut (), dispatched| *dispatched = true);
141142let token = event_loop
143 .handle()
144 .register_dispatcher(dispatcher.clone())
145 .unwrap();
146147// Drop the sender and check that it's actually removed.
148drop(sender);
149150// There should be no event, but the loop still needs to wake up to
151 // process the close event (just like in the ping_closed() test).
152event_loop
153 .dispatch(Duration::ZERO, &mut dispatched)
154 .unwrap();
155assert!(!dispatched);
156157// Pull the source wrapper out.
158159event_loop.handle().remove(token);
160let wrapper = dispatcher.into_source_inner();
161162// Check that the inner source is now gone.
163assert!(wrapper.is_none());
164 }
165166#[test]
167fn ping_fired_and_removed() {
168// This is like ping_removed() with the single difference that we fire a
169 // ping and drop it between two successive dispatches of the loop.
170171 // This keeps track of whether the event fired.
172let mut dispatched = false;
173174let mut event_loop = crate::EventLoop::<bool>::try_new().unwrap();
175176let (sender, source) = make_ping().unwrap();
177let wrapper = TransientSource::from(source);
178179// Check that the source starts off in the wrapper.
180assert!(!wrapper.is_none());
181182// Put the source in the loop.
183184let dispatcher =
185crate::Dispatcher::new(wrapper, |(), &mut (), dispatched| *dispatched = true);
186187let token = event_loop
188 .handle()
189 .register_dispatcher(dispatcher.clone())
190 .unwrap();
191192// Send a ping AND drop the sender and check that it's actually removed.
193sender.ping();
194 drop(sender);
195196// There should be an event, but the source should be removed from the
197 // loop immediately after.
198event_loop
199 .dispatch(Duration::ZERO, &mut dispatched)
200 .unwrap();
201assert!(dispatched);
202203// Pull the source wrapper out.
204205event_loop.handle().remove(token);
206let wrapper = dispatcher.into_source_inner();
207208// Check that the inner source is now gone.
209assert!(wrapper.is_none());
210 }
211212#[test]
213fn ping_multiple_senders() {
214// This is like ping_removed() but for testing the behaviour of multiple
215 // senders.
216217 // This keeps track of whether the event fired.
218let mut dispatched = false;
219220let mut event_loop = crate::EventLoop::<bool>::try_new().unwrap();
221222let (sender0, source) = make_ping().unwrap();
223let wrapper = TransientSource::from(source);
224let sender1 = sender0.clone();
225let sender2 = sender1.clone();
226227// Check that the source starts off in the wrapper.
228assert!(!wrapper.is_none());
229230// Put the source in the loop.
231232let dispatcher =
233crate::Dispatcher::new(wrapper, |(), &mut (), dispatched| *dispatched = true);
234235let token = event_loop
236 .handle()
237 .register_dispatcher(dispatcher.clone())
238 .unwrap();
239240// Send a ping AND drop the sender and check that it's actually removed.
241sender0.ping();
242 drop(sender0);
243244// There should be an event, and the source should remain in the loop.
245event_loop
246 .dispatch(Duration::ZERO, &mut dispatched)
247 .unwrap();
248assert!(dispatched);
249250// Now test that the clones still work. Drop after the dispatch loop
251 // instead of before, this time.
252dispatched = false;
253254 sender1.ping();
255256 event_loop
257 .dispatch(Duration::ZERO, &mut dispatched)
258 .unwrap();
259assert!(dispatched);
260261// Finally, drop all of them without sending anything.
262263dispatched = false;
264265 drop(sender1);
266 drop(sender2);
267268 event_loop
269 .dispatch(Duration::ZERO, &mut dispatched)
270 .unwrap();
271assert!(!dispatched);
272273// Pull the source wrapper out.
274275event_loop.handle().remove(token);
276let wrapper = dispatcher.into_source_inner();
277278// Check that the inner source is now gone.
279assert!(wrapper.is_none());
280 }
281}