A fast and fair select! implementation for asynchronous programming.
See the [select!] or [inline!] macros for documentation.
Add the following to your Cargo.toml
:
toml
selectme = "0.7.1"
The following is a simple example showcasing two branches being polled concurrently. For more documentation see [select!].
```rust async fn dostuffasync() { // work here }
async fn moreasyncwork() { // work here }
selectme::select! { _ = dostuffasync() => { println!("dostuffasync() completed first") } _ = moreasyncwork() => { println!("moreasyncwork() completed first") } }; ```
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.
inline!
macroThe [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.
```rust use std::time::Duration; use tokio::time;
async fn async_operation() -> u32 { // work here }
let output = selectme::inline! { output = asyncoperation() => Some(output), () = time::sleep(Duration::fromsecs(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.
```rust use std::future::Future; use std::pin::Pin; use std::task::{Context, Poll}; use std::time::Duration;
use pinproject::pinproject; use selectme::{Random, StaticSelect}; use tokio::time::{self, Sleep};
struct MyFuture {
#[pin]
select: StaticSelect
impl Future for MyFuture {
type Output = Option
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::frommillis(100)); let s2 = time::sleep(Duration::frommillis(200));
let my_future = MyFuture { select: selectme::inline! { static;
() = s1 => Some(1),
_ = s2 => Some(2),
else => None,
}
};
asserteq!(myfuture.await, Some(1)); ```