dynamic_arena/lib.rs
1//! Implements dynamically typed arenas, where any type of item can be allocated.
2#![deny(missing_docs)]
3use std::alloc::Layout;
4use std::cell::RefCell;
5use std::marker::PhantomData;
6use std::mem;
7use std::os::raw::c_void;
8use std::ptr::{self, NonNull};
9
10use bumpalo::Bump;
11
12/// Marker trait that indicates whether or a `DynamicArena` may be sent across threads
13pub trait SendAbility: Sized {
14 /// Create an arena corresponding to this type of thread-safety
15 fn create_arena<'a>() -> DynamicArena<'a, Self>;
16}
17/// Marker type that indicates you expect everything in the `DynamicArena` to be `Send`
18///
19/// Although this prevents you from allocating non-`Send` types in the arena,
20/// it allows you to `Send` the dynamic itself arena across threads.
21/// We can't safely implement `Send` for `DynamicArena` without this bound,
22/// since you could otherwise place a `Rc` in the arena, send it across threads,
23/// and then proceed to drop the arena and mutate the reference count.
24pub struct Sendable {
25 _marker: (),
26}
27impl SendAbility for Sendable {
28 #[inline]
29 fn create_arena<'a>() -> DynamicArena<'a, Self> {
30 DynamicArena::new_send()
31 }
32}
33/// Marker type that indiates everything in the `DynamicArena` isn't nesicarrily `Send`.
34///
35/// This prevents you from `Send`ing the arena itself across threads,
36/// as described in the `Sendable` docs.
37pub struct NonSend {
38 _marker: std::rc::Rc<()>,
39}
40impl SendAbility for NonSend {
41 #[inline]
42 fn create_arena<'a>() -> DynamicArena<'a, Self> {
43 DynamicArena::new_bounded()
44 }
45}
46
47struct DynamicArenaItem {
48 drop: unsafe fn(*mut c_void),
49 value: *mut c_void,
50}
51impl Drop for DynamicArenaItem {
52 #[inline]
53 fn drop(&mut self) {
54 unsafe { (self.drop)(self.value) }
55 }
56}
57unsafe impl Send for DynamicArenaItem {}
58
59/// An alias for an arena allocator which requires that everything is `Send + 'a`.
60pub type DynamicSendArena<'a> = DynamicArena<'a, Sendable>;
61
62/// An arena allocator where any type of object can be allocated.
63///
64/// Unlike typed arenas, any `Sized` object can be allocated here and they
65/// don't all have to have the same statically known type in advance.
66/// Usually you don't have to worry about the arena's lifetime,
67/// since it should be static by default (see).
68///
69/// ## Performance
70/// Although this is _slightly_ slower than a `typed_arena::Arena`,
71/// it can be much more memory and time efficient than having a ton of seperate typed arenas.
72/// The only point where dynamic dispatch actually gets involved is when the arena is dropped,
73/// since we have to dynamically dispatch the drop functions instead of statically dispatching them.
74///
75/// ## Safety
76/// In order to prevent use after free in a `DynamicArena`, all pointers in the allocated items
77/// need to be valid for the lifetime `'a` to ensure all references outlive the arena itself.
78/// Unfortunately, this statically prevents all self referential structs with `alloc`,
79/// since they can't be known ahead of time to be safe and outlive the arena itself,
80/// and we can't perform [dropchk](https://doc.rust-lang.org/nightly/nomicon/dropck.html)
81/// on a dynamically typed arena (only statically typed ones).
82///
83/// The alternatives to this are `alloc_unchecked`, which bypasses the lifetime and safety,
84/// and `alloc_copy`, which bypasses the lifetime by ensuring `T: Copy`.
85/// This is safe, since a copyable item can never have a custom drop,
86/// and the drop function could never possibly trigger use after free.
87///
88/// This means you can use self-referential structs with a `DynamicArena` as long as they implement `Copy`.
89/// One way to make your types implement copy and support self-refrential structs,
90/// is by replacing owned objects with their borrowed counterparts and then allocating them in the arena.
91/// For example
92/// ````
93/// struct OwnedSelfReferential<'a> {
94/// next: Option<&'a OwnedSelfReferential<'a>>,
95/// text: String,
96/// array: Vec<u32>
97/// }
98/// ````
99/// can't be used with either `alloc` (since it's self referential),
100/// nor `alloc_copy` (since `String` and `Vec` need to be dropped).
101/// However by replacing `String` with `&'a str` and `&'a [u32]`,
102/// we can make the structure `Copy` and enable use with `alloc_copy`.
103///
104/// Then, when someone needs to arena-allocate the struct they can use
105/// the same arena to allocate the `String` and `Vec<u32>` first,
106/// before they proceed to allocate the copyable struct.
107pub struct DynamicArena<'a, S = NonSend> {
108 /// The underlying arena, where we request that they allocate arbitrary bytes.
109 handle: Bump,
110 /// The list of untyped values we've allocated in the arena,
111 /// and whose drop functions need to be invoked.
112 ///
113 /// The drop functions are dynamically dispatched,
114 /// and each item could invoke completely different code for completely different types.
115 /// This is only needed for types that need to be dropped (as determined by `mem::needs_drop`),
116 /// and types that need need to be dropped don't need to be added.
117 items: RefCell<Vec<DynamicArenaItem>>,
118 /// This is the magic `PhantomData` combination to have proper lifetime invariance.
119 ///
120 /// Otherwise the lifetime would be 'variant',
121 /// and borrow checking wouldn't properly enforce the bound for `alloc`.
122 /// This is enforced by the `compile-fail/invalid-drop-counted.rs`
123 marker: PhantomData<*mut &'a ()>,
124 send: PhantomData<S>,
125}
126impl DynamicArena<'static, NonSend> {
127 /// Create a new dynamic arena whose allocated items must outlive the `'static` lifetime,
128 /// and whose items aren't required to be `Send`.
129 ///
130 /// Usually this is what you want,
131 /// since it's only a bound for the allocated items.
132 #[inline]
133 pub fn new() -> Self {
134 DynamicArena::new_bounded()
135 }
136}
137impl<'a, S> DynamicArena<'a, S> {
138 /// Create an arena with pre-allocated capacity for the specified number of items
139 /// and bytes.
140 ///
141 /// NOTE: The "item" capacity excludes `Copy` references that
142 /// don't need to be dropped.
143 pub fn with_capacity(item_capacity: usize, byte_capacity: usize) -> Self {
144 DynamicArena {
145 handle: Bump::with_capacity(byte_capacity),
146 items: RefCell::new(Vec::with_capacity(item_capacity)),
147 marker: PhantomData,
148 send: PhantomData,
149 }
150 }
151 /// Allocate the specified value in this arena,
152 /// returning a reference which will be valid for the lifetime of the entire arena.
153 ///
154 /// The bound on the item requires that `T: Copy`
155 /// to ensure there's no drop function that needs to be invoked.
156 #[inline]
157 #[allow(clippy::mut_from_ref)]
158 pub fn alloc_copy<T: Copy + Send>(&self, value: T) -> &mut T {
159 unsafe { self.alloc_unchecked(value) }
160 }
161 /// Allocate the specified value in this arena,
162 /// without calling its `Drop` function.
163 ///
164 /// Since this doesn't call the 'Drop' impleentation,
165 /// this function leaks the underlying memory.
166 ///
167 /// ## Safety
168 /// Technically, this function is safe to use.
169 /// However, it leaks memory unconditionally (without calling Drop).
170 #[inline]
171 #[allow(clippy::mut_from_ref)]
172 pub unsafe fn alloc_unchecked<T>(&self, value: T) -> &mut T {
173 let ptr = self.alloc_layout(Layout::new::<T>()).as_ptr().cast::<T>();
174 ptr.write(value);
175 &mut *ptr
176 }
177 /// Allocate space for an object with the specified layout
178 ///
179 /// The returned pointer points at uninitialized memory
180 ///
181 /// ## Safety
182 /// Technically, only the use of the memory is unsafe.
183 ///
184 /// It would theoretically be possible to mark this function safe,
185 /// just like [Bump::alloc_layout].
186 #[inline]
187 pub unsafe fn alloc_layout(&self, layout: Layout) -> NonNull<u8> {
188 self.handle.alloc_layout(layout)
189 }
190 /// Dynamically drop the specified value,
191 /// invoking the drop function when the arena is dropped.
192 ///
193 /// ## Safety
194 /// This assumes it's safe to drop the value at the same time the arena is dropped.
195 /// Not only are you assuming that [ptr::drop_in_place] would be safe,
196 /// you're also assuming that the drop function won't reference any dangling pointers,
197 /// and that [dropchk](https://doc.rust-lang.org/nomicon/dropck.html) would be successful.
198 ///
199 /// Normally these invariants are statically checked by the `alloc` method,
200 /// which ensures that the memory is owned and all pointers
201 /// would be valid for the lifetime of the entire arena.
202 #[inline]
203 pub unsafe fn dynamic_drop<T>(&self, value: *mut T) {
204 if mem::needs_drop::<T>() {
205 self.items.borrow_mut().push(DynamicArenaItem {
206 drop: mem::transmute::<unsafe fn(*mut T), unsafe fn(*mut c_void)>(
207 ptr::drop_in_place::<T>,
208 ),
209 value: value as *mut c_void,
210 })
211 }
212 }
213 /// Retrieve the underlying [bump allocator](bumpalo::Bump) for this arena
214 #[inline]
215 pub fn as_bumpalo(&self) -> &'_ bumpalo::Bump {
216 &self.handle
217 }
218}
219impl<'a> DynamicArena<'a, Sendable> {
220 /// Create a new empty arena, bounded by the inferred lifetime for this type `'a`
221 ///
222 /// Since this arena has been marked `Sendable`,
223 /// all items in the arena need to implement `Send`.
224 pub fn new_send() -> Self {
225 DynamicArena {
226 handle: Bump::new(),
227 items: RefCell::new(Vec::new()),
228 marker: PhantomData,
229 send: PhantomData,
230 }
231 }
232 /// Allocate the specified value in this arena,
233 /// returning a reference which will be valid for the lifetime of the entire arena.
234 ///
235 /// The bound on this item requires that `T: 'a`
236 /// to ensure the drop function is safe to invoke.
237 /// Additionally, since the arena is `Sendable`,
238 /// the bound on the item also requires that `T: Send`.
239 #[inline]
240 #[allow(clippy::mut_from_ref)]
241 pub fn alloc<T: Send + 'a>(&self, value: T) -> &mut T {
242 unsafe {
243 let target = self.alloc_unchecked(value);
244 self.dynamic_drop(target);
245 target
246 }
247 }
248}
249impl<'a> DynamicArena<'a, NonSend> {
250 /// Create a new empty arena, bounded by the inferred lifetime for this type `'a`
251 ///
252 /// Since this arena has been marked `NonSend`,
253 /// the items in the arena don't necessarily need to implement `Send`.
254 pub fn new_bounded() -> Self {
255 DynamicArena {
256 handle: Bump::new(),
257 items: RefCell::new(Vec::new()),
258 marker: PhantomData,
259 send: PhantomData,
260 }
261 }
262 /// Allocate the specified value in this arena,
263 /// returning a reference which will be valid for the lifetime of the entire arena.
264 ///
265 /// The bound on this item requires that `T: 'a`
266 /// to ensure the drop function is safe to invoke.
267 #[inline]
268 #[allow(clippy::mut_from_ref)]
269 pub fn alloc<T: 'a>(&self, value: T) -> &mut T {
270 unsafe {
271 let target = self.alloc_unchecked(value);
272 self.dynamic_drop(target);
273 target
274 }
275 }
276}
277impl<'a, S: SendAbility> Default for DynamicArena<'a, S> {
278 #[inline]
279 fn default() -> Self {
280 S::create_arena()
281 }
282}
283unsafe impl<'a, S: SendAbility + Send> Send for DynamicArena<'a, S> {}
284impl<'a, S> Drop for DynamicArena<'a, S> {
285 #[inline]
286 fn drop(&mut self) {
287 // Items must be dropped before the arena
288 self.items.get_mut().clear();
289 }
290}
291
292#[cfg(test)]
293mod test {
294 use super::*;
295 use std::cell::Cell;
296
297 const EXPECTED_DROP_COUNT: u32 = 4787;
298 const EXPECTED_DEPTHS: &[u32] = &[5, 27, 43];
299
300 struct DropCounted<'a>(&'a Cell<u32>);
301 impl<'a> Drop for DropCounted<'a> {
302 fn drop(&mut self) {
303 let old_count = self.0.get();
304 self.0.set(old_count + 1);
305 }
306 }
307 #[derive(Copy, Clone)]
308 pub struct SelfReferential<'a>(u32, Option<&'a SelfReferential<'a>>);
309 impl<'a> SelfReferential<'a> {
310 #[inline]
311 pub fn with_depth(arena: &'a DynamicArena, depth: u32) -> &'a Self {
312 arena.alloc_copy(match depth {
313 0 => SelfReferential(depth, None),
314 _ => SelfReferential(depth, Some(SelfReferential::with_depth(arena, depth - 1))),
315 })
316 }
317 #[inline]
318 pub fn depth(&self) -> u32 {
319 match self.1 {
320 Some(inner) => inner.depth() + 1,
321 None => 0,
322 }
323 }
324 }
325 #[test]
326 fn test_send() {
327 let arena = DynamicArena::new_send();
328 verify_copyable(do_copyable(&arena));
329 ::std::thread::spawn(move || {
330 verify_copyable(do_copyable(&arena));
331 });
332 }
333
334 #[test]
335 fn copyable() {
336 let arena = DynamicArena::new();
337 for _ in 0..5 {
338 verify_copyable(do_copyable(&arena));
339 }
340 }
341 #[test]
342 fn self_referential() {
343 let arena = DynamicArena::new();
344 for _ in 0..5 {
345 verify_self_referential(do_self_referential(&arena));
346 }
347 }
348 #[test]
349 fn drop_counted() {
350 let cell = Box::new(Cell::new(0));
351 let arena = DynamicArena::new_bounded();
352 {
353 do_drop_counted(&arena, &cell);
354 assert_eq!(cell.get(), 0);
355 }
356 drop(arena);
357 assert_eq!(cell.get(), EXPECTED_DROP_COUNT);
358 }
359 #[test]
360 fn mixed() {
361 let cell = Cell::new(0);
362 let arena = DynamicArena::new_bounded();
363 {
364 do_drop_counted(&arena, &cell);
365 for _ in 0..5 {
366 verify_copyable(do_copyable(&arena));
367 verify_self_referential(do_self_referential(&arena));
368 }
369 assert_eq!(cell.get(), 0);
370 }
371 drop(arena);
372 assert_eq!(cell.get(), EXPECTED_DROP_COUNT);
373 }
374 fn do_copyable<'a, S>(arena: &'a DynamicArena<S>) -> Vec<&'a u32> {
375 let mut results = Vec::new();
376 for i in 0..10 {
377 results.push(&*arena.alloc_copy(i * 3));
378 }
379 results
380 }
381 fn verify_copyable(results: Vec<&u32>) {
382 for (actual, expected) in results.iter().zip(0..10) {
383 assert_eq!(**actual, expected * 3);
384 }
385 }
386 fn do_drop_counted<'a, 'd: 'a>(arena: &'a DynamicArena<'d>, counter: &'d Cell<u32>) {
387 for _ in 0..EXPECTED_DROP_COUNT {
388 arena.alloc(DropCounted(counter));
389 }
390 }
391 fn do_self_referential<'a>(arena: &'a DynamicArena) -> Vec<&'a SelfReferential<'a>> {
392 let mut result = Vec::new();
393 for &depth in EXPECTED_DEPTHS {
394 result.push(SelfReferential::with_depth(arena, depth));
395 }
396 result
397 }
398 fn verify_self_referential<'a>(results: Vec<&'a SelfReferential<'a>>) {
399 for (&actual, &depth) in results.iter().zip(EXPECTED_DEPTHS.iter()) {
400 assert_eq!(actual.depth(), depth);
401 }
402 }
403}