selectme/lib.rs
1//! [<img alt="github" src="https://img.shields.io/badge/github-udoprog/selectme-8da0cb?style=for-the-badge&logo=github" height="20">](https://github.com/udoprog/selectme)
2//! [<img alt="crates.io" src="https://img.shields.io/crates/v/selectme.svg?style=for-the-badge&color=fc8d62&logo=rust" height="20">](https://crates.io/crates/selectme)
3//! [<img alt="docs.rs" src="https://img.shields.io/badge/docs.rs-selectme-66c2a5?style=for-the-badge&logoColor=white&logo=data:image/svg+xml;base64,PHN2ZyByb2xlPSJpbWciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDUxMiA1MTIiPjxwYXRoIGZpbGw9IiNmNWY1ZjUiIGQ9Ik00ODguNiAyNTAuMkwzOTIgMjE0VjEwNS41YzAtMTUtOS4zLTI4LjQtMjMuNC0zMy43bC0xMDAtMzcuNWMtOC4xLTMuMS0xNy4xLTMuMS0yNS4zIDBsLTEwMCAzNy41Yy0xNC4xIDUuMy0yMy40IDE4LjctMjMuNCAzMy43VjIxNGwtOTYuNiAzNi4yQzkuMyAyNTUuNSAwIDI2OC45IDAgMjgzLjlWMzk0YzAgMTMuNiA3LjcgMjYuMSAxOS45IDMyLjJsMTAwIDUwYzEwLjEgNS4xIDIyLjEgNS4xIDMyLjIgMGwxMDMuOS01MiAxMDMuOSA1MmMxMC4xIDUuMSAyMi4xIDUuMSAzMi4yIDBsMTAwLTUwYzEyLjItNi4xIDE5LjktMTguNiAxOS45LTMyLjJWMjgzLjljMC0xNS05LjMtMjguNC0yMy40LTMzLjd6TTM1OCAyMTQuOGwtODUgMzEuOXYtNjguMmw4NS0zN3Y3My4zek0xNTQgMTA0LjFsMTAyLTM4LjIgMTAyIDM4LjJ2LjZsLTEwMiA0MS40LTEwMi00MS40di0uNnptODQgMjkxLjFsLTg1IDQyLjV2LTc5LjFsODUtMzguOHY3NS40em0wLTExMmwtMTAyIDQxLjQtMTAyLTQxLjR2LS42bDEwMi0zOC4yIDEwMiAzOC4ydi42em0yNDAgMTEybC04NSA0Mi41di03OS4xbDg1LTM4Ljh2NzUuNHptMC0xMTJsLTEwMiA0MS40LTEwMi00MS40di0uNmwxMDItMzguMiAxMDIgMzguMnYuNnoiPjwvcGF0aD48L3N2Zz4K" height="20">](https://docs.rs/selectme)
4//!
5//! A fast and fair select! implementation for asynchronous programming.
6//!
7//! See the [select!] or [inline!] macros for documentation.
8//!
9//! <br>
10//!
11//! ## Usage
12//!
13//! Add the following to your `Cargo.toml`:
14//!
15//! ```toml
16//! selectme = "0.7.1"
17//! ```
18//!
19//! <br>
20//!
21//! ## Examples
22//!
23//! The following is a simple example showcasing two branches being polled
24//! concurrently. For more documentation see [select!].
25//!
26//! ```
27//! async fn do_stuff_async() {
28//! // work here
29//! }
30//!
31//! async fn more_async_work() {
32//! // work here
33//! }
34//!
35//! # #[selectme::main] async fn main() {
36//! selectme::select! {
37//! _ = do_stuff_async() => {
38//! println!("do_stuff_async() completed first")
39//! }
40//! _ = more_async_work() => {
41//! println!("more_async_work() completed first")
42//! }
43//! };
44//! # }
45//! ```
46//!
47//! <br>
48//!
49//! ## Entrypoint macros
50//!
51//! This crate provides entrypoint attributes which are compatible with the ones
52//! provided by Tokio through [`#[selectme::main]`][selectme-main] and
53//! [`#[selectme::test]`][selectme-test] with one exception. They do not check
54//! (because they cannot) which Tokio features are enabled and simply assumes
55//! that you want to build a multithreaded runtime unless `flavor` is specified.
56//!
57//! So why does this project provide entrypoint macros? Well, there's [a handful
58//! of issues related to performance and ergonomics][tokio-entrypoints-pr] which
59//! turns out to be quite hard to fix in Tokio proper since backwards
60//! compatibility needs to be maintained. So until a Tokio `2.x` is released and
61//! we can bake another breaking release. Until such a time, you can find those
62//! macros here.
63//!
64//! <br>
65//!
66//! ## The `inline!` macro
67//!
68//! The [inline!] macro provides an *inlined* variant of the [select!] macro.
69//!
70//! Instead of awaiting directly it evaluates to an instance of the [Select] or
71//! [StaticSelect] allowing for more efficient multiplexing and complex control
72//! flow.
73//!
74//! When combined with the `static;` option it performs the least amount of
75//! magic possible to multiplex multiple asynchronous operations making it
76//! suitable for efficient and custom abstractions.
77//!
78//! ```
79//! use std::time::Duration;
80//! use tokio::time;
81//!
82//! async fn async_operation() -> u32 {
83//! // work here
84//! # 42
85//! }
86//!
87//! # #[selectme::main]
88//! # pub(crate) async fn main() {
89//! let output = selectme::inline! {
90//! output = async_operation() => Some(output),
91//! () = time::sleep(Duration::from_secs(5)) => None,
92//! }.await;
93//!
94//! match output {
95//! Some(output) => {
96//! assert_eq!(output, 42);
97//! }
98//! None => {
99//! panic!("operation timed out!")
100//! }
101//! }
102//! # }
103//! ```
104//!
105//! The more interesting trick is producing a [StaticSelect] through the
106//! `static;` option which can be properly named and used inside of another
107//! future.
108//!
109//! ```
110//! use std::future::Future;
111//! use std::pin::Pin;
112//! use std::task::{Context, Poll};
113//! use std::time::Duration;
114//!
115//! use pin_project::pin_project;
116//! use selectme::{Random, StaticSelect};
117//! use tokio::time::{self, Sleep};
118//!
119//! #[pin_project]
120//! struct MyFuture {
121//! #[pin]
122//! select: StaticSelect<u8, (Sleep, Sleep), Random, Option<u32>>,
123//! }
124//!
125//! impl Future for MyFuture {
126//! type Output = Option<u32>;
127//!
128//! fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
129//! let this = self.project();
130//! this.select.poll_next(cx)
131//! }
132//! }
133//!
134//! # #[selectme::main] pub(crate) async fn main() {
135//! let s1 = time::sleep(Duration::from_millis(100));
136//! let s2 = time::sleep(Duration::from_millis(200));
137//!
138//! let my_future = MyFuture {
139//! select: selectme::inline! {
140//! static;
141//!
142//! () = s1 => Some(1),
143//! _ = s2 => Some(2),
144//! else => None,
145//! }
146//! };
147//!
148//! assert_eq!(my_future.await, Some(1));
149//! # }
150//! ```
151//!
152//! [inline!]: https://docs.rs/selectme/latest/selectme/macro.inline.html
153//! [select!]: https://docs.rs/selectme/latest/selectme/macro.select.html
154//! [Select]: https://docs.rs/selectme/latest/selectme/struct.Select.html
155//! [selectme-main]: https://docs.rs/selectme/latest/selectme/attr.main.html
156//! [selectme-test]: https://docs.rs/selectme/latest/selectme/attr.test.html
157//! [StaticSelect]: https://docs.rs/selectme/latest/selectme/struct.StaticSelect.html
158//! [tokio-entrypoints-pr]: https://github.com/tokio-rs/tokio/pull/4513
159
160// This project contains code and documentation licensed under the MIT license
161// from the futures-rs project.
162//
163// See: https://github.com/rust-lang/futures-rs/blob/c3d3e08/LICENSE-MIT
164
165// This project contains code and documentation licensed under the MIT license
166// from the Tokio project.
167//
168// See: https://github.com/tokio-rs/tokio/blob/986b88b/LICENSE
169
170#![deny(missing_debug_implementations)]
171#![deny(missing_docs)]
172#![deny(rust_2018_idioms)]
173#![deny(rustdoc::broken_intra_doc_links)]
174#![deny(unreachable_pub)]
175#![cfg_attr(not(feature = "std"), no_std)]
176
177mod bias;
178pub use self::bias::{Random, Unbiased};
179
180#[cfg(feature = "random")]
181mod rand;
182
183mod select;
184pub use crate::select::Select;
185
186mod static_select;
187pub use crate::static_select::StaticSelect;
188
189mod set;
190
191#[macro_use]
192mod macros;
193
194#[doc(inline)]
195pub use ::selectme_macros::{main, test};
196
197/// Hidden support module used by macros.
198#[doc(hidden)]
199pub mod __support {
200 pub use crate::bias::{Bias, Random, Unbiased};
201 pub use crate::select::DISABLED;
202 pub use core::future::Future;
203 pub use core::pin::Pin;
204 pub use core::task::Poll;
205 pub use selectme_macros::{inline, select};
206
207 use core::task::Context;
208
209 use crate::select::Select;
210 use crate::set::{Number, Set};
211 use crate::static_select::StaticSelect;
212
213 /// Construct a random bias.
214 #[inline]
215 #[cfg(feature = "random")]
216 pub fn random() -> Random {
217 Random::new(crate::rand::thread_rng_n(64))
218 }
219
220 /// Construct an unbiased bias.
221 #[inline]
222 pub const fn unbiased() -> Unbiased {
223 Unbiased
224 }
225
226 /// Setup a [Select] with a dynamic function used to poll.
227 #[inline]
228 pub fn select<Bits, S, B, T, O>(mask: Bits, bias: B, state: S, poll: T) -> Select<Bits, S, B, T>
229 where
230 Bits: Number,
231 B: Bias<Bits>,
232 T: FnMut(&mut Context<'_>, Pin<&mut S>, &mut Set<Bits>, u32) -> Poll<O>,
233 {
234 Select::new(Set::new(mask), bias, state, poll)
235 }
236
237 /// Setup a [Select] with a static function used to poll.
238 #[inline]
239 pub fn static_select<Bits, S, B, O>(
240 mask: Bits,
241 bias: B,
242 state: S,
243 poll: fn(&mut Context<'_>, Pin<&mut S>, &mut Set<Bits>, u32) -> Poll<O>,
244 ) -> StaticSelect<Bits, S, B, O>
245 where
246 Bits: Number,
247 B: Bias<Bits>,
248 {
249 StaticSelect::new(Set::new(mask), bias, state, poll)
250 }
251}