From 51a2b0ac91d462efde1bcba432123723f74ed95f Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Wed, 17 Dec 2025 12:39:49 -0800 Subject: [PATCH 1/2] sources/channel: Use a `PingOnDrop` helper I believe previous there is a theoretically possible race, since the `.ping()` call would have happened before the `mpsc::Sender` was dropped. --- src/sources/channel.rs | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/src/sources/channel.rs b/src/sources/channel.rs index 49f439ea..4ceee041 100644 --- a/src/sources/channel.rs +++ b/src/sources/channel.rs @@ -10,6 +10,7 @@ use std::cmp; use std::fmt; +use std::ops; use std::sync::mpsc; use crate::{EventSource, Poll, PostAction, Readiness, Token, TokenFactory}; @@ -30,13 +31,32 @@ pub enum Event { Closed, } +#[derive(Debug)] +struct PingOnDrop(Ping); + +impl ops::Deref for PingOnDrop { + type Target = Ping; + + fn deref(&self) -> &Ping { + &self.0 + } +} + +impl Drop for PingOnDrop { + fn drop(&mut self) { + self.0.ping(); + } +} + /// The sender end of a channel /// /// It can be cloned and sent accross threads (if `T` is). #[derive(Debug)] pub struct Sender { sender: mpsc::Sender, - ping: Ping, + // Dropped after `sender` so receiver is guaranteed to get `Disconnected` + // after ping. + ping: PingOnDrop, } impl Clone for Sender { @@ -44,7 +64,7 @@ impl Clone for Sender { fn clone(&self) -> Sender { Sender { sender: self.sender.clone(), - ping: self.ping.clone(), + ping: PingOnDrop(self.ping.clone()), } } } @@ -59,13 +79,6 @@ impl Sender { } } -impl Drop for Sender { - fn drop(&mut self) { - // ping on drop, to notify about channel closure - self.ping.ping(); - } -} - /// The sender end of a synchronous channel /// /// It can be cloned and sent accross threads (if `T` is). @@ -164,7 +177,7 @@ pub fn channel() -> (Sender, Channel) { ( Sender { sender, - ping: ping.clone(), + ping: PingOnDrop(ping.clone()), }, Channel { receiver, From a885a07209f554ff725588be4912c6615ced0e08 Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Wed, 17 Dec 2025 12:41:51 -0800 Subject: [PATCH 2/2] sources/channel: Ping on drop of last instance of `SyncSender` Presumably this should behave like `Sender`, but only ping once all clones have been dropped. This should achieve that. --- src/sources/channel.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/sources/channel.rs b/src/sources/channel.rs index 4ceee041..fc84fea6 100644 --- a/src/sources/channel.rs +++ b/src/sources/channel.rs @@ -11,7 +11,7 @@ use std::cmp; use std::fmt; use std::ops; -use std::sync::mpsc; +use std::sync::{mpsc, Arc}; use crate::{EventSource, Poll, PostAction, Readiness, Token, TokenFactory}; @@ -85,7 +85,9 @@ impl Sender { #[derive(Debug)] pub struct SyncSender { sender: mpsc::SyncSender, - ping: Ping, + // Dropped after `sender` so receiver is guaranteed to get `Disconnected` + // after ping. + ping: Arc, } impl Clone for SyncSender { @@ -195,7 +197,7 @@ pub fn sync_channel(bound: usize) -> (SyncSender, Channel) { ( SyncSender { sender, - ping: ping.clone(), + ping: Arc::new(PingOnDrop(ping.clone())), }, Channel { receiver,