You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I stumbled upon a surprising behavior that caused a program to never end, because dropping
the runtime never ended.
The behavior can be better explained with this reproducer:
fnmain(){let rt = tokio::runtime::Runtime::new().unwrap();// With an mpsc, there is no blocking on runtime drop// With a watch, the runtime drop can block infinitely.// let (sender, mut receiver) = tokio::sync::mpsc::channel::<()>(1);let(sender,mut receiver) = tokio::sync::watch::channel(false);// This task will block the runtime during drop
rt.spawn(asyncmove{loop{// let _r = receiver.recv().await;let _r = receiver.changed().await;}});drop(sender);println!("drop runtime");drop(rt);println!("runtime has been dropped");}
Running this, "drop runtime" is printed but "runtime has been dropped" is never printed and the program never ends. This is because the task spin loops on the call to receiver.changed().await which returns an error.
It would be expected that this .await would yield and thus allow the runtime to drop the task, but
that does not appear to be the case. This is afaict surprising behavior from my understanding on how shutting down the runtime is documented, especially given that for other types of queues (for example mpsc), the recv() call does yield even if the sender has been dropped for example.
Of course the code isn't ideal here and the bug can clearly be avoided, but I thought it would be interesting to report. Either this is a bug and the changed().await should yield, or maybe the documentation on this function should make it clear that in the case where the sender has been dropped, this call does not yield?
The text was updated successfully, but these errors were encountered:
It's entirely normal that async operations don't yield if they can complete immediately. However, we do have a mechanism to avoid this kind of bug called cooperative scheduling, which we should apply to the watch channel.
Darksonn
changed the title
watch::Receiver::changed does not yield when sender has been droppedwatch::Receiver::changed does use cooperative scheduling
Sep 12, 2024
Version
tokio 1.40.0
Platform
Linux hostname 6.10.8-arch1-1 #1 SMP PREEMPT_DYNAMIC Wed, 04 Sep 2024 15:16:37 +0000 x86_64 GNU/Linux
Description
I stumbled upon a surprising behavior that caused a program to never end, because dropping
the runtime never ended.
The behavior can be better explained with this reproducer:
Running this, "drop runtime" is printed but "runtime has been dropped" is never printed and the program never ends. This is because the task spin loops on the call to
receiver.changed().await
which returns an error.It would be expected that this
.await
would yield and thus allow the runtime to drop the task, butthat does not appear to be the case. This is afaict surprising behavior from my understanding on how shutting down the runtime is documented, especially given that for other types of queues (for example mpsc), the recv() call does yield even if the sender has been dropped for example.
Of course the code isn't ideal here and the bug can clearly be avoided, but I thought it would be interesting to report. Either this is a bug and the
changed().await
should yield, or maybe the documentation on this function should make it clear that in the case where the sender has been dropped, this call does not yield?The text was updated successfully, but these errors were encountered: