Crate spawns

Crate spawns 

Source
Expand description

spawns proposes thread context task spawner to ease async runtime agnostic coding.

It introduces few concepts for async runtimes to setup thread context task spawner:

With above, spawns provides spawn() and JoinHandle to spawn and join tasks.

Below is a minimum runtime agnostic echo server. All you have to do for it to be function is setting up thread context task spawner.

use async_net::*;
use futures_lite::io;

pub async fn echo_server(port: u16) {
    let listener = TcpListener::bind(("127.0.0.1", port)).await.unwrap();
    println!("Listen on port: {}", listener.local_addr().unwrap().port());
    let mut echos = vec![];
    let mut id_counter = 0;
    loop {
        let (stream, remote_addr) = listener.accept().await.unwrap();
        id_counter += 1;
        let id = id_counter;
        let handle = spawns::spawn(async move {
            eprintln!("{:010}[{}]: serving", id, remote_addr);
            let (reader, writer) = io::split(stream);
            match io::copy(reader, writer).await {
                Ok(_) => eprintln!("{:010}[{}]: closed", id, remote_addr),
                Err(err) => eprintln!("{:010}[{}]: {:?}", id, remote_addr, err),
            }
        })
        .attach();
        echos.push(handle);
    }
}

§Compatibility with existing async runtimes

This is an open world, there might be tens async runtimes. spawns provides features to inject spawners for few.

  • tokio: uses tokio::runtime::Handle::try_current() to detect thread local tokio runtime handle.
  • smol: uses smol::spawn to spawn task in absent of thread local spawners.
  • async-global-executor: uses async_global_executor::spawn to spawn task in absent of thread local spawners.

For other async runtimes, one could inject Compats to COMPATS themselves.

Noted that, all those compatibility features, injections should only active on tests and binaries. Otherwise, they will be propagated to dependents with unnecessary dependencies.

§Dealing with multiple global executors

Global executor cloud spawn task with no help from thread context. But this exposes us an dilemma to us, which one to use if there are multiple global executors present ? By default, spawns randomly chooses one and stick to it to spawn tasks in absent of thread context spawners. Generally, this should be safe as global executors should be designed to spawn everywhere. If this is not the case, one could use environment variable SPAWNS_GLOBAL_SPAWNER to specify one. As a safety net, feature panic-multiple-global-spawners is provided to panic if there are multiple global candidates.

§Panic: “no spawner”

spawns will panic “no spawner” in case that it can’t find a suitable way to spawn task. This could happen if neither Spawn nor suitable Compats found. But it could also happen in cases that Compat symbols get discarded by linker. You may have to tweak compiler flags or profile settings to overcome this, say, you probably will need lto = "fat" in [profile.release] to ship crates as staticlib.

[lib]
crate-type = ["staticlib"]

[profile.release]
lto = "fat"

See also:

Structs§

Blockingexecutor
Executor construct to block future until completion.
Builder
Builder to spawn task with more options.
CancelHandle
Handle to cancel associated task.
Id
Unique among running tasks.
JoinError
Error in polling JoinHandle.
JoinHandle
Handle to join or cancel associated task.
Name
Task name.
SpawnScope
Scope where tasks are spawned through given Spawn.
Task
Thin wrapper around task to accommodate possible new members.
TaskHandle
An owned permission to cancel task on Drop besides join and cancel on demand.

Enums§

Compatcompat
Item of COMPATS to encapsulate functions to spawn task for async runtime.

Statics§

COMPATScompat
DistributedSlice to collect Compats from distributed_slice.

Traits§

Spawn
Trait to spawn task.

Functions§

block_onexecutor
Blocks current thread and runs given future until completion.
enter
Enters a scope where new tasks will be spawned through given Spawn.
id
Gets running task’s id. Panic if no reside in managed async task.
spawn
Spawns a new task.
try_id
Gets running task’s id. None if no reside in managed async task.