use crate::{pool::Pool, Error, Result, Status};
use alloc::boxed::Box;
use core::ffi::c_void;
use core::marker::PhantomData;
use core::ptr;
pub struct Queue<'pool> {
ptr: *mut apr_sys::apr_queue_t,
_phantom: PhantomData<&'pool Pool<'pool>>,
}
impl<'pool> Queue<'pool> {
pub fn new(capacity: u32, pool: &'pool Pool<'pool>) -> Result<Self> {
let mut queue_ptr: *mut apr_sys::apr_queue_t = ptr::null_mut();
let status =
unsafe { apr_sys::apr_queue_create(&mut queue_ptr, capacity, pool.as_mut_ptr()) };
if status != apr_sys::APR_SUCCESS as i32 {
return Err(Error::from_status(Status::from(status)));
}
Ok(Queue {
ptr: queue_ptr,
_phantom: PhantomData,
})
}
pub unsafe fn from_ptr(ptr: *mut apr_sys::apr_queue_t) -> Self {
Self {
ptr,
_phantom: PhantomData,
}
}
pub unsafe fn push(&mut self, data: *mut c_void) -> Result<()> {
let status = apr_sys::apr_queue_push(self.ptr, data);
if status != apr_sys::APR_SUCCESS as i32 {
return Err(Error::from_status(Status::from(status)));
}
Ok(())
}
pub unsafe fn try_push(&mut self, data: *mut c_void) -> Result<()> {
let status = apr_sys::apr_queue_trypush(self.ptr, data);
if status != apr_sys::APR_SUCCESS as i32 {
return Err(Error::from_status(Status::from(status)));
}
Ok(())
}
pub fn pop(&mut self) -> Result<*mut c_void> {
let mut data: *mut c_void = ptr::null_mut();
let status = unsafe { apr_sys::apr_queue_pop(self.ptr, &mut data) };
if status != apr_sys::APR_SUCCESS as i32 {
return Err(Error::from_status(Status::from(status)));
}
Ok(data)
}
pub fn try_pop(&mut self) -> Result<*mut c_void> {
let mut data: *mut c_void = ptr::null_mut();
let status = unsafe { apr_sys::apr_queue_trypop(self.ptr, &mut data) };
if status != apr_sys::APR_SUCCESS as i32 {
return Err(Error::from_status(Status::from(status)));
}
Ok(data)
}
pub fn size(&self) -> u32 {
unsafe { apr_sys::apr_queue_size(self.ptr) }
}
pub fn is_empty(&self) -> bool {
self.size() == 0
}
pub fn interrupt_all(&mut self) -> Result<()> {
let status = unsafe { apr_sys::apr_queue_interrupt_all(self.ptr) };
if status != apr_sys::APR_SUCCESS as i32 {
return Err(Error::from_status(Status::from(status)));
}
Ok(())
}
pub fn terminate(&mut self) -> Result<()> {
let status = unsafe { apr_sys::apr_queue_term(self.ptr) };
if status != apr_sys::APR_SUCCESS as i32 {
return Err(Error::from_status(Status::from(status)));
}
Ok(())
}
pub unsafe fn as_ptr(&self) -> *const apr_sys::apr_queue_t {
self.ptr
}
pub unsafe fn as_mut_ptr(&mut self) -> *mut apr_sys::apr_queue_t {
self.ptr
}
}
unsafe impl<'pool> Send for Queue<'pool> {}
unsafe impl<'pool> Sync for Queue<'pool> {}
impl<'pool> core::fmt::Debug for Queue<'pool> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Queue")
.field("size", &self.size())
.field("ptr", &self.ptr)
.finish()
}
}
pub struct TypedQueue<'pool, T> {
inner: Queue<'pool>,
_phantom: PhantomData<T>,
}
impl<'pool, T> TypedQueue<'pool, T> {
pub fn new(capacity: u32, pool: &'pool Pool) -> Result<Self> {
Ok(TypedQueue {
inner: Queue::new(capacity, pool)?,
_phantom: PhantomData,
})
}
pub unsafe fn from_ptr(ptr: *mut apr_sys::apr_queue_t) -> Self {
Self {
inner: Queue::from_ptr(ptr),
_phantom: PhantomData,
}
}
pub fn push_ref(&mut self, data: &'pool T) -> Result<()> {
unsafe { self.inner.push(data as *const T as *mut c_void) }
}
pub fn try_push_ref(&mut self, data: &'pool T) -> Result<()> {
unsafe { self.inner.try_push(data as *const T as *mut c_void) }
}
pub fn pop_ref(&mut self) -> Result<&'pool T> {
let ptr = self.inner.pop()?;
Ok(unsafe { &*(ptr as *const T) })
}
pub fn try_pop_ref(&mut self) -> Result<&'pool T> {
let ptr = self.inner.try_pop()?;
Ok(unsafe { &*(ptr as *const T) })
}
pub fn size(&self) -> u32 {
self.inner.size()
}
pub fn is_empty(&self) -> bool {
self.inner.is_empty()
}
pub fn interrupt_all(&mut self) -> Result<()> {
self.inner.interrupt_all()
}
pub fn terminate(&mut self) -> Result<()> {
self.inner.terminate()
}
}
unsafe impl<'pool, T: Send> Send for TypedQueue<'pool, T> {}
unsafe impl<'pool, T: Send> Sync for TypedQueue<'pool, T> {}
pub struct BoxedQueue<'pool, T: Send> {
inner: Queue<'pool>,
_phantom: PhantomData<T>,
}
impl<'pool, T: Send> BoxedQueue<'pool, T> {
pub fn new(capacity: u32, pool: &'pool Pool) -> Result<Self> {
Ok(BoxedQueue {
inner: Queue::new(capacity, pool)?,
_phantom: PhantomData,
})
}
pub fn push(&mut self, value: T) -> Result<()> {
let boxed = Box::new(value);
let ptr = Box::into_raw(boxed);
unsafe { self.inner.push(ptr as *mut c_void) }
}
pub fn try_push(&mut self, value: T) -> Result<()> {
let boxed = Box::new(value);
let ptr = Box::into_raw(boxed);
match unsafe { self.inner.try_push(ptr as *mut c_void) } {
Ok(()) => Ok(()),
Err(e) => {
unsafe {
drop(Box::from_raw(ptr));
}
Err(e)
}
}
}
pub fn pop(&mut self) -> Result<T> {
let ptr = self.inner.pop()?;
Ok(*unsafe { Box::from_raw(ptr as *mut T) })
}
pub fn try_pop(&mut self) -> Result<T> {
let ptr = self.inner.try_pop()?;
Ok(*unsafe { Box::from_raw(ptr as *mut T) })
}
pub fn size(&self) -> u32 {
self.inner.size()
}
pub fn is_empty(&self) -> bool {
self.inner.is_empty()
}
pub fn interrupt_all(&mut self) -> Result<()> {
self.inner.interrupt_all()
}
pub fn terminate(&mut self) -> Result<()> {
self.inner.terminate()
}
}
impl<'pool, T: Send> core::fmt::Debug for BoxedQueue<'pool, T> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("BoxedQueue")
.field("size", &self.size())
.finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::Pool;
use alloc::string::String;
#[test]
fn test_queue_basic() {
let pool = Pool::new();
let mut queue = Queue::new(10, &pool).unwrap();
assert!(queue.is_empty());
assert_eq!(queue.size(), 0);
let val1 = 42i32;
let val2 = 84i32;
unsafe {
queue.push(&val1 as *const i32 as *mut c_void).unwrap();
queue.push(&val2 as *const i32 as *mut c_void).unwrap();
}
assert_eq!(queue.size(), 2);
assert!(!queue.is_empty());
let out1 = queue.pop().unwrap();
let out2 = queue.pop().unwrap();
unsafe {
assert_eq!(*(out1 as *const i32), 42);
assert_eq!(*(out2 as *const i32), 84);
}
assert!(queue.is_empty());
}
#[test]
fn test_typed_queue() {
let pool = Pool::new();
let mut queue = TypedQueue::<i32>::new(10, &pool).unwrap();
let val1 = 42;
let val2 = 84;
queue.push_ref(&val1).unwrap();
queue.push_ref(&val2).unwrap();
assert_eq!(queue.size(), 2);
let out1 = queue.pop_ref().unwrap();
let out2 = queue.pop_ref().unwrap();
assert_eq!(*out1, 42);
assert_eq!(*out2, 84);
}
#[test]
fn test_boxed_queue() {
let pool = Pool::new();
let mut queue = BoxedQueue::new(10, &pool).unwrap();
assert!(queue.is_empty());
queue.push(String::from("hello")).unwrap();
queue.push(String::from("world")).unwrap();
assert_eq!(queue.size(), 2);
let s1 = queue.pop().unwrap();
let s2 = queue.pop().unwrap();
assert_eq!(s1, "hello");
assert_eq!(s2, "world");
assert!(queue.is_empty());
}
#[test]
fn test_queue_try_operations() {
let pool = Pool::new();
let mut queue = BoxedQueue::<i32>::new(1, &pool).unwrap();
assert!(queue.try_pop().is_err());
queue.try_push(42).unwrap();
assert!(queue.try_push(84).is_err());
let val = queue.try_pop().unwrap();
assert_eq!(val, 42);
}
}