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
: usestokio::runtime::Handle::try_current()
to detect thread localtokio
runtime handle.smol
: usessmol::spawn
to spawn task in absent of thread local spawners.async-global-executor
: usesasync_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§
- Blocking
executor
- Executor construct to block future until completion.
- Builder
- Builder to spawn task with more options.
- Cancel
Handle - Handle to cancel associated task.
- Id
- Unique among running tasks.
- Join
Error - Error in polling JoinHandle.
- Join
Handle - Handle to join or cancel associated task.
- Name
- Task name.
- Spawn
Scope - Scope where tasks are spawned through given Spawn.
- Task
- Thin wrapper around task to accommodate possible new members.
- Task
Handle - An owned permission to cancel task on Drop besides join and cancel on demand.
Enums§
Statics§
- COMPATS
compat
- DistributedSlice to collect Compats from distributed_slice.
Traits§
- Spawn
- Trait to spawn task.
Functions§
- block_
on executor
- 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.