Skip to content

Commit 34aa872

Browse files
committed
std: leak remaining messages in bounded channel if message destructor panics
1 parent 4e9e465 commit 34aa872

File tree

1 file changed

+42
-66
lines changed

1 file changed

+42
-66
lines changed

library/std/src/sync/mpmc/array.rs

+42-66
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use super::utils::{Backoff, CachePadded};
1515
use super::waker::SyncWaker;
1616

1717
use crate::cell::UnsafeCell;
18-
use crate::mem::{self, MaybeUninit};
18+
use crate::mem::MaybeUninit;
1919
use crate::ptr;
2020
use crate::sync::atomic::{self, AtomicUsize, Ordering};
2121
use crate::time::Instant;
@@ -479,82 +479,58 @@ impl<T> Channel<T> {
479479
///
480480
/// `tail` should be the current (and therefore last) value of `tail`.
481481
///
482+
/// # Panicking
483+
/// If a destructor panics, the remaining messages are leaked, matching the
484+
/// behaviour of the unbounded channel.
485+
///
482486
/// # Safety
483487
/// This method must only be called when dropping the last receiver. The
484488
/// destruction of all other receivers must have been observed with acquire
485489
/// ordering or stronger.
486490
unsafe fn discard_all_messages(&self, tail: usize) {
487491
debug_assert!(self.is_disconnected());
488492

489-
/// Use a helper struct with a custom `Drop` to ensure all messages are
490-
/// dropped, even if a destructor panicks.
491-
struct DiscardState<'a, T> {
492-
channel: &'a Channel<T>,
493-
head: usize,
494-
tail: usize,
495-
backoff: Backoff,
496-
}
493+
// Only receivers modify `head`, so since we are the last one,
494+
// this value will not change and will not be observed (since
495+
// no new messages can be sent after disconnection).
496+
let mut head = self.head.load(Ordering::Relaxed);
497+
let tail = tail & !self.mark_bit;
497498

498-
impl<'a, T> DiscardState<'a, T> {
499-
fn discard(&mut self) {
500-
loop {
501-
// Deconstruct the head.
502-
let index = self.head & (self.channel.mark_bit - 1);
503-
let lap = self.head & !(self.channel.one_lap - 1);
504-
505-
// Inspect the corresponding slot.
506-
debug_assert!(index < self.channel.buffer.len());
507-
let slot = unsafe { self.channel.buffer.get_unchecked(index) };
508-
let stamp = slot.stamp.load(Ordering::Acquire);
509-
510-
// If the stamp is ahead of the head by 1, we may drop the message.
511-
if self.head + 1 == stamp {
512-
self.head = if index + 1 < self.channel.cap {
513-
// Same lap, incremented index.
514-
// Set to `{ lap: lap, mark: 0, index: index + 1 }`.
515-
self.head + 1
516-
} else {
517-
// One lap forward, index wraps around to zero.
518-
// Set to `{ lap: lap.wrapping_add(1), mark: 0, index: 0 }`.
519-
lap.wrapping_add(self.channel.one_lap)
520-
};
521-
522-
// We updated the head, so even if this descrutor panics,
523-
// we will not attempt to destroy the slot again.
524-
unsafe {
525-
(*slot.msg.get()).assume_init_drop();
526-
}
527-
// If the tail equals the head, that means the channel is empty.
528-
} else if self.tail == self.head {
529-
return;
530-
// Otherwise, a sender is about to write into the slot, so we need
531-
// to wait for it to update the stamp.
532-
} else {
533-
self.backoff.spin_heavy();
534-
}
535-
}
536-
}
537-
}
499+
let backoff = Backoff::new();
500+
loop {
501+
// Deconstruct the head.
502+
let index = head & (self.mark_bit - 1);
503+
let lap = head & !(self.one_lap - 1);
538504

539-
impl<'a, T> Drop for DiscardState<'a, T> {
540-
fn drop(&mut self) {
541-
self.discard();
505+
// Inspect the corresponding slot.
506+
debug_assert!(index < self.buffer.len());
507+
let slot = unsafe { self.buffer.get_unchecked(index) };
508+
let stamp = slot.stamp.load(Ordering::Acquire);
509+
510+
// If the stamp is ahead of the head by 1, we may drop the message.
511+
if head + 1 == stamp {
512+
head = if index + 1 < self.cap {
513+
// Same lap, incremented index.
514+
// Set to `{ lap: lap, mark: 0, index: index + 1 }`.
515+
head + 1
516+
} else {
517+
// One lap forward, index wraps around to zero.
518+
// Set to `{ lap: lap.wrapping_add(1), mark: 0, index: 0 }`.
519+
lap.wrapping_add(self.one_lap)
520+
};
521+
522+
unsafe {
523+
(*slot.msg.get()).assume_init_drop();
524+
}
525+
// If the tail equals the head, that means the channel is empty.
526+
} else if tail == head {
527+
return;
528+
// Otherwise, a sender is about to write into the slot, so we need
529+
// to wait for it to update the stamp.
530+
} else {
531+
backoff.spin_heavy();
542532
}
543533
}
544-
545-
let mut state = DiscardState {
546-
channel: self,
547-
// Only receivers modify `head`, so since we are the last one,
548-
// this value will not change and will not be observed (since
549-
// no new messages can be sent after disconnection).
550-
head: self.head.load(Ordering::Relaxed),
551-
tail: tail & !self.mark_bit,
552-
backoff: Backoff::new(),
553-
};
554-
state.discard();
555-
// This point is only reached if no destructor panics, so all messages
556-
// have already been dropped.
557-
mem::forget(state);
558534
}
559535

560536
/// Returns `true` if the channel is disconnected.

0 commit comments

Comments
 (0)