Async execution - rogue task

I understand that in async, tasks are meant to call await fairly quickly. I probably am not thinking this through right, but what exactly happens if there is a "rogue task"?

Can other async tasks be prevented from running entirely? What if a task is just a bit slow, say it takes 5 msec before calling await, can this prevent other async tasks running in the mean-time?

[ Quite likely I am just muddled here, maybe the question shows a mis-understanding of how aysnc works... but the question just popped into my head, so I thought I would ask ]

Rogue task can make the whole async runtime unusable for all async tasks.

Async is a cooperative multitasking, and has no defense against non-cooperative tasks.

If you use a single-threaded runtime, then one slow task will block all other tasks from running. In a multi-threaded runtime it depends. Usually it will block only tasks scheduled to run on that thread, but due to locks and dependencies between tasks it can still wreck havoc.

Right. I guess my question is whether that schedule is fixed in advance? Or can the run-time switch those tasks to run on another thread?

It depends on implementation of the runtime. Work-stealing queues could deal with it, but I wouldn't count on it saving you every time.

If you have some specific tasks that you're worried may cause problems, you can launch them on a separate runtime with its own thread pool. I use this pattern in some of my servers — one tokio runtime for low-latency network-only tasks, and another runtime for big complex background processing jobs.

1 Like

I wrote an article about this. The answer is yes.

2 Likes

Ah, by following a link, I found this:

" Currently, the answer to this problem is that the user of Tokio is responsible for adding yield points in both the application and libraries. In practice, very few actually do this and end up being vulnerable to this sort of problem."

So it might be a good idea to add these in a task that isn't urgent, for example? I think a sub-conscious memory of this may have prompted my post question.

Well, when it comes to yield_now, I think mostly you don't need it. I tend to recommend rayon or spawn_blocking for that kind of situation instead.

1 Like

I just did a test, and as far as I can tell, with tokio, a SINGLE rogue task will not stop other async tasks from running, when using rt-multi-thread.

[ Well, if there is only a single CPU, and it simply loops using all the CPU there is, and the operating system doesn't intervene somehow, I guess it could. But otherwise, it will not ]

My test ( inserted into an async function )

                println!("Doing big sum");
                let mut x = 0; for i in 0..1000000000000u64 { x += i % 10; }
                println!("Done big sum, x={}", x );

While it is doing the sum, everything else continues to work fine.

Yeah. Tokio will try to perform work-stealing which can help with the problem. However work stealing is not guaranteed to happen in a timely manner, and if the other threads are busy, they are not going to attempt to do any work stealing (as opposed to pulling from the global queue, which they always do periodically even if busy).

1 Like

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.