mirror of
https://github.com/tokio-rs/tokio.git
synced 2026-09-08 00:00:13 +02:00
sync: fix cloning value when receiving from broadcast channel
The broadcast channel does not require values to implement `Sync` yet it calls the `.clone()` method without synchronizing. This is unsound logic. This patch adds per-value synchronization on receive to handle this case. It is unlikely any usage of the broadcast channel is currently at risk of the unsoundeness issue as it requires accessing a `!Sync` type during `.clone()`, which would be very unusual when using the broadcast channel.
This commit is contained in:
+27
-28
@@ -118,7 +118,7 @@
|
|||||||
|
|
||||||
use crate::loom::cell::UnsafeCell;
|
use crate::loom::cell::UnsafeCell;
|
||||||
use crate::loom::sync::atomic::{AtomicBool, AtomicUsize};
|
use crate::loom::sync::atomic::{AtomicBool, AtomicUsize};
|
||||||
use crate::loom::sync::{Arc, Mutex, MutexGuard, RwLock, RwLockReadGuard};
|
use crate::loom::sync::{Arc, Mutex, MutexGuard};
|
||||||
use crate::util::linked_list::{self, GuardedLinkedList, LinkedList};
|
use crate::util::linked_list::{self, GuardedLinkedList, LinkedList};
|
||||||
use crate::util::WakeList;
|
use crate::util::WakeList;
|
||||||
|
|
||||||
@@ -303,7 +303,7 @@ use self::error::{RecvError, SendError, TryRecvError};
|
|||||||
/// Data shared between senders and receivers.
|
/// Data shared between senders and receivers.
|
||||||
struct Shared<T> {
|
struct Shared<T> {
|
||||||
/// slots in the channel.
|
/// slots in the channel.
|
||||||
buffer: Box<[RwLock<Slot<T>>]>,
|
buffer: Box<[Mutex<Slot<T>>]>,
|
||||||
|
|
||||||
/// Mask a position -> index.
|
/// Mask a position -> index.
|
||||||
mask: usize,
|
mask: usize,
|
||||||
@@ -347,7 +347,7 @@ struct Slot<T> {
|
|||||||
///
|
///
|
||||||
/// The value is set by `send` when the write lock is held. When a reader
|
/// The value is set by `send` when the write lock is held. When a reader
|
||||||
/// drops, `rem` is decremented. When it hits zero, the value is dropped.
|
/// drops, `rem` is decremented. When it hits zero, the value is dropped.
|
||||||
val: UnsafeCell<Option<T>>,
|
val: Option<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An entry in the wait queue.
|
/// An entry in the wait queue.
|
||||||
@@ -385,7 +385,7 @@ generate_addr_of_methods! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct RecvGuard<'a, T> {
|
struct RecvGuard<'a, T> {
|
||||||
slot: RwLockReadGuard<'a, Slot<T>>,
|
slot: MutexGuard<'a, Slot<T>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Receive a value future.
|
/// Receive a value future.
|
||||||
@@ -394,11 +394,15 @@ struct Recv<'a, T> {
|
|||||||
receiver: &'a mut Receiver<T>,
|
receiver: &'a mut Receiver<T>,
|
||||||
|
|
||||||
/// Entry in the waiter `LinkedList`.
|
/// Entry in the waiter `LinkedList`.
|
||||||
waiter: UnsafeCell<Waiter>,
|
waiter: WaiterCell,
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl<'a, T: Send> Send for Recv<'a, T> {}
|
// The wrapper around `UnsafeCell` isolates the unsafe impl `Send` and `Sync`
|
||||||
unsafe impl<'a, T: Send> Sync for Recv<'a, T> {}
|
// from `Recv`.
|
||||||
|
struct WaiterCell(UnsafeCell<Waiter>);
|
||||||
|
|
||||||
|
unsafe impl Send for WaiterCell {}
|
||||||
|
unsafe impl Sync for WaiterCell {}
|
||||||
|
|
||||||
/// Max number of receivers. Reserve space to lock.
|
/// Max number of receivers. Reserve space to lock.
|
||||||
const MAX_RECEIVERS: usize = usize::MAX >> 2;
|
const MAX_RECEIVERS: usize = usize::MAX >> 2;
|
||||||
@@ -466,12 +470,6 @@ pub fn channel<T: Clone>(capacity: usize) -> (Sender<T>, Receiver<T>) {
|
|||||||
(tx, rx)
|
(tx, rx)
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl<T: Send> Send for Sender<T> {}
|
|
||||||
unsafe impl<T: Send> Sync for Sender<T> {}
|
|
||||||
|
|
||||||
unsafe impl<T: Send> Send for Receiver<T> {}
|
|
||||||
unsafe impl<T: Send> Sync for Receiver<T> {}
|
|
||||||
|
|
||||||
impl<T> Sender<T> {
|
impl<T> Sender<T> {
|
||||||
/// Creates the sending-half of the [`broadcast`] channel.
|
/// Creates the sending-half of the [`broadcast`] channel.
|
||||||
///
|
///
|
||||||
@@ -510,10 +508,10 @@ impl<T> Sender<T> {
|
|||||||
let mut buffer = Vec::with_capacity(capacity);
|
let mut buffer = Vec::with_capacity(capacity);
|
||||||
|
|
||||||
for i in 0..capacity {
|
for i in 0..capacity {
|
||||||
buffer.push(RwLock::new(Slot {
|
buffer.push(Mutex::new(Slot {
|
||||||
rem: AtomicUsize::new(0),
|
rem: AtomicUsize::new(0),
|
||||||
pos: (i as u64).wrapping_sub(capacity as u64),
|
pos: (i as u64).wrapping_sub(capacity as u64),
|
||||||
val: UnsafeCell::new(None),
|
val: None,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -599,7 +597,7 @@ impl<T> Sender<T> {
|
|||||||
tail.pos = tail.pos.wrapping_add(1);
|
tail.pos = tail.pos.wrapping_add(1);
|
||||||
|
|
||||||
// Get the slot
|
// Get the slot
|
||||||
let mut slot = self.shared.buffer[idx].write().unwrap();
|
let mut slot = self.shared.buffer[idx].lock();
|
||||||
|
|
||||||
// Track the position
|
// Track the position
|
||||||
slot.pos = pos;
|
slot.pos = pos;
|
||||||
@@ -608,7 +606,7 @@ impl<T> Sender<T> {
|
|||||||
slot.rem.with_mut(|v| *v = rem);
|
slot.rem.with_mut(|v| *v = rem);
|
||||||
|
|
||||||
// Write the value
|
// Write the value
|
||||||
slot.val = UnsafeCell::new(Some(value));
|
slot.val = Some(value);
|
||||||
|
|
||||||
// Release the slot lock before notifying the receivers.
|
// Release the slot lock before notifying the receivers.
|
||||||
drop(slot);
|
drop(slot);
|
||||||
@@ -695,7 +693,7 @@ impl<T> Sender<T> {
|
|||||||
while low < high {
|
while low < high {
|
||||||
let mid = low + (high - low) / 2;
|
let mid = low + (high - low) / 2;
|
||||||
let idx = base_idx.wrapping_add(mid) & self.shared.mask;
|
let idx = base_idx.wrapping_add(mid) & self.shared.mask;
|
||||||
if self.shared.buffer[idx].read().unwrap().rem.load(SeqCst) == 0 {
|
if self.shared.buffer[idx].lock().rem.load(SeqCst) == 0 {
|
||||||
low = mid + 1;
|
low = mid + 1;
|
||||||
} else {
|
} else {
|
||||||
high = mid;
|
high = mid;
|
||||||
@@ -737,7 +735,7 @@ impl<T> Sender<T> {
|
|||||||
let tail = self.shared.tail.lock();
|
let tail = self.shared.tail.lock();
|
||||||
|
|
||||||
let idx = (tail.pos.wrapping_sub(1) & self.shared.mask as u64) as usize;
|
let idx = (tail.pos.wrapping_sub(1) & self.shared.mask as u64) as usize;
|
||||||
self.shared.buffer[idx].read().unwrap().rem.load(SeqCst) == 0
|
self.shared.buffer[idx].lock().rem.load(SeqCst) == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the number of active receivers.
|
/// Returns the number of active receivers.
|
||||||
@@ -1057,7 +1055,7 @@ impl<T> Receiver<T> {
|
|||||||
let idx = (self.next & self.shared.mask as u64) as usize;
|
let idx = (self.next & self.shared.mask as u64) as usize;
|
||||||
|
|
||||||
// The slot holding the next value to read
|
// The slot holding the next value to read
|
||||||
let mut slot = self.shared.buffer[idx].read().unwrap();
|
let mut slot = self.shared.buffer[idx].lock();
|
||||||
|
|
||||||
if slot.pos != self.next {
|
if slot.pos != self.next {
|
||||||
// Release the `slot` lock before attempting to acquire the `tail`
|
// Release the `slot` lock before attempting to acquire the `tail`
|
||||||
@@ -1074,7 +1072,7 @@ impl<T> Receiver<T> {
|
|||||||
let mut tail = self.shared.tail.lock();
|
let mut tail = self.shared.tail.lock();
|
||||||
|
|
||||||
// Acquire slot lock again
|
// Acquire slot lock again
|
||||||
slot = self.shared.buffer[idx].read().unwrap();
|
slot = self.shared.buffer[idx].lock();
|
||||||
|
|
||||||
// Make sure the position did not change. This could happen in the
|
// Make sure the position did not change. This could happen in the
|
||||||
// unlikely event that the buffer is wrapped between dropping the
|
// unlikely event that the buffer is wrapped between dropping the
|
||||||
@@ -1367,12 +1365,12 @@ impl<'a, T> Recv<'a, T> {
|
|||||||
fn new(receiver: &'a mut Receiver<T>) -> Recv<'a, T> {
|
fn new(receiver: &'a mut Receiver<T>) -> Recv<'a, T> {
|
||||||
Recv {
|
Recv {
|
||||||
receiver,
|
receiver,
|
||||||
waiter: UnsafeCell::new(Waiter {
|
waiter: WaiterCell(UnsafeCell::new(Waiter {
|
||||||
queued: AtomicBool::new(false),
|
queued: AtomicBool::new(false),
|
||||||
waker: None,
|
waker: None,
|
||||||
pointers: linked_list::Pointers::new(),
|
pointers: linked_list::Pointers::new(),
|
||||||
_p: PhantomPinned,
|
_p: PhantomPinned,
|
||||||
}),
|
})),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1384,7 +1382,7 @@ impl<'a, T> Recv<'a, T> {
|
|||||||
is_unpin::<&mut Receiver<T>>();
|
is_unpin::<&mut Receiver<T>>();
|
||||||
|
|
||||||
let me = self.get_unchecked_mut();
|
let me = self.get_unchecked_mut();
|
||||||
(me.receiver, &me.waiter)
|
(me.receiver, &me.waiter.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1418,6 +1416,7 @@ impl<'a, T> Drop for Recv<'a, T> {
|
|||||||
// `Shared::notify_rx` before we drop the object.
|
// `Shared::notify_rx` before we drop the object.
|
||||||
let queued = self
|
let queued = self
|
||||||
.waiter
|
.waiter
|
||||||
|
.0
|
||||||
.with(|ptr| unsafe { (*ptr).queued.load(Acquire) });
|
.with(|ptr| unsafe { (*ptr).queued.load(Acquire) });
|
||||||
|
|
||||||
// If the waiter is queued, we need to unlink it from the waiters list.
|
// If the waiter is queued, we need to unlink it from the waiters list.
|
||||||
@@ -1432,6 +1431,7 @@ impl<'a, T> Drop for Recv<'a, T> {
|
|||||||
// `Relaxed` order suffices because we hold the tail lock.
|
// `Relaxed` order suffices because we hold the tail lock.
|
||||||
let queued = self
|
let queued = self
|
||||||
.waiter
|
.waiter
|
||||||
|
.0
|
||||||
.with_mut(|ptr| unsafe { (*ptr).queued.load(Relaxed) });
|
.with_mut(|ptr| unsafe { (*ptr).queued.load(Relaxed) });
|
||||||
|
|
||||||
if queued {
|
if queued {
|
||||||
@@ -1440,7 +1440,7 @@ impl<'a, T> Drop for Recv<'a, T> {
|
|||||||
// safety: tail lock is held and the wait node is verified to be in
|
// safety: tail lock is held and the wait node is verified to be in
|
||||||
// the list.
|
// the list.
|
||||||
unsafe {
|
unsafe {
|
||||||
self.waiter.with_mut(|ptr| {
|
self.waiter.0.with_mut(|ptr| {
|
||||||
tail.waiters.remove((&mut *ptr).into());
|
tail.waiters.remove((&mut *ptr).into());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -1486,7 +1486,7 @@ impl<'a, T> RecvGuard<'a, T> {
|
|||||||
where
|
where
|
||||||
T: Clone,
|
T: Clone,
|
||||||
{
|
{
|
||||||
self.slot.val.with(|ptr| unsafe { (*ptr).clone() })
|
self.slot.val.clone()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1494,8 +1494,7 @@ impl<'a, T> Drop for RecvGuard<'a, T> {
|
|||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
// Decrement the remaining counter
|
// Decrement the remaining counter
|
||||||
if 1 == self.slot.rem.fetch_sub(1, SeqCst) {
|
if 1 == self.slot.rem.fetch_sub(1, SeqCst) {
|
||||||
// Safety: Last receiver, drop the value
|
self.slot.val = None;
|
||||||
self.slot.val.with_mut(|ptr| unsafe { *ptr = None });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user