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.
For macOS users, you may have to put below to your Cargo.toml
for above to function.
[profile.dev]
lto = "thin"
See https://github.com/dtolnay/linkme/issues/61 for sure.
§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.
Structs§
- Blocking
- 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§
- Compat
- Compat encapsulate functions to find async runtimes to spawn task.
Statics§
- COMPATS
- DistributedSlice to collect Compats.
Traits§
- Spawn
- Trait to spawn task.
Functions§
- block_
on - 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.