#![doc(test(attr(deny(warnings))))]
#![warn(missing_docs)]
use std::alloc::Layout;
use std::mem::ManuallyDrop;
use std::ptr::NonNull;
use std::sync::Mutex;
use bumpalo::Bump;
type HerdInner = Vec<Box<Bump>>;
#[derive(Default, Debug)]
pub struct Herd(Mutex<HerdInner>);
impl Herd {
pub fn new() -> Self {
Self::default()
}
pub fn reset(&mut self) {
for e in self.0.get_mut().unwrap().iter_mut() {
e.reset();
}
}
pub fn get(&self) -> Member<'_> {
let mut lock = self.0.lock().unwrap();
let bump = lock.pop().unwrap_or_default();
Member {
arena: ManuallyDrop::new(bump),
owner: self,
}
}
}
#[derive(Debug)]
pub struct Member<'h> {
arena: ManuallyDrop<Box<Bump>>,
owner: &'h Herd,
}
macro_rules! alloc_fn {
($(pub fn $name: ident<($($g: tt)*)>(&self, $($pname: ident: $pty: ty),*) -> $res: ty;)*) => {
$(
pub fn $name<$($g)*>(&self, $($pname: $pty),*) -> $res {
self.extend(self.arena.$name($($pname),*))
}
)*
}
}
#[allow(missing_docs)] impl<'h> Member<'h> {
alloc_fn! {
pub fn alloc<(T)>(&self, val: T) -> &'h mut T;
pub fn alloc_with<(T, F: FnOnce() -> T)>(&self, f: F) -> &'h mut T;
pub fn alloc_str<()>(&self, src: &str) -> &'h mut str;
pub fn alloc_slice_clone<(T: Clone)>(&self, src: &[T]) -> &'h mut [T];
pub fn alloc_slice_copy<(T: Copy)>(&self, src: &[T]) -> &'h mut [T];
pub fn alloc_slice_fill_clone<(T: Clone)>(&self, len: usize, value: &T) -> &'h mut [T];
pub fn alloc_slice_fill_copy<(T: Copy)>(&self, len: usize, value: T) -> &'h mut [T];
pub fn alloc_slice_fill_default<(T: Default)>(&self, len: usize) -> &'h mut [T];
pub fn alloc_slice_fill_with<(T, F: FnMut(usize) -> T)>(&self, len: usize, f: F)
-> &'h mut [T];
}
pub fn alloc_slice_fill_iter<T, I>(&self, iter: I) -> &'h mut [T]
where
I: IntoIterator<Item = T>,
I::IntoIter: ExactSizeIterator,
{
self.extend(self.arena.alloc_slice_fill_iter(iter))
}
pub fn alloc_layout(&self, layout: Layout) -> NonNull<u8> {
self.arena.as_ref().alloc_layout(layout)
}
}
impl<'h> Member<'h> {
fn extend<'s, T: ?Sized>(&'s self, v: &'s mut T) -> &'h mut T {
let result = v as *mut T;
unsafe { &mut *result }
}
pub fn as_bump(&self) -> &Bump {
&self.arena
}
}
impl Drop for Member<'_> {
fn drop(&mut self) {
let mut lock = self.owner.0.lock().unwrap();
let member = unsafe { ManuallyDrop::take(&mut self.arena) };
lock.push(member);
}
}
#[cfg(test)]
#[cfg_attr(all(miri, target_os = "windows"), allow(unused_imports))]
mod tests {
use std::sync::Mutex;
use super::*;
use crossbeam_utils::thread;
#[test]
#[cfg(not(all(miri, target_os = "windows")))]
fn alloc_miri() {
let mut herd = Herd::new();
let v = Mutex::new(Vec::new());
thread::scope(|s| {
s.spawn(|_| {
let bump = herd.get();
v.lock().unwrap().push(bump.alloc(42));
});
})
.unwrap();
let sum: u32 = v.into_inner().unwrap().iter().map(|i| **i).sum();
assert_eq!(42, sum);
herd.reset();
let hello = herd.get().alloc_str("hello");
assert_eq!("hello", hello);
}
}