Crate twizzler_async

source ·
Expand description

Support for asynchronous programming on Twizzler. This crate provides executor functionality along with support for async events and waiting, timers and timeouts, and a couple general helper functions.

Executors

We provide three types of executors:

  1. block_on, which blocks until the future is completed.
  2. Thread-local, for futures that aren’t Send.
  3. Global, which puts tasks in a global scheduling context for thread pools to handle.

Examples

The most basic way to run a future is:

let result = block_on(async { /* some async code */ });

But this of course doesn’t really make it possible to actually run things concurrently, since it just waits for this single future. Instead, you probably want to use a real executor. The main one you probably want is the global executor:

let result = Task::spawn(async { /* some async code */ }).await;

Now, this does assume that there is a thread that has called [mod@run()], eg:

let result = run(async { Task::spawn(async { /* some async code */ }).await });

Generally, though, if you want a thread pool, you can spawn a thread into a pool like this:

std::thread::spawn(|| twizzler_async::run(std::future::pending::<()>()));

Then, later on, you can spawn a Task and await it. You can also detach a Task with .detach(), which just places the thread on the runqueues and runs it without you having to await the result.

AsyncSetup, and Async

Traits and types for asynchronous operations on objects that have generic wait and signal events.

For example, a queue might have the following interface presented to the user:

  1. async fn send(T)
  2. async fn recv() -> T

Making these functions async requires defining some Future that can wait and be signaled when something happens – say we send and want to wait if the queue is full, or recv and want to wait if the queue is empty, and of course we don’t want to busy-wait. The queue can implement AsyncDuplexSetup so that we can wrap the queue in a AsyncDuplex and then use its functions to access the queue’s underlying structures in a non-blocking way, automatically sleeping when necessary.

Structs

  • A wrapper type around some “handle” that we want to perform asynchronous operations on, where that handle must implement AsyncSetup.
  • A wrapper type around some “handle” that we want to perform asynchronous operations on, where that handle must implement AsyncDuplexSetup.
  • A basic condition variable for async tasks. If you call wait() you get back a future that you can await on, which will complete once another tasks calls signal_all(). But there’s a gotcha here.
  • A spawned future. Tasks are futures themselves and yield the output of the spawned future.
  • A timer future that returns after a specified period of time.

Traits

  • Implement setting up externally signaled asynchronous events for the async runner to wait for, in the case where there is a duplex mode for reading and writing to this object, each of which could fail with some “would block” error.
  • Implement setting up externally signaled asynchronous events for the async runner to wait for, in the case where there is a single “runnable” abstraction for this object.

Functions

  • Run a future to completion, sleeping the thread if there is no progress that can be made.
  • Runs executors.
  • Await a future until a timeout occurs (or that future completes). If the timeout happens, return None, otherwise return Some of the result of the future. This timeout expires after a duration.
  • Await a future until a timeout occurs (or that future completes). If the timeout happens, return None, otherwise return Some of the result of the future. This timeout expires at an instant in time.
  • A future that waits on two sub-futures until the first one completes. If the second one completes first, this future will continue awaiting on the first future. If the first one completes first, this future returns immediately without continuing to wait on the second future.