Expand description

An Executor and various primitives for running asynchronous code.

This crate is meant to be used with the futures-rs crate that provides further combinators and utility functions to combine and manage futures.

Running top-level futures.

If there is only one top-level future to run, use the Executor::run_until method.

Implementations

There are currently two paths for asynchronous IO: epoll and io_uring. The io_uring backend may be disabled at compile time. When io_uring support is enabled, the framework performs a runtime check to determine the backend to use.

The backend selection should be transparent to the users of this crate.

Concrete async types

This crate provides asynchronous versions of various IO types, like File and Event. Users should use these high-level types to perform asynchronous IO operations.

Thread pools

The Executor supports driving futures simultaneously from multiple threads. To use a thread pool, simply spawn the desired number of threads and call Executor::run or Executor::run_until on each thread. Futures spawned via Executor::spawn may then run on any of the threads in the thread pool.

Global executor

This crate deliberately doesn’t define a global executor. Instead all methods are implemented directly on the Executor type. Users who want to use a single global executor for their whole program can easily do so:

use cros_async::Executor;
use once_cell::sync::Lazy;
static GLOBAL_EXECUTOR: Lazy<Executor> = Lazy::new(Executor::new);

let val = GLOBAL_EXECUTOR.run_until(async { 11 + 23 }).unwrap();
assert_eq!(val, 34);

Dealing with !Send futures

Almost all the functions of the async types in this crate are !Send. This status is infectious: when one of these futures are awaited from another future, that future also becomes !Send, which can be quite inconvenient. One way to isolate the !Send future is to use Executor::spawn_local with a oneshot::channel:

use std::mem::size_of;

use futures::channel::oneshot::channel;
use cros_async::{Executor, File};

async fn outer_future_implements_send(ex: Executor, f: File) -> u64 {
    let (tx, rx) = channel();
    let mut buf = 0x77u64.to_ne_bytes();
    ex.spawn_local(async move {
        let res = f.read(&mut buf, None).await;
        let _ = tx.send((res, buf));
    }).detach();

    let (res, buf) = rx.await.expect("task canceled after detach()");
    assert_eq!(res.unwrap(), size_of::<u64>());
    u64::from_ne_bytes(buf)
}

let ex = Executor::new();
let f = File::open("/dev/zero").unwrap();

// `spawn` requires that the future implements `Send`.
let task = ex.spawn(outer_future_implements_send(ex.clone(), f));

let val = ex.run_until(task).unwrap();
assert_eq!(val, 0);

A cancelable version of the inner future can be implemented using abortable. However keep in mind that on backends like io_uring, cancelling the future may not cancel the underlying IO operation.

Modules

blocking 🔒
enter 🔒
event 🔒
executor 🔒
file 🔒
iobuf 🔒
sys 🔒
timer 🔒

Structs

A thread pool for running work that may block.
An async version of SafeDescriptor.
An asynchronous version of a base::Event.
An executor for scheduling tasks that poll futures to completion.
A reference to an open file on the filesystem.
An owned buffer for IO.
The error returned from with_deadline when the deadline expires before the future completes.
A timer that expires at a specific time.
A Unix SOCK_SEQPACKET.
Like a UnixListener but for accepting SOCK_SEQPACKET sockets.

Traits

A trait for describing regions of memory to be used for IO.

Functions

Run a future to completion on the current thread.
Add a deadline to an asynchronous operation.