Async loops in async code

I think I still don't quite understand when to spawn a system thread vs spawn a tokio blocking/non-blocking task in a case when I need to run a few services in my async code. Imagine the code like this:

#[tokio::main]
async fn main() {
  let watcher = init_watcher(...);
  let http_server = init_http_server(...);
}

async fn init_watcher(...) { ... }
async fn init_http_server(...) { ... }

Both init_watcher and init_http_server end up internally with a loop waiting asynchronously for something. And this is a part I'm struggling with - as both functions would essentially block the main thread with their internal loops, my first thought was - they should go into a separate threads. But they are also async which, makes them rather a candidates for a tasks spawned by tokio::spawn or tokio::task::spawn_blocking. Yes, I know that we shouldn't block tasks, but is a loop waiting for a message from a stream blocking? From what I understand, if stream does not yield a message, tokio runtime moves on to the other task, so technically no blocking happens. Is that correct?

On the other hand the loop will never finish, so in fact the task remains blocked...

I feel a bit confused here. Any help highly appreciated :slight_smile:

In this context, "blocking" means a synchronously blocking call, like a std::net call or the like. If it's sitting on an .await, the task had been moved off the thread and it can be used by another task.

Tldr: It's perfectly ok to endlessly loop in an async function, so long as it hits an .await!

2 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.