Skip to content

Commit 8754c3d

Browse files
committed
Added missing {enqueue, dequeue}_unchecked interfaces
1 parent bf6728e commit 8754c3d

File tree

1 file changed

+56
-9
lines changed

1 file changed

+56
-9
lines changed

src/spsc.rs

+56-9
Original file line numberDiff line numberDiff line change
@@ -224,18 +224,27 @@ impl<T, const N: usize> Queue<T, N> {
224224
}
225225
}
226226

227+
// The memory for enqueueing is "owned" by the tail pointer.
228+
// NOTE: This internal function uses internal mutability to allow the [`Producer`] to enqueue
229+
// items without doing pointer arithmetic and accessing internal fields of this type.
230+
unsafe fn inner_enqueue_unchecked(&self, val: T) {
231+
let current_tail = self.tail.load(Ordering::Relaxed);
232+
233+
(self.buffer.get_unchecked(current_tail).get()).write(MaybeUninit::new(val));
234+
self.tail
235+
.store(Self::increment(current_tail), Ordering::Release);
236+
}
237+
238+
/// Adds an `item` to the end of the queue, without checking if it's full
239+
///
227240
/// # Unsafety
228241
///
229242
/// If the queue is full this operation will leak a value (T's destructor won't run on
230243
/// the value that got overwritten by `item`), *and* will allow the `dequeue` operation
231244
/// to create a copy of `item`, which could result in `T`'s destructor running on `item`
232245
/// twice.
233-
unsafe fn enqueue_unchecked(&mut self, val: T) {
234-
let current_tail = self.tail.load(Ordering::Relaxed);
235-
236-
(self.buffer.get_unchecked(current_tail).get()).write(MaybeUninit::new(val));
237-
self.tail
238-
.store(Self::increment(current_tail), Ordering::Release);
246+
pub unsafe fn enqueue_unchecked(&mut self, val: T) {
247+
self.inner_enqueue_unchecked(val)
239248
}
240249

241250
// The memory for dequeuing is "owned" by the head pointer,.
@@ -256,6 +265,29 @@ impl<T, const N: usize> Queue<T, N> {
256265
}
257266
}
258267

268+
// The memory for dequeuing is "owned" by the head pointer,.
269+
// NOTE: This internal function uses internal mutability to allow the [`Consumer`] to dequeue
270+
// items without doing pointer arithmetic and accessing internal fields of this type.
271+
unsafe fn inner_dequeue_unchecked(&self) -> T {
272+
let current_head = self.head.load(Ordering::Relaxed);
273+
let v = (self.buffer.get_unchecked(current_head).get() as *const T).read();
274+
275+
self.head
276+
.store(Self::increment(current_head), Ordering::Release);
277+
278+
v
279+
}
280+
281+
/// Returns the item in the front of the queue, without checking if there is something in the
282+
/// queue
283+
///
284+
/// # Unsafety
285+
///
286+
/// If the queue is empty this operation will return uninitialized memory.
287+
pub unsafe fn dequeue_unchecked(&mut self) -> T {
288+
self.inner_dequeue_unchecked()
289+
}
290+
259291
/// Splits a queue into producer and consumer endpoints
260292
pub fn split(&mut self) -> (Producer<'_, T, N>, Consumer<'_, T, N>) {
261293
(Producer { rb: self }, Consumer { rb: self })
@@ -464,6 +496,15 @@ impl<'a, T, const N: usize> Consumer<'a, T, N> {
464496
unsafe { self.rb.inner_dequeue() }
465497
}
466498

499+
/// Returns the item in the front of the queue, without checking if there are elements in the
500+
/// queue
501+
///
502+
/// See [`Queue::dequeue_unchecked`] for safety
503+
#[inline]
504+
pub unsafe fn dequeue_unchecked(&mut self) -> T {
505+
self.rb.inner_dequeue_unchecked()
506+
}
507+
467508
/// Returns if there are any items to dequeue. When this returns `true`, at least the
468509
/// first subsequent dequeue will succeed
469510
#[inline]
@@ -505,14 +546,20 @@ impl<'a, T, const N: usize> Consumer<'a, T, N> {
505546
}
506547

507548
impl<'a, T, const N: usize> Producer<'a, T, N> {
508-
/// Adds an `item` to the end of the queue
509-
///
510-
/// Returns back the `item` if the queue is full
549+
/// Adds an `item` to the end of the queue, returns back the `item` if the queue is full
511550
#[inline]
512551
pub fn enqueue(&mut self, val: T) -> Result<(), T> {
513552
unsafe { self.rb.inner_enqueue(val) }
514553
}
515554

555+
/// Adds an `item` to the end of the queue, without checking if the queue is full
556+
///
557+
/// See [`Queue::enqueue_unchecked`] for safety
558+
#[inline]
559+
pub unsafe fn enqueue_unchecked(&mut self, val: T) {
560+
self.rb.inner_enqueue_unchecked(val)
561+
}
562+
516563
/// Returns if there is any space to enqueue a new item. When this returns true, at
517564
/// least the first subsequent enqueue will succeed.
518565
#[inline]

0 commit comments

Comments
 (0)