Skip to content

Commit 3c3fa07

Browse files
committed
This is actually not safe
1 parent d88ec35 commit 3c3fa07

File tree

1 file changed

+78
-65
lines changed

1 file changed

+78
-65
lines changed

rtic-sync/src/channel.rs

Lines changed: 78 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,23 @@ unsafe impl<T, const N: usize> Send for Channel<T, N> {}
5555

5656
unsafe impl<T, const N: usize> Sync for Channel<T, N> {}
5757

58+
macro_rules! cs_access {
59+
($name:ident, $type:ty) => {
60+
/// Access the value mutably.
61+
///
62+
/// SAFETY: this function must not be called recursively within `f`.
63+
unsafe fn $name<F, R>(&self, _cs: critical_section::CriticalSection, f: F) -> R
64+
where
65+
F: FnOnce(&mut $type) -> R,
66+
{
67+
self.$name.with_mut(|v| {
68+
let v = unsafe { &mut *v };
69+
f(v)
70+
})
71+
}
72+
};
73+
}
74+
5875
impl<T, const N: usize> Channel<T, N> {
5976
const _CHECK: () = assert!(N < 256, "This queue support a maximum of 255 entries");
6077

@@ -113,45 +130,10 @@ impl<T, const N: usize> Channel<T, N> {
113130
(Sender(self), Receiver(self))
114131
}
115132

116-
fn freeq<F, R>(&self, _cs: critical_section::CriticalSection, f: F) -> R
117-
where
118-
F: FnOnce(&mut Deque<u8, N>) -> R,
119-
{
120-
self.freeq.with_mut(|freeq| {
121-
let queue = unsafe { &mut *freeq };
122-
f(queue)
123-
})
124-
}
125-
126-
fn readyq<F, R>(&self, _cs: critical_section::CriticalSection, f: F) -> R
127-
where
128-
F: FnOnce(&mut Deque<u8, N>) -> R,
129-
{
130-
self.readyq.with_mut(|readyq| {
131-
let queue = unsafe { &mut *readyq };
132-
f(queue)
133-
})
134-
}
135-
136-
fn receiver_dropped<F, R>(&self, _cs: critical_section::CriticalSection, f: F) -> R
137-
where
138-
F: FnOnce(&mut bool) -> R,
139-
{
140-
self.receiver_dropped.with_mut(|receiver_dropped| {
141-
let receiver_dropped = unsafe { &mut *receiver_dropped };
142-
f(receiver_dropped)
143-
})
144-
}
145-
146-
fn num_senders<F, R>(&self, _cs: critical_section::CriticalSection, f: F) -> R
147-
where
148-
F: FnOnce(&mut usize) -> R,
149-
{
150-
self.num_senders.with_mut(|num_senders| {
151-
let num_senders = unsafe { &mut *num_senders };
152-
f(num_senders)
153-
})
154-
}
133+
cs_access!(freeq, Deque<u8, N>);
134+
cs_access!(readyq, Deque<u8, N>);
135+
cs_access!(receiver_dropped, bool);
136+
cs_access!(num_senders, usize);
155137
}
156138

157139
/// Creates a split channel with `'static` lifetime.R
@@ -276,7 +258,8 @@ impl<T, const N: usize> Sender<'_, T, N> {
276258

277259
// Write the value into the ready queue.
278260
critical_section::with(|cs| {
279-
assert!(!self.0.readyq(cs, |q| q.is_full()));
261+
// SAFETY: the closure does not call `readyq`
262+
assert!(unsafe { !self.0.readyq(cs, |q| q.is_full()) });
280263
unsafe { self.0.readyq(cs, |q| q.push_back_unchecked(idx)) }
281264
});
282265

@@ -298,12 +281,14 @@ impl<T, const N: usize> Sender<'_, T, N> {
298281
return Err(TrySendError::NoReceiver(val));
299282
}
300283

301-
let idx =
302-
if let Some(idx) = critical_section::with(|cs| self.0.freeq(cs, |q| q.pop_front())) {
303-
idx
304-
} else {
305-
return Err(TrySendError::Full(val));
306-
};
284+
// SAFETY: the closure does not call `freeq`
285+
let idx = if let Some(idx) =
286+
critical_section::with(|cs| unsafe { self.0.freeq(cs, |q| q.pop_front()) })
287+
{
288+
idx
289+
} else {
290+
return Err(TrySendError::Full(val));
291+
};
307292

308293
self.send_footer(idx, val);
309294

@@ -337,7 +322,8 @@ impl<T, const N: usize> Sender<'_, T, N> {
337322
// Do all this in one critical section, else there can be race conditions
338323
let queue_idx = critical_section::with(|cs| {
339324
let wq_empty = self.0.wait_queue.is_empty();
340-
let fq_empty = self.0.freeq(cs, |q| q.is_empty());
325+
// SAFETY: the closure does not call `freeq`
326+
let fq_empty = unsafe { self.0.freeq(cs, |q| q.is_empty()) };
341327

342328
if !wq_empty || fq_empty {
343329
// SAFETY: This pointer is only dereferenced here and on drop of the future
@@ -365,7 +351,8 @@ impl<T, const N: usize> Sender<'_, T, N> {
365351
}
366352
}
367353

368-
assert!(!self.0.freeq(cs, |q| q.is_empty()));
354+
// SAFETY: the closure does not call `freeq`
355+
assert!(unsafe { !self.0.freeq(cs, |q| q.is_empty()) });
369356
// Get index as the queue is guaranteed not empty and the wait queue is empty
370357
let idx = unsafe { self.0.freeq(cs, |q| q.pop_front_unchecked()) };
371358

@@ -395,28 +382,34 @@ impl<T, const N: usize> Sender<'_, T, N> {
395382

396383
/// Returns true if there is no `Receiver`s.
397384
pub fn is_closed(&self) -> bool {
398-
critical_section::with(|cs| self.0.receiver_dropped(cs, |v| *v))
385+
// SAFETY: the closure does not call `receiver_dropped`
386+
critical_section::with(|cs| unsafe { self.0.receiver_dropped(cs, |v| *v) })
399387
}
400388

401389
/// Is the queue full.
402390
pub fn is_full(&self) -> bool {
403-
critical_section::with(|cs| self.0.freeq(cs, |q| q.is_empty()))
391+
// SAFETY: the closure does not call `freeq`
392+
critical_section::with(|cs| unsafe { self.0.freeq(cs, |q| q.is_empty()) })
404393
}
405394

406395
/// Is the queue empty.
407396
pub fn is_empty(&self) -> bool {
408-
critical_section::with(|cs| self.0.freeq(cs, |q| q.is_full()))
397+
// SAFETY: the closure does not call `freeq`
398+
critical_section::with(|cs| unsafe { self.0.freeq(cs, |q| q.is_full()) })
409399
}
410400
}
411401

412402
impl<T, const N: usize> Drop for Sender<'_, T, N> {
413403
fn drop(&mut self) {
414404
// Count down the reference counter
415405
let num_senders = critical_section::with(|cs| {
416-
self.0.num_senders(cs, |v| {
417-
*v -= 1;
418-
*v
419-
})
406+
// SAFETY: the closure does not call `num_senders`
407+
unsafe {
408+
self.0.num_senders(cs, |v| {
409+
*v -= 1;
410+
*v
411+
})
412+
}
420413
});
421414

422415
// If there are no senders, wake the receiver to do error handling.
@@ -429,7 +422,8 @@ impl<T, const N: usize> Drop for Sender<'_, T, N> {
429422
impl<T, const N: usize> Clone for Sender<'_, T, N> {
430423
fn clone(&self) -> Self {
431424
// Count up the reference counter
432-
critical_section::with(|cs| self.0.num_senders(cs, |v| *v += 1));
425+
// SAFETY: the closure does not call `num_senders`
426+
critical_section::with(|cs| unsafe { self.0.num_senders(cs, |v| *v += 1) });
433427

434428
Self(self.0)
435429
}
@@ -469,7 +463,10 @@ impl<T, const N: usize> Receiver<'_, T, N> {
469463
/// Receives a value if there is one in the channel, non-blocking.
470464
pub fn try_recv(&mut self) -> Result<T, ReceiveError> {
471465
// Try to get a ready slot.
472-
let ready_slot = critical_section::with(|cs| self.0.readyq(cs, |q| q.pop_front()));
466+
let ready_slot = critical_section::with(|cs| {
467+
// SAFETY: the closure does not call `readyq`.
468+
unsafe { self.0.readyq(cs, |q| q.pop_front()) }
469+
});
473470

474471
if let Some(rs) = ready_slot {
475472
// Read the value from the slots, note; this memcpy is not under a critical section.
@@ -480,10 +477,14 @@ impl<T, const N: usize> Receiver<'_, T, N> {
480477

481478
// Return the index to the free queue after we've read the value.
482479
critical_section::with(|cs| {
483-
self.0.freeq(cs, |freeq| {
484-
assert!(!freeq.is_full());
485-
unsafe { freeq.push_back_unchecked(rs) }
486-
});
480+
// SAFETY: the closure does not call `freeq`
481+
unsafe {
482+
self.0.freeq(cs, |freeq| {
483+
assert!(!freeq.is_full());
484+
// SAFETY: `freeq` is not full.
485+
freeq.push_back_unchecked(rs)
486+
});
487+
}
487488

488489
fence(Ordering::SeqCst);
489490

@@ -528,24 +529,36 @@ impl<T, const N: usize> Receiver<'_, T, N> {
528529

529530
/// Returns true if there are no `Sender`s.
530531
pub fn is_closed(&self) -> bool {
531-
critical_section::with(|cs| self.0.num_senders(cs, |v| *v == 0))
532+
critical_section::with(|cs| {
533+
// SAFETY: the closure does not call `num_senders`
534+
unsafe { self.0.num_senders(cs, |v| *v == 0) }
535+
})
532536
}
533537

534538
/// Is the queue full.
535539
pub fn is_full(&self) -> bool {
536-
critical_section::with(|cs| self.0.readyq(cs, |q| q.is_full()))
540+
critical_section::with(|cs| {
541+
// SAFETY: the closure does not call `readyq`
542+
unsafe { self.0.readyq(cs, |q| q.is_full()) }
543+
})
537544
}
538545

539546
/// Is the queue empty.
540547
pub fn is_empty(&self) -> bool {
541-
critical_section::with(|cs| self.0.readyq(cs, |q| q.is_empty()))
548+
critical_section::with(|cs| {
549+
// SAFETY: the closure does not call `readyq`
550+
unsafe { self.0.readyq(cs, |q| q.is_empty()) }
551+
})
542552
}
543553
}
544554

545555
impl<T, const N: usize> Drop for Receiver<'_, T, N> {
546556
fn drop(&mut self) {
547557
// Mark the receiver as dropped and wake all waiters
548-
critical_section::with(|cs| self.0.receiver_dropped(cs, |v| *v = true));
558+
critical_section::with(|cs| {
559+
// SAFETY: the closure does not call `receiver_dropped`
560+
unsafe { self.0.receiver_dropped(cs, |v| *v = true) }
561+
});
549562

550563
while let Some(waker) = self.0.wait_queue.pop() {
551564
waker.wake();

0 commit comments

Comments
 (0)