jD91mZM2
Posted on December 30, 2017
This "article" is going to be specifically about Rust and Tokio, but it should apply to more frameworks
This is more of a question than an article. I've been working on Synac for a while, and I've needed to handle multiple connections on one thread.
For the server, I went with tokio. It allowed me to do something like this:
listener.incoming().for_each(|(conn, addr)| {
ssl.accept_async(conn).map_err(|_| ()).and_then(move |conn| {
println!("Client connected!");
});
});
core.run();
The problem with that is that often you can't just make everything async unless you're programming in a language where blocking calls are barely possible (looking at you, JavaScript). Sure, Tokio is easy once you get the hang of it. But usually you want to combine syncs, asyncs, and all kinds of things. This is where Tokio becomes a problem.
For the synac client, I already had experience with a library that only allowed blocking reads, so multithreading wasn't possible. I didn't want to force built-in mutexes upon users, and I didn't want to prevent multithreading, so I came up with my own solution. It gave me code like this:
if let Ok(Some(packet)) = listener.try_read() {
println!("Packet received: {:?}", packet);
}
(If you wonder how I did this magic sorcery, I simply did something similar to read_exact, but in a struct. Code)
This ended up being a good decision, because I could then do non-blocking reads inside gtk::timeout_add
! And it wasn't too difficult to code it either!
Now I want to ask: When should you use an async framework with cores and stuff, and when should you just write a non-blocking listener? I feel like non-blocking listeners are way more flexible, but there has to be a reason tokio exists... Right?
Posted on December 30, 2017
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.