use std::alloc::Layout;
use crossbeam_queue::SegQueue;
struct DeferredItem {
ptr: *mut u8,
layout: Layout,
drop_fn: Option<unsafe fn(*mut u8)>,
}
unsafe impl Send for DeferredItem {}
unsafe impl Sync for DeferredItem {}
pub struct MkDeferredQueue {
queue: SegQueue<DeferredItem>,
}
impl MkDeferredQueue {
pub fn new() -> Self {
Self {
queue: SegQueue::new(),
}
}
pub unsafe fn push(&self, ptr: *mut u8, layout: Layout, drop_fn: Option<unsafe fn(*mut u8)>) {
self.queue.push(DeferredItem { ptr, layout, drop_fn });
}
pub fn reclaim<F>(&self, mut free_fn: F) -> usize
where
F: FnMut(*mut u8, Layout),
{
let mut count = 0;
while let Some(item) = self.queue.pop() {
unsafe {
if let Some(drop_fn) = item.drop_fn {
drop_fn(item.ptr);
}
free_fn(item.ptr, item.layout);
}
count += 1;
}
count
}
pub fn is_empty(&self) -> bool {
self.queue.is_empty()
}
pub fn len(&self) -> usize {
self.queue.len()
}
}
impl Default for MkDeferredQueue {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
#[test]
fn test_deferred_queue_basic() {
let queue = MkDeferredQueue::new();
let dropped = Arc::new(AtomicBool::new(false));
let dropped_clone = Arc::clone(&dropped);
unsafe {
let ptr = std::alloc::alloc(Layout::new::<u32>());
queue.push(ptr, Layout::new::<u32>(), None);
}
let mut freed = false;
queue.reclaim(|ptr, layout| {
unsafe { std::alloc::dealloc(ptr, layout) };
freed = true;
});
assert!(freed);
assert!(queue.is_empty());
}
}