Expand description
A fast and fair select! implementation for asynchronous programming.
See the select! or inline! macros for documentation.
Usage
Add the following to your Cargo.toml
:
selectme = "0.7.1"
Examples
The following is a simple example showcasing two branches being polled concurrently. For more documentation see select!.
async fn do_stuff_async() {
// work here
}
async fn more_async_work() {
// work here
}
selectme::select! {
_ = do_stuff_async() => {
println!("do_stuff_async() completed first")
}
_ = more_async_work() => {
println!("more_async_work() completed first")
}
};
Entrypoint macros
This crate provides entrypoint attributes which are compatible with the ones
provided by Tokio through #[selectme::main]
and
#[selectme::test]
with one exception. They do not check
(because they cannot) which Tokio features are enabled and simply assumes
that you want to build a multithreaded runtime unless flavor
is specified.
So why does this project provide entrypoint macros? Well, there’s a handful
of issues related to performance and ergonomics which
turns out to be quite hard to fix in Tokio proper since backwards
compatibility needs to be maintained. So until a Tokio 2.x
is released and
we can bake another breaking release. Until such a time, you can find those
macros here.
The inline!
macro
The inline! macro provides an inlined variant of the select! macro.
Instead of awaiting directly it evaluates to an instance of the Select or StaticSelect allowing for more efficient multiplexing and complex control flow.
When combined with the static;
option it performs the least amount of
magic possible to multiplex multiple asynchronous operations making it
suitable for efficient and custom abstractions.
use std::time::Duration;
use tokio::time;
async fn async_operation() -> u32 {
// work here
}
let output = selectme::inline! {
output = async_operation() => Some(output),
() = time::sleep(Duration::from_secs(5)) => None,
}.await;
match output {
Some(output) => {
assert_eq!(output, 42);
}
None => {
panic!("operation timed out!")
}
}
The more interesting trick is producing a StaticSelect through the
static;
option which can be properly named and used inside of another
future.
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
use std::time::Duration;
use pin_project::pin_project;
use selectme::{Random, StaticSelect};
use tokio::time::{self, Sleep};
#[pin_project]
struct MyFuture {
#[pin]
select: StaticSelect<u8, (Sleep, Sleep), Random, Option<u32>>,
}
impl Future for MyFuture {
type Output = Option<u32>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.project();
this.select.poll_next(cx)
}
}
let s1 = time::sleep(Duration::from_millis(100));
let s2 = time::sleep(Duration::from_millis(200));
let my_future = MyFuture {
select: selectme::inline! {
static;
() = s1 => Some(1),
_ = s2 => Some(2),
else => None,
}
};
assert_eq!(my_future.await, Some(1));
Macros
- The
inline!
macro provides an inlined variant of the select! macro. - Waits on multiple concurrent branches, returning when the first branch completes, cancelling the remaining branches.
Structs
- A biased selector which applies the given random pattern to selection.
- This is the type produced by the inline! macro unless the
static;
option is enabled. - An unbiased selector which starts from the top and works its way to the bottom.
Attribute Macros
- Marks async function to be executed by runtime, suitable to test environment