I am new to Rust, I want to know if it is possible to write a single-threaded concurrent application if non-blocking APIs are not available.
I have written a server-side code for a VPN using a UDP socket and tun device (virtual network interface) to work in Linux.
This code is intended to wait for something to be written in tun fd or packets to arrive on UDP socket.
loop {
tokio::select! {
_ = async {
log::debug!("reading from interface");
tun.read(&mut buf1).unwrap();
} => {
log::debug!("sending to client {}", &caddr);
socket.send_to(&buf1, &caddr).unwrap();
},
_ = async {
log::debug!("reading from client");
let (_amt, addr) = socket.recv_from(&mut buf2).unwrap();
caddr = addr.to_string();
} => {
log::debug!("writting to interface");
tun.write(&mut buf2).unwrap();
}
};
I thought tokio::select!
will fulfill my requirement as its official documentation states about macro -
Waits on multiple concurrent branches, returning when the first branch completes, canceling the remaining branches
Here is the output I got after running it
$ sudo RUST_LOG=debug target/debug/dprox server -l 8080
[2022-09-23T10:58:31Z INFO dprox] Starting as server
[2022-09-23T10:58:31Z DEBUG dprox] reading from client
To my surprise, the debug log reading from interface
did not get a chance to run and I guess it's due to blocking socket.recv_from()
. I was unaware that tokio::select!
will not switch if I will block it.
Is there a way to achieve intended behavior in a single thread if non-blocking APIs do not exist?
Edit:
I found libraries tokio::net::UdpSocket
and tokio_tun
which provide non-blocking APIs that seem to work for my usecase.
But I am still curious to know how to achieve single-threaded concurrency if non-blocking APIs do not exist?