tokio-rs / tokio

A runtime for writing reliable asynchronous applications with Rust. Provides I/O, networking, scheduling, timers, ...
https://tokio.rs
MIT License
27.12k stars 2.49k forks source link

If tokio::sync::Notify notify() is invoked prior to tokio::sync::Notify notfied().await, then the call to notified().await will result in perpetual blocking without being notified. #6952

Open ybbh opened 2 weeks ago

ybbh commented 2 weeks ago

Version List the versions of all tokio crates you are using.

cargo tree | grep tokio

tokio-notify v0.1.0 
└── tokio v1.41.0
    └── tokio-macros v2.4.0 (proc-macro)

Platform The output of uname -a (UNIX), or version and 32 or 64-bit (Windows)

Linux ybbh-ubuntu24 6.8.0-48-generic #48-Ubuntu SMP PREEMPT_DYNAMIC Fri Sep 27 14:04:52 UTC 2024 x86_64 x86_64 x86_64 GNU/Linux

Description

If notify() is invoked prior to notfied().await, then the call to notified().await will result in perpetual blocking without the possibility of being notified.

I tried this code:

use tokio::sync::Notify;
use std::sync::Arc;
use std::thread;
use std::thread::sleep;
use std::time::Duration;

fn main() {
    let notify = Arc::new(Notify::new());
    let notify2 = notify.clone();
    let thd = thread::spawn(move ||{
        let runtime = tokio::runtime::Builder::new_current_thread()
            .enable_all()
            .build()
            .unwrap();
        runtime.block_on(async move {
            println!("waiting notification");
            notify2.notified().await;
            println!("notified");
        })
    });
    // uncomment this line would be OK, when notify() is called after notfied().await
    // sleep(Duration::from_secs(1));
    notify.notify_waiters();
    println!("sending notification");
    thd.join().unwrap();
}

I expected to see this happen:

The call to notified().await should be notified. Or if it is unsafe to invoke notify() before notified().await, this should be clearly documented to avoid potential issues.

Instead, this happened:

The call to notified().await cannot be notified.

Darksonn commented 2 weeks ago

Your program sleeps forever for the same reason as this program:

use tokio::sync::Notify;

#[tokio::main]
async fn main() {
    let notify = Notify::new();
    notify.notify_waiters();
    notify.notified().await;
    // never reaches this line
}

It has nothing to do with being inside/outside the runtime.