I made a futures executor!
It's a future's executor. You know, like tokio or async-std, it takes futures and, uh, executes them.
fn main() {
let future = async {
println!("In a future!");
42
};
let runtime = guillotine::runtime::Runtime::new().unwrap();
let _forty_two = runtime.block_on(future);
}
Yeah! (I'm as surprised as you are.) Check out the examples folder, where there are working examples of
- UDP sockets
- TCP sockets
- Spawning (async and blocking)
- Sleeping
- Using the
Waker
externally.
The executor is built on top of epoll
and when there is no work to do, it sits in epoll_wait
waiting for work to do until it is woken up.
Each future is associated with an eventfd
file descriptor that, when written to, will wake up the epoll_wait
, giving control back to the executor. The executor then figures out which future the wakeup corresponded to and polls that future.
The Waker
that is provided to Future::poll
is a small wrapper around the eventfd
and writes to the eventfd
whenever .wake()
is called.
Ha, no. Absolutely not. I created this because I wanted to better understand how futures runtimes work. Not because I thought I could make something better than what's already out there.
And in that sense, it was a success! I do feel like I understand better.
But no, this is nowhere near as good as the existing futures executors that have had countless hours poured into making them good. I still use tokio in production.
When I started this, I thought the most interesting bits would be the RawWakerVTable
. That is in src/runtime/waker.rs.
That turned out to be more straightforward than I was expecting – turning a pointer to an Arc into an actual Arc, and dropping or not dropping it as appropriate.
Providing built-in futures with a context that they can hook into turned out to be a bit of a challenge; setting a thread-local variable before polling a future, then clearning it afterward, so that the future can grab that context and use it. That's mostly in src/runtime/context.rs.
And because I wanted to be able to spawn futures, I found keeping track of all of the futures in the runtime was pretty interesting, too. That's in Runtime::block
in src/runtime/mod.rs.