Crate worst_executor
source ·Expand description
The worst async executor you could think of.
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
and is literally 14 lines of code.
This can become more useful using the core::future::join
macro
which allows you to wait on multiple futures at once (each time polling a different future).
Currently this library requires rust nightly for the pin
macro, the join
macro(used in tests) and the const_waker
feature (required for complete optimization of block_on
)
Examples
Block on a simple future
use worst_executor::block_on;
let val = block_on(async { 42 });
assert_eq!(val, 42);
Receive and send messages through a channel
This works in an event-loop style, each time checking if it can send or recieve a message.
#![feature(future_join)]
use core::future::join;
use worst_executor::block_on;
let (sender, receiver) = async_channel::bounded(1);
block_on(async {
let send_20_fut = async {
for i in 0..20 {
sender.send(i).await.unwrap();
}
};
let recv_20_fut = async {
for i in 0..20 {
assert_eq!(receiver.recv().await.unwrap(), i);
}
};
join!(send_20_fut, recv_20_fut).await;
});
A single threaded TCP server
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()
}
Functions
poll
method until it is ready.