Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Owned version of Notified<'_> #7231

Open
Razz4780 opened this issue Mar 26, 2025 · 1 comment
Open

Owned version of Notified<'_> #7231

Razz4780 opened this issue Mar 26, 2025 · 1 comment
Labels
A-tokio Area: The main tokio crate C-feature-request Category: A feature request. M-sync Module: tokio/sync

Comments

@Razz4780
Copy link

Razz4780 commented Mar 26, 2025

Several times I've found myself frustrated with Notify semantics. Notify::notify_waiters allows for notifying only those Notified instances that were acquired before the call. This makes it hard to use this interface when working with background tasks.

Example:

async fn example() {
    let notify = Arc::new(Notify::new());

    for _ in 0..10 {
        let notify = notify.clone();
        tokio::spawn(async move {
            let notified = notify.notified();

            tokio::select! {
                _ = some_logic() => {},
                _ = notified => {}
            }

            some_cleanup().await;
        });
    }

    some_other_logic().await;

    notify.notify_waiters();
}

In the snippet above, spawned background tasks may not pick up the notification from the Notify::notify_waiters call. It seems to me that Notify was designed specifically for this kind of problems. However, the interface does not allow for a safe solution, as Notified cannot be sent between tasks.

I can work around this problem by using other primitives, like a Semaphore or a Shared<oneshot::Receiver> that never receives any value (instead, waits for the sender to be dropped), but I think Notify would be much simpler and easier to understand.

Would be nice to have an owned version of Notified, e.g like this:

impl Notify {
    pub fn notified_owned(self: Arc<Self>) -> NotifiedOwned { ... }
}

If this sounds ok, I'm willing to implement it

@Razz4780 Razz4780 added A-tokio Area: The main tokio crate C-feature-request Category: A feature request. labels Mar 26, 2025
@Darksonn Darksonn added the M-sync Module: tokio/sync label Mar 28, 2025
@Daniel-Aaron-Bloom
Copy link

As an alternative, this is presently doable (but a bit painful) with ouroboros or similar.

#[ouroboros::self_referencing]
struct NotifyOwnedData {
    data: Arc<Notify>,

    #[borrows(data)]
    #[not_covariant]
    borrowed: Pin<Box<Notified<'this>>>,
}

pub struct NotifyOwned(NotifyOwnedData);

impl NotifyOwned {
    fn notified(data: Arc<Notify>) -> Self {
        Self(NotifyOwnedDataBuilder {
            data,
            borrowed_builder: move |data: &Arc<Notify>| {
                Box::pin(data.notified())
            },
        }
        .build())
    }
}

impl Future for NotifyOwned {
    type Output = ();

    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
        self.0.with_borrowed_mut(|b| {
            b.as_mut().poll(cx)
        })
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-tokio Area: The main tokio crate C-feature-request Category: A feature request. M-sync Module: tokio/sync
Projects
None yet
Development

No branches or pull requests

3 participants