Skip to content

A fast and fair select! macro for async Rust.

Notifications You must be signed in to change notification settings

udoprog/selectme

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

88 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

selectme

github crates.io docs.rs build status

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.3"

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));

About

A fast and fair select! macro for async Rust.

Resources

Stars

Watchers

Forks

Packages

No packages published

Languages