Skip to content

Commit bbff85d

Browse files
committed
fix: port fix discard all message on receiver droped
1 parent 407375c commit bbff85d

File tree

3 files changed

+99
-45
lines changed

3 files changed

+99
-45
lines changed

crossbeam-channel/src/channel.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -662,7 +662,7 @@ impl<T> Drop for Sender<T> {
662662
fn drop(&mut self) {
663663
unsafe {
664664
match &self.flavor {
665-
SenderFlavor::Array(chan) => chan.release(|c| c.disconnect()),
665+
SenderFlavor::Array(chan) => chan.release(|c| c.disconnect_senders()),
666666
SenderFlavor::List(chan) => chan.release(|c| c.disconnect_senders()),
667667
SenderFlavor::Zero(chan) => chan.release(|c| c.disconnect()),
668668
}
@@ -1159,7 +1159,7 @@ impl<T> Drop for Receiver<T> {
11591159
fn drop(&mut self) {
11601160
unsafe {
11611161
match &self.flavor {
1162-
ReceiverFlavor::Array(chan) => chan.release(|c| c.disconnect()),
1162+
ReceiverFlavor::Array(chan) => chan.release(|c| c.disconnect_receivers()),
11631163
ReceiverFlavor::List(chan) => chan.release(|c| c.disconnect_receivers()),
11641164
ReceiverFlavor::Zero(chan) => chan.release(|c| c.disconnect()),
11651165
ReceiverFlavor::At(_) => {}

crossbeam-channel/src/flavors/array.rs

Lines changed: 85 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
1111
use std::boxed::Box;
1212
use std::cell::UnsafeCell;
13-
use std::mem::{self, MaybeUninit};
13+
use std::mem::MaybeUninit;
1414
use std::ptr;
1515
use std::sync::atomic::{self, AtomicUsize, Ordering};
1616
use std::time::Instant;
@@ -476,21 +476,102 @@ impl<T> Channel<T> {
476476
Some(self.cap())
477477
}
478478

479-
/// Disconnects the channel and wakes up all blocked senders and receivers.
479+
/// Disconnects senders and wakes up all blocked senders and receivers.
480480
///
481481
/// Returns `true` if this call disconnected the channel.
482-
pub(crate) fn disconnect(&self) -> bool {
482+
pub(crate) fn disconnect_senders(&self) -> bool {
483483
let tail = self.tail.fetch_or(self.mark_bit, Ordering::SeqCst);
484484

485485
if tail & self.mark_bit == 0 {
486-
self.senders.disconnect();
487486
self.receivers.disconnect();
488487
true
489488
} else {
490489
false
491490
}
492491
}
493492

493+
/// Disconnects receivers and wakes up all blocked senders.
494+
///
495+
/// Returns `true` if this call disconnected the channel.
496+
///
497+
/// # Safety
498+
/// May only be called once upon dropping the last receiver. The
499+
/// destruction of all other receivers must have been observed with acquire
500+
/// ordering or stronger.
501+
pub(crate) unsafe fn disconnect_receivers(&self) -> bool {
502+
let tail = self.tail.fetch_or(self.mark_bit, Ordering::SeqCst);
503+
let disconnected = if tail & self.mark_bit == 0 {
504+
self.senders.disconnect();
505+
true
506+
} else {
507+
false
508+
};
509+
510+
unsafe {
511+
self.discard_all_messages(tail);
512+
}
513+
514+
disconnected
515+
}
516+
517+
/// Discards all messages.
518+
///
519+
/// `tail` should be the current (and therefore last) value of `tail`.
520+
///
521+
/// # Panicking
522+
/// If a destructor panics, the remaining messages are leaked, matching the
523+
/// behaviour of the unbounded channel.
524+
///
525+
/// # Safety
526+
/// This method must only be called when dropping the last receiver. The
527+
/// destruction of all other receivers must have been observed with acquire
528+
/// ordering or stronger.
529+
unsafe fn discard_all_messages(&self, tail: usize) {
530+
debug_assert!(self.is_disconnected());
531+
532+
// Only receivers modify `head`, so since we are the last one,
533+
// this value will not change and will not be observed (since
534+
// no new messages can be sent after disconnection).
535+
let mut head = self.head.load(Ordering::Relaxed);
536+
let tail = tail & !self.mark_bit;
537+
538+
let backoff = Backoff::new();
539+
loop {
540+
// Deconstruct the head.
541+
let index = head & (self.mark_bit - 1);
542+
let lap = head & !(self.one_lap - 1);
543+
544+
// Inspect the corresponding slot.
545+
debug_assert!(index < self.buffer.len());
546+
let slot = unsafe { self.buffer.get_unchecked(index) };
547+
let stamp = slot.stamp.load(Ordering::Acquire);
548+
549+
// If the stamp is ahead of the head by 1, we may drop the message.
550+
if head + 1 == stamp {
551+
head = if index + 1 < self.cap() {
552+
// Same lap, incremented index.
553+
// Set to `{ lap: lap, mark: 0, index: index + 1 }`.
554+
head + 1
555+
} else {
556+
// One lap forward, index wraps around to zero.
557+
// Set to `{ lap: lap.wrapping_add(1), mark: 0, index: 0 }`.
558+
lap.wrapping_add(self.one_lap)
559+
};
560+
561+
unsafe {
562+
(*slot.msg.get()).assume_init_drop();
563+
}
564+
// If the tail equals the head, that means the channel is empty.
565+
} else if tail == head {
566+
return;
567+
// Otherwise, a sender is about to write into the slot, so we need
568+
// to wait for it to update the stamp.
569+
} else {
570+
backoff.spin();
571+
}
572+
}
573+
}
574+
494575
/// Returns `true` if the channel is disconnected.
495576
pub(crate) fn is_disconnected(&self) -> bool {
496577
self.tail.load(Ordering::SeqCst) & self.mark_bit != 0
@@ -521,45 +602,6 @@ impl<T> Channel<T> {
521602
}
522603
}
523604

524-
impl<T> Drop for Channel<T> {
525-
fn drop(&mut self) {
526-
if mem::needs_drop::<T>() {
527-
// Get the index of the head.
528-
let head = *self.head.get_mut();
529-
let tail = *self.tail.get_mut();
530-
531-
let hix = head & (self.mark_bit - 1);
532-
let tix = tail & (self.mark_bit - 1);
533-
534-
let len = if hix < tix {
535-
tix - hix
536-
} else if hix > tix {
537-
self.cap() - hix + tix
538-
} else if (tail & !self.mark_bit) == head {
539-
0
540-
} else {
541-
self.cap()
542-
};
543-
544-
// Loop over all slots that hold a message and drop them.
545-
for i in 0..len {
546-
// Compute the index of the next slot holding a message.
547-
let index = if hix + i < self.cap() {
548-
hix + i
549-
} else {
550-
hix + i - self.cap()
551-
};
552-
553-
unsafe {
554-
debug_assert!(index < self.buffer.len());
555-
let slot = self.buffer.get_unchecked_mut(index);
556-
(*slot.msg.get()).assume_init_drop();
557-
}
558-
}
559-
}
560-
}
561-
}
562-
563605
/// Receiver handle to a channel.
564606
pub(crate) struct Receiver<'a, T>(&'a Channel<T>);
565607

crossbeam-channel/tests/array.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -742,3 +742,15 @@ fn panic_on_drop() {
742742
// Elements after the panicked element will leak.
743743
assert!(!b);
744744
}
745+
746+
#[test]
747+
fn drop_unreceived() {
748+
let (tx, rx) = bounded::<std::rc::Rc<()>>(1);
749+
let msg = std::rc::Rc::new(());
750+
let weak = std::rc::Rc::downgrade(&msg);
751+
assert!(tx.send(msg).is_ok());
752+
drop(rx);
753+
// Messages should be dropped immediately when the last receiver is destroyed.
754+
assert!(weak.upgrade().is_none());
755+
drop(tx);
756+
}

0 commit comments

Comments
 (0)