Crates.io | worst-executor |
lib.rs | worst-executor |
version | 0.1.1 |
source | src |
created_at | 2023-01-26 12:01:14.067812 |
updated_at | 2023-01-26 13:33:30.419542 |
description | A spin-loop like executor for async |
homepage | |
repository | https://github.com/elichai/worst-executor |
max_upload_size | |
id | 768402 |
size | 40,274 |
The simplest async executor possible.
This crate provides a single function, block_on
, which takes a future and
blocks the current thread until the future is resolved.
The way it works is by "spin-looping" over the poll
method until it is ready.
The nice thing about this is that it optimizes very well,
for example worst_executor::block_on(async { 42 })
compiles to a single mov
instruction.
The bad thing about this is that it does not actually do any scheduling, meaning that if you wait on a future that never resolves, your program will hang. which is why you should probably not use this.
Note that because of its simplicity, the library only uses core
and does not require std
or alloc
nor does it have any dependencies, and is literally 16 lines of code.
Add this to your Cargo.toml
:
[dependencies]
worst-executor = "0.1"
Then in your main.rs:
fn main() {
worst_executor::block_on(async {
// Your async code goes here.
});
}
Say you're using a library which returns a future, but you know it does no I/O,
and you want to use it in a synchronous context without bringing in a full async runtime like tokio/smol/async-std
You can use worst_executor::block_on
to block the current thread until the future is resolved.
Another scenario can be that you want to run an "event loop" in a single thread using async
rust.
so you can use the core::future::join
macro together with the futures::select
macro
to handle the control flow of your program, while always running on a single thread and never yielding.
use async_net::{TcpListener, TcpStream};
use futures::{stream::FuturesUnordered, AsyncReadExt, AsyncWriteExt, StreamExt};
use worst_executor::block_on;
block_on(async {
let listener = TcpListener::bind("127.0.0.1:8080").await.unwrap();
let mut connection_handlers = FuturesUnordered::new();
// This stream is infinite so it's OK to call fuse.
let mut listener = listener.incoming().fuse();
loop {
futures::select! {
new_connection = listener.select_next_some() => connection_handlers.push(handle_connection(new_connection?)),
socket = connection_handlers.select_next_some() =>
if let Some(socket) = socket {
connection_handlers.push(handle_connection(socket));
},
}
}
})
async fn handle_connection(mut stream: TcpStream) -> Option<TcpStream> {
let mut buf = [0u8; 1024];
let n = match stream.read(&mut buf).await {
Ok(n) if n == 0 => return Some(stream),
Ok(n) => n,
Err(e) => {
eprintln!("failed to read from the socket {e:?}");
return None;
}
};
// Write the data back
stream.write_all(&buf[0..n]).await
.map_err(|e| eprintln!("failed to write to the socket {e:?}"))
.map(|()| stream)
.ok()
}