Mental model for event loop using Tokio


I'm trying to write a simple program with async/tokio and might need to have my mental model of everything adjusted a bit.

I'm used to writing event loops by hand (i.e. using epoll).

In my use case I have 2 top level FDs, a signal FD an a socket server.
So I've written in my main (pseudo-code):

loop {
        tokio::select! {
            _ = signal_stream.recv() => {
                println!("got sig");
            _ = /* socket server something*/ => {
                // accept client


Now, I'm blocked on what should I do once I accept the client.
If I was writing an epoll based event loop, I'd register the client on the epoll and return to the loop.
In this case, I'm not sure what to do with the client.
AFAICT I can't add it to the select!. What happens if I .await on its recv method inside the select! block?

Hope this question is not too vague.


Usually you would use tokio::spawn to create a new task for every connection you get. To kill all the tasks when a signal is sent (I assume it is for ctrl+c signals?), you can use a broadcast channel to send the shutdown signal as described here.

1 Like

Actually SIGCHLD in my case, but that'd be helpful in the future.

Do I need to do anything to make sure these spawned tasks, ummm, keep going?
Or is it the case that once spawned they'd just run whenever IO is available?

I guess that as long as I implement the proposed shutdown channel I can just drop the JoinHandle and forget about them, right?

No, you don't have to do anything (besides not blocking the thread), and you can indeed just drop the JoinHandle.

1 Like