use std::ffi::c_void;
pub struct HandleArena {
handles: Vec<*mut c_void>,
destructors: Vec<unsafe extern "C" fn(*mut c_void)>,
}
impl HandleArena {
pub fn new() -> Self {
Self {
handles: Vec::new(),
destructors: Vec::new(),
}
}
pub fn register(&mut self, ptr: *mut c_void, dtor: unsafe extern "C" fn(*mut c_void)) {
self.handles.push(ptr);
self.destructors.push(dtor);
}
pub fn destroy_all(&mut self) {
for (ptr, dtor) in self.handles.drain(..).zip(self.destructors.drain(..)) {
unsafe { dtor(ptr) };
}
}
}
impl Default for HandleArena {
fn default() -> Self {
Self::new()
}
}
#[no_mangle]
pub extern "C" fn weaveffi_arena_create() -> *mut HandleArena {
Box::into_raw(Box::new(HandleArena::new()))
}
#[no_mangle]
pub extern "C" fn weaveffi_arena_register(
arena: *mut HandleArena,
ptr: *mut c_void,
dtor: unsafe extern "C" fn(*mut c_void),
) {
if arena.is_null() {
return;
}
let arena = unsafe { &mut *arena };
arena.register(ptr, dtor);
}
#[no_mangle]
pub extern "C" fn weaveffi_arena_destroy(arena: *mut HandleArena) {
if arena.is_null() {
return;
}
let mut arena = unsafe { *Box::from_raw(arena) };
arena.destroy_all();
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::atomic::{AtomicUsize, Ordering};
unsafe extern "C" fn counting_dtor(ptr: *mut c_void) {
let counter = ptr as *const AtomicUsize;
(*counter).fetch_add(1, Ordering::SeqCst);
}
#[test]
fn arena_destroys_all_handles() {
let counter = AtomicUsize::new(0);
let counter_ptr = &counter as *const AtomicUsize as *mut c_void;
let mut arena = HandleArena::new();
for _ in 0..5 {
arena.register(counter_ptr, counting_dtor);
}
assert_eq!(counter.load(Ordering::SeqCst), 0);
arena.destroy_all();
assert_eq!(counter.load(Ordering::SeqCst), 5);
}
#[test]
fn arena_destroy_all_is_idempotent() {
let counter = AtomicUsize::new(0);
let counter_ptr = &counter as *const AtomicUsize as *mut c_void;
let mut arena = HandleArena::new();
arena.register(counter_ptr, counting_dtor);
arena.destroy_all();
arena.destroy_all();
assert_eq!(counter.load(Ordering::SeqCst), 1);
}
#[test]
fn arena_c_api_roundtrip() {
let counter = AtomicUsize::new(0);
let counter_ptr = &counter as *const AtomicUsize as *mut c_void;
let arena = weaveffi_arena_create();
assert!(!arena.is_null());
weaveffi_arena_register(arena, counter_ptr, counting_dtor);
weaveffi_arena_register(arena, counter_ptr, counting_dtor);
weaveffi_arena_destroy(arena);
assert_eq!(counter.load(Ordering::SeqCst), 2);
}
#[test]
fn arena_null_register_is_safe() {
unsafe extern "C" fn noop(_ptr: *mut c_void) {}
weaveffi_arena_register(
std::ptr::null_mut(),
std::ptr::dangling_mut::<c_void>(),
noop,
);
}
#[test]
fn arena_null_destroy_is_safe() {
weaveffi_arena_destroy(std::ptr::null_mut());
}
}