152 lines
4.5 KiB
Rust
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());
|
|
}
|
|
}
|