rt/rust/src/sync/queue.rs

152 lines
4.5 KiB
Rust

use core::{
cell::UnsafeCell,
ffi::{c_uchar, c_void},
marker::{PhantomData, PhantomPinned},
mem::{size_of, MaybeUninit},
panic::{RefUnwindSafe, UnwindSafe},
};
use crate::{
bindings::{
rt_queue, rt_queue_peek, rt_queue_pop, rt_queue_push, rt_queue_timedpeek,
rt_queue_timedpop, rt_queue_timedpush, rt_queue_trypeek, rt_queue_trypop, rt_queue_trypush,
},
sync::semaphore::c_sem_init,
tick::Utick,
};
pub struct Queue<T: ?Sized> {
q: UnsafeCell<rt_queue>,
_phantom_elem: PhantomData<T>,
_pin_marker: PhantomPinned,
}
unsafe impl<T: ?Sized + Send> Send for Queue<T> {}
unsafe impl<T: ?Sized + Send> Sync for Queue<T> {}
impl<T: ?Sized> UnwindSafe for Queue<T> {}
impl<T: ?Sized> RefUnwindSafe for Queue<T> {}
impl<T> Queue<T> {
pub const fn init<const N: usize>(
queue: &'static Queue<T>,
slots: &'static [c_uchar; N],
data: &'static [T; N],
) -> Queue<T> {
let q = unsafe { &*(queue.q.get() as *const rt_queue) };
Queue {
q: UnsafeCell::new(rt_queue {
push_sem: c_sem_init(&q.push_sem, N as i32, i32::max_value()),
pop_sem: c_sem_init(&q.pop_sem, 0, i32::max_value()),
enq: 0,
deq: 0,
slots: slots.as_ptr() as *mut c_uchar,
data: data.as_ptr() as *mut c_void,
num_elems: N,
elem_size: size_of::<T>(),
}),
_phantom_elem: PhantomData,
_pin_marker: PhantomPinned,
}
}
pub fn push(&self, elem: T) {
unsafe { rt_queue_push(self.q.get(), &elem as *const T as *const c_void) }
}
pub fn pop(&self) -> T {
let mut elem = MaybeUninit::<T>::uninit();
unsafe {
rt_queue_pop(self.q.get(), elem.as_mut_ptr() as *mut c_void);
elem.assume_init()
}
}
pub fn peek(&self) -> T {
let mut elem = MaybeUninit::<T>::uninit();
unsafe {
rt_queue_peek(self.q.get(), elem.as_mut_ptr() as *mut c_void);
elem.assume_init()
}
}
pub fn try_push(&self, elem: T) -> Result<(), T> {
if unsafe { rt_queue_trypush(self.q.get(), &elem as *const T as *const c_void) } {
Ok(())
} else {
Err(elem)
}
}
pub fn try_pop(&self) -> Option<T> {
let mut elem = MaybeUninit::<T>::uninit();
if unsafe { rt_queue_trypop(self.q.get(), elem.as_mut_ptr() as *mut c_void) } {
Some(unsafe { elem.assume_init() })
} else {
None
}
}
pub fn try_peek(&self) -> Option<T> {
let mut elem = MaybeUninit::<T>::uninit();
if unsafe { rt_queue_trypeek(self.q.get(), elem.as_mut_ptr() as *mut c_void) } {
Some(unsafe { elem.assume_init() })
} else {
None
}
}
pub fn timed_push(&self, elem: T, ticks: Utick) -> Result<(), T> {
if unsafe { rt_queue_timedpush(self.q.get(), &elem as *const T as *const c_void, ticks) } {
Ok(())
} else {
Err(elem)
}
}
pub fn timed_pop(&self, ticks: Utick) -> Option<T> {
let mut elem = MaybeUninit::<T>::uninit();
if unsafe { rt_queue_timedpop(self.q.get(), elem.as_mut_ptr() as *mut c_void, ticks) } {
Some(unsafe { elem.assume_init() })
} else {
None
}
}
pub fn timed_peek(&self, ticks: Utick) -> Option<T> {
let mut elem = MaybeUninit::<T>::uninit();
if unsafe { rt_queue_timedpeek(self.q.get(), elem.as_mut_ptr() as *mut c_void, ticks) } {
Some(unsafe { elem.assume_init() })
} else {
None
}
}
}
#[macro_export]
macro_rules! queue {
($name: ident, $type: ty, $num: literal) => {
$crate::paste::paste! {
static mut [< $name _SLOTS >]: [core::ffi::c_uchar; $num] = [0u8; $num];
static mut [< $name _DATA >]: [$type; $num] =
unsafe { core::mem::transmute([0u8; core::mem::size_of::<$type>() * $num]) };
static $name: $crate::sync::Queue<$type> = $crate::sync::Queue::init(
&$name,
unsafe { &[< $name _SLOTS >] },
unsafe { &[< $name _DATA >] },
);
}
};
}
#[cfg(test)]
mod tests {
queue!(QUEUE, i32, 10);
#[test]
fn fast_path() {
QUEUE.push(1);
assert!(QUEUE.pop() == 1);
assert!(QUEUE.try_pop().is_none());
}
}