Skip to content

Commit 486e24d

Browse files
committed
rtic-sync: add some newtypes & better comments
1 parent 8046c58 commit 486e24d

File tree

3 files changed

+41
-28
lines changed

3 files changed

+41
-28
lines changed

rtic-sync/src/channel/channel.rs

Lines changed: 33 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -18,23 +18,29 @@ use super::{Receiver, Sender};
1818
pub(crate) type WaitQueueData = (Waker, FreeSlotPtr);
1919
pub(crate) type WaitQueue = DoublyLinkedList<WaitQueueData>;
2020

21+
#[derive(Debug)]
22+
pub(crate) struct FreeSlot(u8);
23+
2124
/// A pointer to a free slot.
2225
#[derive(Clone)]
23-
pub(crate) struct FreeSlotPtr(*mut Option<u8>);
26+
pub(crate) struct FreeSlotPtr(*mut Option<FreeSlot>);
2427

2528
impl FreeSlotPtr {
2629
/// SAFETY: `inner` must be valid until the [`Link`] containing this [`FreeSlotPtr`] is popped.
2730
/// Additionally, this [`FreeSlotPtr`] must have exclusive access to the data pointed to by
2831
/// `inner`.
29-
pub unsafe fn new(inner: *mut Option<u8>) -> Self {
32+
pub unsafe fn new(inner: *mut Option<FreeSlot>) -> Self {
3033
Self(inner)
3134
}
3235

3336
/// Replace the value of this slot with `new_value`, and return
3437
/// the old value.
3538
///
3639
/// SAFETY: the pointer in this [`FreeSlotPtr`] must be valid for writes.
37-
pub(crate) unsafe fn take(&mut self, cs: critical_section::CriticalSection) -> Option<u8> {
40+
pub(crate) unsafe fn take(
41+
&mut self,
42+
cs: critical_section::CriticalSection,
43+
) -> Option<FreeSlot> {
3844
self.replace(None, cs)
3945
}
4046

@@ -45,9 +51,9 @@ impl FreeSlotPtr {
4551
/// be obtained from `freeq`.
4652
unsafe fn replace(
4753
&mut self,
48-
new_value: Option<u8>,
54+
new_value: Option<FreeSlot>,
4955
_cs: critical_section::CriticalSection,
50-
) -> Option<u8> {
56+
) -> Option<FreeSlot> {
5157
// SAFETY: we are in a critical section.
5258
unsafe { core::ptr::replace(self.0, new_value) }
5359
}
@@ -147,31 +153,32 @@ impl<T, const N: usize> Channel<T, N> {
147153
///
148154
/// This will do one of two things:
149155
/// 1. If there are any waiting `send`-ers, wake the longest-waiting one and hand it `slot`.
150-
/// 2. else, insert `slot` into `self.freeq`.
156+
/// 2. else, insert `slot` into the free queue.
151157
///
152-
/// SAFETY: `slot` must be a `u8` that is obtained from [`Channel::readyq`] or through [`FreeSlotPtr::take`].
153-
pub(crate) unsafe fn push_free_slot(&self, slot: u8) {
158+
/// SAFETY: `slot` must be obtained from this exact channel instance.
159+
pub(crate) unsafe fn return_free_slot(&self, slot: FreeSlot) {
154160
critical_section::with(|cs| {
155161
fence(Ordering::SeqCst);
156162

157163
// If a sender is waiting in the `wait_queue`, wake the first one up & hand it the free slot.
158164
if let Some((wait_head, mut freeq_slot)) = self.wait_queue.pop() {
159165
// SAFETY: `freeq_slot` is valid for writes: we are in a critical
160-
// section & the `SlotPtr` lives for at least the duration of the wait queue link.
166+
// section & the `FreeSlotPtr` lives for at least the duration of the wait queue link.
161167
unsafe { freeq_slot.replace(Some(slot), cs) };
162168
wait_head.wake();
163169
} else {
164170
assert!(!self.access(cs).freeq.is_full());
165-
unsafe { self.access(cs).freeq.push_back_unchecked(slot) }
171+
unsafe { self.access(cs).freeq.push_back_unchecked(slot.0) }
166172
}
167173
});
168174
}
169175

170-
/// Push a value into a specific slot.
176+
/// Send a value using the given `slot` in this channel.
171177
///
172-
/// SAFETY: `slot` must be obtained from [`Channel::pop_free_slot`] or through a popped [`SlotPtr`].
178+
/// SAFETY: `slot` must be obtained from this exact channel instance.
173179
#[inline(always)]
174-
pub(crate) unsafe fn slot_ready(&self, slot: u8, val: T) {
180+
pub(crate) unsafe fn send_value(&self, slot: FreeSlot, val: T) {
181+
let slot = slot.0;
175182
// Write the value to the slots, note; this memcpy is not under a critical section.
176183
unsafe { ptr::write(self.slots.get_unchecked(slot as usize).get() as *mut T, val) }
177184

@@ -187,16 +194,21 @@ impl<T, const N: usize> Channel<T, N> {
187194
self.receiver_waker.wake();
188195
}
189196

190-
/// Pop a ready slot.
191-
pub(crate) fn pop_ready_slot(&self) -> Option<T> {
197+
/// Pop the value of a ready slot to make it available to a receiver.
198+
///
199+
/// Internally, this function does these things:
200+
/// 1. Pop a ready slot from the ready queue.
201+
/// 2. If available, read the data from the backing slot storage.
202+
/// 3. If available, return the now-free slot to the free queue.
203+
pub(crate) fn receive_value(&self) -> Option<T> {
192204
let ready_slot = critical_section::with(|cs| self.access(cs).readyq.pop_front());
193205

194206
if let Some(rs) = ready_slot {
195207
let r = unsafe { ptr::read(self.slots.get_unchecked(rs as usize).get() as *const T) };
196208

197209
// Return the index to the free queue after we've read the value.
198-
// SAFETY: `rs` comes directly from `readyq`.
199-
unsafe { self.push_free_slot(rs) };
210+
// SAFETY: `rs` is now a free slot obtained from this channel.
211+
unsafe { self.return_free_slot(FreeSlot(rs)) };
200212

201213
Some(r)
202214
} else {
@@ -207,7 +219,7 @@ impl<T, const N: usize> Channel<T, N> {
207219
/// Register a new waiter in the wait queue.
208220
///
209221
/// SAFETY: `link` must be valid until it is popped.
210-
pub(crate) unsafe fn push_to_wait_queue(&self, link: Pin<&Link<WaitQueueData>>) {
222+
pub(crate) unsafe fn push_wait_queue(&self, link: Pin<&Link<WaitQueueData>>) {
211223
self.wait_queue.push(link);
212224
}
213225

@@ -216,8 +228,9 @@ impl<T, const N: usize> Channel<T, N> {
216228
}
217229

218230
/// Pop a free slot.
219-
pub(crate) fn pop_free_slot(&self) -> Option<u8> {
220-
critical_section::with(|cs| self.access(cs).freeq.pop_front())
231+
pub(crate) fn pop_free_slot(&self) -> Option<FreeSlot> {
232+
let slot = critical_section::with(|cs| self.access(cs).freeq.pop_front());
233+
slot.map(FreeSlot)
221234
}
222235

223236
pub(crate) fn num_senders(&self) -> usize {

rtic-sync/src/channel/receiver.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ impl<T, const N: usize> Receiver<'_, T, N> {
3737
/// Receives a value if there is one in the channel, non-blocking.
3838
pub fn try_recv(&mut self) -> Result<T, ReceiveError> {
3939
// Try to get a ready slot.
40-
let ready_slot = self.0.pop_ready_slot();
40+
let ready_slot = self.0.receive_value();
4141

4242
if let Some(value) = ready_slot {
4343
Ok(value)

rtic-sync/src/channel/sender.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use core::{future::poll_fn, pin::Pin, task::Poll};
33
use rtic_common::{dropper::OnDrop, wait_queue::Link};
44

55
use super::{
6-
channel::{FreeSlotPtr, WaitQueueData},
6+
channel::{FreeSlot, FreeSlotPtr, WaitQueueData},
77
Channel,
88
};
99

@@ -113,15 +113,15 @@ impl<T, const N: usize> Sender<'_, T, N> {
113113
return Err(TrySendError::Full(val));
114114
};
115115

116-
unsafe { self.0.slot_ready(idx, val) };
116+
unsafe { self.0.send_value(idx, val) };
117117

118118
Ok(())
119119
}
120120

121121
/// Send a value. If there is no place left in the queue this will wait until there is.
122122
/// If the receiver does not exist this will return an error.
123123
pub async fn send(&mut self, val: T) -> Result<(), NoReceiver<T>> {
124-
let mut free_slot_ptr: Option<u8> = None;
124+
let mut free_slot_ptr: Option<FreeSlot> = None;
125125
let mut link_ptr: Option<Link<WaitQueueData>> = None;
126126

127127
// Make this future `Drop`-safe.
@@ -146,8 +146,8 @@ impl<T, const N: usize> Sender<'_, T, N> {
146146
// about this a bit more...
147147
critical_section::with(|cs| {
148148
if let Some(freed_slot) = unsafe { free_slot_ptr2.take(cs) } {
149-
// SAFETY: `freed_slot` is obtained through `FreeSlotPtr::take`.
150-
unsafe { self.0.push_free_slot(freed_slot) };
149+
// SAFETY: `freed_slot` is a free slot in our referenced channel.
150+
unsafe { self.0.return_free_slot(freed_slot) };
151151
}
152152
});
153153
});
@@ -197,7 +197,7 @@ impl<T, const N: usize> Sender<'_, T, N> {
197197
// are shadowed and we make sure in `dropper` that the link is removed from the queue
198198
// before dropping `link_ptr` AND `dropper` makes sure that the shadowed
199199
// `ptr`s live until the end of the stack frame.
200-
unsafe { self.0.push_to_wait_queue(Pin::new_unchecked(link_ref)) };
200+
unsafe { self.0.push_wait_queue(Pin::new_unchecked(link_ref)) };
201201

202202
Poll::Pending
203203
}
@@ -210,7 +210,7 @@ impl<T, const N: usize> Sender<'_, T, N> {
210210

211211
if let Ok(slot) = idx {
212212
// SAFETY: `slot` is provided through a `SlotPtr` or comes from `pop_free_slot`.
213-
unsafe { self.0.slot_ready(slot, val) };
213+
unsafe { self.0.send_value(slot, val) };
214214

215215
Ok(())
216216
} else {

0 commit comments

Comments
 (0)