event_listener_strategy/lib.rs
1// SPDX-Licenser-Identifier: MIT OR Apache-2.0
2//! A strategy for using the [`event-listener`] crate in both blocking and non-blocking contexts.
3//!
4//! One of the stand-out features of the [`event-listener`] crate is the ability to use it in both
5//! asynchronous and synchronous contexts. However, sometimes using it like this causes a lot of
6//! boilerplate to be duplicated. This crate aims to reduce that boilerplate by providing an
7//! [`EventListenerFuture`] trait that implements both blocking and non-blocking functionality.
8//!
9//! # Examples
10//!
11//! ```
12//! use event_listener_strategy::{
13//! event_listener::{Event, EventListener},
14//! EventListenerFuture, FutureWrapper, Strategy
15//! };
16//!
17//! use std::pin::Pin;
18//! use std::task::Poll;
19//! use std::thread;
20//! use std::sync::Arc;
21//!
22//! // A future that waits three seconds for an event to be fired.
23//! fn wait_three_seconds() -> WaitThreeSeconds {
24//! let event = Event::new();
25//! let listener = event.listen();
26//!
27//! thread::spawn(move || {
28//! thread::sleep(std::time::Duration::from_secs(3));
29//! event.notify(1);
30//! });
31//!
32//! WaitThreeSeconds { listener: Some(listener) }
33//! }
34//!
35//! struct WaitThreeSeconds {
36//! listener: Option<EventListener>,
37//! }
38//!
39//! impl EventListenerFuture for WaitThreeSeconds {
40//! type Output = ();
41//!
42//! fn poll_with_strategy<'a, S: Strategy<'a>>(
43//! mut self: Pin<&mut Self>,
44//! strategy: &mut S,
45//! context: &mut S::Context,
46//! ) -> Poll<Self::Output> {
47//! strategy.poll(&mut self.listener, context)
48//! }
49//! }
50//!
51//! // Use the future in a blocking context.
52//! let future = wait_three_seconds();
53//! future.wait();
54//!
55//! // Use the future in a non-blocking context.
56//! futures_lite::future::block_on(async {
57//! let future = FutureWrapper::new(wait_three_seconds());
58//! future.await;
59//! });
60//! ```
61
62#![cfg_attr(not(feature = "std"), no_std)]
63#![cfg_attr(docsrs, feature(doc_cfg))]
64#![forbid(future_incompatible, missing_docs)]
65#![doc(
66 html_favicon_url = "https://raw.githubusercontent.com/smol-rs/smol/master/assets/images/logo_fullsize_transparent.png"
67)]
68#![doc(
69 html_logo_url = "https://raw.githubusercontent.com/smol-rs/smol/master/assets/images/logo_fullsize_transparent.png"
70)]
71
72use core::future::Future;
73use core::marker::PhantomData;
74use core::pin::Pin;
75use core::task::{Context, Poll};
76
77use event_listener::{EventListener, Listener};
78
79#[doc(hidden)]
80pub use pin_project_lite::pin_project;
81
82#[doc(no_inline)]
83pub use event_listener;
84
85/// A wrapper around an [`EventListenerFuture`] that can be easily exported for use.
86///
87/// This type implements [`Future`], has a `_new()` constructor, and a `wait()` method
88/// that uses the [`Blocking`] strategy to poll the future until it is ready.
89///
90/// # Examples
91///
92/// ```
93/// mod my_future {
94/// use event_listener_strategy::{easy_wrapper, EventListenerFuture, Strategy};
95/// use std::pin::Pin;
96/// use std::task::Poll;
97///
98/// struct MyFuture;
99///
100/// impl EventListenerFuture for MyFuture {
101/// type Output = ();
102///
103/// fn poll_with_strategy<'a, S: Strategy<'a>>(
104/// self: Pin<&mut Self>,
105/// strategy: &mut S,
106/// context: &mut S::Context,
107/// ) -> Poll<Self::Output> {
108/// /* ... */
109/// # Poll::Ready(())
110/// }
111/// }
112///
113/// easy_wrapper! {
114/// /// A future that does something.
115/// pub struct MyFutureWrapper(MyFuture => ());
116/// /// Wait for it.
117/// pub wait();
118/// }
119///
120/// impl MyFutureWrapper {
121/// /// Create a new instance of the future.
122/// pub fn new() -> Self {
123/// Self::_new(MyFuture)
124/// }
125/// }
126/// }
127///
128/// use my_future::MyFutureWrapper;
129///
130/// // Use the future in a blocking context.
131/// let future = MyFutureWrapper::new();
132/// future.wait();
133///
134/// // Use the future in a non-blocking context.
135/// futures_lite::future::block_on(async {
136/// let future = MyFutureWrapper::new();
137/// future.await;
138/// });
139/// ```
140#[macro_export]
141macro_rules! easy_wrapper {
142 (
143 $(#[$meta:meta])*
144 $vis:vis struct $name:ident
145
146 $(<
147 $( $lifetime:lifetime $(: $lifetime_bound:lifetime)? ),* $(,)?
148 $( $generics:ident
149 $(: $generics_bound:path)?
150 $(: ?$generics_unsized_bound:path)?
151 $(: $generics_lifetime_bound:lifetime)?
152 $(= $generics_default:ty)?
153 ),* $(,)?
154 >)?
155
156 ($inner:ty => $output:ty)
157
158 $(where
159 $( $where_clause_ty:ty
160 $(: $where_clause_bound:path)?
161 $(: ?$where_clause_unsized_bound:path)?
162 $(: $where_clause_lifetime_bound:lifetime)?
163 ),* $(,)?
164 )?
165
166 ;
167
168 $(#[$wait_meta:meta])*
169 $wait_vis: vis wait();
170 ) => {
171 $crate::pin_project! {
172 $(#[$meta])*
173 $vis struct $name $(<
174 $( $lifetime $(: $lifetime_bound)? ),*
175 $( $generics
176 $(: $generics_bound)?
177 $(: ?$generics_unsized_bound)?
178 $(: $generics_lifetime_bound)?
179 $(= $generics_default)?
180 ),*
181 >)? $(
182 where
183 $( $where_clause_ty
184 $(: $where_clause_bound)?
185 $(: ?$where_clause_unsized_bound)?
186 $(: $where_clause_lifetime_bound)?
187 ),*
188 )? {
189 #[pin]
190 _inner: $crate::FutureWrapper<$inner>
191 }
192 }
193
194 impl $(<
195 $( $lifetime $(: $lifetime_bound)? ,)*
196 $( $generics
197 $(: $generics_bound)?
198 $(: ?$generics_unsized_bound)?
199 $(: $generics_lifetime_bound)?
200 $(= $generics_default)?
201 ),*
202 >)? $name $(<
203 $( $lifetime ,)*
204 $( $generics ),*
205 >)? $(
206 where
207 $( $where_clause_ty
208 $(: $where_clause_bound)?
209 $(: ?$where_clause_unsized_bound)?
210 $(: $where_clause_lifetime_bound)?
211 ),*
212 )? {
213 #[inline]
214 fn _new(inner: $inner) -> Self {
215 Self {
216 _inner: $crate::FutureWrapper::new(inner)
217 }
218 }
219
220 $(#[$wait_meta])*
221 #[inline]
222 $wait_vis fn wait(self) -> $output {
223 use $crate::EventListenerFuture;
224 self._inner.into_inner().wait()
225 }
226
227 pub(crate) fn poll_with_strategy<'__strategy, __S: $crate::Strategy<'__strategy>>(
228 self: ::core::pin::Pin<&mut Self>,
229 strategy: &mut __S,
230 context: &mut __S::Context,
231 ) -> ::core::task::Poll<$output> {
232 self.project()._inner.get_pin_mut().poll_with_strategy(strategy, context)
233 }
234 }
235
236 impl $(<
237 $( $lifetime $(: $lifetime_bound)? ,)*
238 $( $generics
239 $(: $generics_bound)?
240 $(: ?$generics_unsized_bound)?
241 $(: $generics_lifetime_bound)?
242 $(= $generics_default)?
243 ),*
244 >)? ::core::future::Future for $name $(
245 <
246 $( $lifetime ,)*
247 $( $generics ),*
248 >
249 )? $(
250 where
251 $( $where_clause_ty
252 $(: $where_clause_bound)?
253 $(: ?$where_clause_unsized_bound)?
254 $(: $where_clause_lifetime_bound)?
255 ),*
256 )? {
257 type Output = $output;
258
259 #[inline]
260 fn poll(
261 self: ::core::pin::Pin<&mut Self>,
262 context: &mut ::core::task::Context<'_>
263 ) -> ::core::task::Poll<Self::Output> {
264 self.project()._inner.poll(context)
265 }
266 }
267 };
268}
269
270/// A future that runs using the [`event-listener`] crate.
271///
272/// This is similar to the [`Future`] trait from libstd, with one notable difference: it takes
273/// a strategy that tells it whether to operate in a blocking or non-blocking context. The
274/// `poll_with_strategy` method is the equivalent of the `poll` method in this regard; it uses
275/// the [`Strategy`] trait to determine how to poll the future.
276///
277/// From here, there are two additional things one can do with this trait:
278///
279/// - The `wait` method, which uses the [`Blocking`] strategy to poll the future until it is
280/// ready, blocking the current thread until it is.
281/// - The [`FutureWrapper`] type, which implements [`Future`] and uses the [`NonBlocking`]
282/// strategy to poll the future.
283pub trait EventListenerFuture {
284 /// The type of value produced on completion.
285 type Output;
286
287 /// Poll the future using the provided strategy.
288 ///
289 /// This function should use the `Strategy::poll` method to poll the future, and proceed
290 /// based on the result.
291 fn poll_with_strategy<'a, S: Strategy<'a>>(
292 self: Pin<&mut Self>,
293 strategy: &mut S,
294 context: &mut S::Context,
295 ) -> Poll<Self::Output>;
296
297 /// Wait for the future to complete, blocking the current thread.
298 ///
299 /// This function uses the [`Blocking`] strategy to poll the future until it is ready.
300 ///
301 /// The future should only return `Pending` if `Strategy::poll` returns error. Otherwise,
302 /// this function polls the future in a hot loop.
303 #[cfg(all(feature = "std", not(target_family = "wasm")))]
304 #[cfg_attr(docsrs, doc(all(feature = "std", not(target_family = "wasm"))))]
305 fn wait(mut self) -> Self::Output
306 where
307 Self: Sized,
308 {
309 // SAFETY: `self`/`this` is not moved out after this.
310 let mut this = unsafe { Pin::new_unchecked(&mut self) };
311
312 loop {
313 if let Poll::Ready(res) = this
314 .as_mut()
315 .poll_with_strategy(&mut Blocking::default(), &mut ())
316 {
317 return res;
318 }
319 }
320 }
321}
322
323pin_project_lite::pin_project! {
324 /// A wrapper around an [`EventListenerFuture`] that implements [`Future`].
325 ///
326 /// [`Future`]: core::future::Future
327 #[derive(Debug, Clone)]
328 pub struct FutureWrapper<F: ?Sized> {
329 #[pin]
330 inner: F,
331 }
332}
333
334impl<F: EventListenerFuture> FutureWrapper<F> {
335 /// Create a new `FutureWrapper` from the provided future.
336 #[inline]
337 pub fn new(inner: F) -> Self {
338 Self { inner }
339 }
340
341 /// Consume the `FutureWrapper`, returning the inner future.
342 #[inline]
343 pub fn into_inner(self) -> F {
344 self.inner
345 }
346}
347
348impl<F: ?Sized> FutureWrapper<F> {
349 /// Get a reference to the inner future.
350 #[inline]
351 pub fn get_ref(&self) -> &F {
352 &self.inner
353 }
354
355 /// Get a mutable reference to the inner future.
356 #[inline]
357 pub fn get_mut(&mut self) -> &mut F {
358 &mut self.inner
359 }
360
361 /// Get a pinned mutable reference to the inner future.
362 #[inline]
363 pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut F> {
364 self.project().inner
365 }
366
367 /// Get a pinned reference to the inner future.
368 #[inline]
369 pub fn get_pin_ref(self: Pin<&Self>) -> Pin<&F> {
370 self.project_ref().inner
371 }
372}
373
374impl<F: EventListenerFuture> From<F> for FutureWrapper<F> {
375 #[inline]
376 fn from(inner: F) -> Self {
377 Self { inner }
378 }
379}
380
381impl<F: EventListenerFuture + ?Sized> Future for FutureWrapper<F> {
382 type Output = F::Output;
383
384 #[inline]
385 fn poll(self: Pin<&mut Self>, context: &mut Context<'_>) -> Poll<Self::Output> {
386 self.project()
387 .inner
388 .poll_with_strategy(&mut NonBlocking::default(), context)
389 }
390}
391
392/// A strategy for polling an [`EventListenerFuture`] or an [`EventListener`].
393///
394/// This trait is used by the [`EventListenerFuture::poll_with_strategy`] method to determine
395/// how to poll the future. It can also be used standalone, by calling the [`Strategy::wait`]
396/// method.
397///
398/// [`EventListenerFuture::poll_with_strategy`]: EventListenerFuture::poll_with_strategy
399/// [`EventListener`]: event_listener::EventListener
400///
401/// # Examples
402///
403/// ```
404/// use event_listener_strategy::{
405/// event_listener::{Event, EventListener},
406/// EventListenerFuture, Strategy, Blocking, NonBlocking
407/// };
408/// use std::pin::Pin;
409///
410/// async fn wait_on<'a, S: Strategy<'a>>(evl: EventListener, strategy: &mut S) {
411/// strategy.wait(evl).await;
412/// }
413///
414/// # futures_lite::future::block_on(async {
415/// // Block on the future.
416/// let ev = Event::new();
417/// let listener = ev.listen();
418/// ev.notify(1);
419///
420/// wait_on(listener, &mut Blocking::default()).await;
421///
422/// // Poll the future.
423/// let listener = ev.listen();
424/// ev.notify(1);
425///
426/// wait_on(listener, &mut NonBlocking::default()).await;
427/// # });
428/// ```
429pub trait Strategy<'a> {
430 /// The context needed to poll the future.
431 type Context: ?Sized;
432
433 /// The future returned by the [`Strategy::wait`] method.
434 type Future: Future + 'a;
435
436 /// Poll the event listener until it is ready.
437 fn poll<T, L: Listener<T> + Unpin>(
438 &mut self,
439 event_listener: &mut Option<L>,
440 context: &mut Self::Context,
441 ) -> Poll<T>;
442
443 /// Wait for the event listener to become ready.
444 fn wait(&mut self, evl: EventListener) -> Self::Future;
445}
446
447/// A strategy that uses polling to efficiently wait for an event.
448#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
449pub struct NonBlocking<'a> {
450 /// The type `&'a mut &'a T` is invariant over `'a`, like `Context` is.
451 ///
452 /// We used to just use `Context` here, but then `Context` became `!Send`
453 /// and `!Sync`, making all of the futures that use this type `!Send` and
454 /// `!Sync` as well. So we just take the lifetime invariance and none of
455 /// the downsides.
456 _marker: PhantomData<&'a mut &'a ()>,
457}
458
459impl<'a> Strategy<'_> for NonBlocking<'a> {
460 type Context = Context<'a>;
461 type Future = EventListener;
462
463 #[inline]
464 fn wait(&mut self, evl: EventListener) -> Self::Future {
465 evl
466 }
467
468 #[inline]
469 fn poll<T, L: Listener<T> + Unpin>(
470 &mut self,
471 event_listener: &mut Option<L>,
472 context: &mut Self::Context,
473 ) -> Poll<T> {
474 let poll = Pin::new(
475 event_listener
476 .as_mut()
477 .expect("`event_listener` should never be `None`"),
478 )
479 .poll(context);
480 if poll.is_ready() {
481 *event_listener = None;
482 }
483 poll
484 }
485}
486
487/// A strategy that blocks the current thread until the event is signalled.
488#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
489#[cfg(all(feature = "std", not(target_family = "wasm")))]
490pub struct Blocking {
491 _private: (),
492}
493
494#[cfg(all(feature = "std", not(target_family = "wasm")))]
495impl Strategy<'_> for Blocking {
496 type Context = ();
497 type Future = Ready;
498
499 #[inline]
500 fn wait(&mut self, evl: EventListener) -> Self::Future {
501 evl.wait();
502 Ready { _private: () }
503 }
504
505 #[inline]
506 fn poll<T, L: Listener<T> + Unpin>(
507 &mut self,
508 event_listener: &mut Option<L>,
509 _context: &mut Self::Context,
510 ) -> Poll<T> {
511 let result = event_listener
512 .take()
513 .expect("`event_listener` should never be `None`")
514 .wait();
515 Poll::Ready(result)
516 }
517}
518
519/// A future that is always ready.
520#[cfg(feature = "std")]
521#[doc(hidden)]
522#[derive(Debug, Clone)]
523pub struct Ready {
524 _private: (),
525}
526
527#[cfg(feature = "std")]
528impl Future for Ready {
529 type Output = ();
530
531 #[inline]
532 fn poll(self: Pin<&mut Self>, _context: &mut Context<'_>) -> Poll<Self::Output> {
533 Poll::Ready(())
534 }
535}
536
537#[test]
538fn send_and_sync() {
539 fn assert_send_and_sync<T: Send + Sync>() {}
540
541 #[cfg(all(feature = "std", not(target_family = "wasm")))]
542 {
543 assert_send_and_sync::<Blocking>();
544 assert_send_and_sync::<Ready>();
545 }
546
547 assert_send_and_sync::<NonBlocking<'static>>();
548 assert_send_and_sync::<FutureWrapper<()>>();
549}