]> git.lizzy.rs Git - rust.git/blob - src/libarena/lib.rs
Rework Arena structure
[rust.git] / src / libarena / lib.rs
1 // Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 //! The arena, a fast but limited type of allocator.
12 //!
13 //! Arenas are a type of allocator that destroy the objects within, all at
14 //! once, once the arena itself is destroyed. They do not support deallocation
15 //! of individual objects while the arena itself is still alive. The benefit
16 //! of an arena is very fast allocation; just a pointer bump.
17 //!
18 //! This crate has two arenas implemented: `TypedArena`, which is a simpler
19 //! arena but can only hold objects of a single type, and `Arena`, which is a
20 //! more complex, slower arena which can hold objects of any type.
21
22 #![crate_name = "arena"]
23 #![unstable(feature = "rustc_private", issue = "27812")]
24 #![crate_type = "rlib"]
25 #![crate_type = "dylib"]
26 #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
27        html_favicon_url = "https://doc.rust-lang.org/favicon.ico",
28        html_root_url = "https://doc.rust-lang.org/nightly/",
29        test(no_crate_inject, attr(deny(warnings))))]
30
31 #![feature(alloc)]
32 #![feature(core_intrinsics)]
33 #![feature(drop_in_place)]
34 #![feature(heap_api)]
35 #![feature(raw)]
36 #![feature(heap_api)]
37 #![feature(staged_api)]
38 #![feature(dropck_parametricity)]
39 #![cfg_attr(test, feature(test))]
40
41 extern crate alloc;
42
43 use std::cell::{Cell, RefCell};
44 use std::cmp;
45 use std::intrinsics;
46 use std::marker::{PhantomData, Send};
47 use std::mem;
48 use std::ptr;
49
50 use alloc::heap;
51 use alloc::raw_vec::RawVec;
52
53 // The way arena uses arrays is really deeply awful. The arrays are
54 // allocated, and have capacities reserved, but the fill for the array
55 // will always stay at 0.
56 struct Chunk {
57     data: RawVec<u8>,
58     fill: Cell<usize>,
59     is_copy: Cell<bool>,
60 }
61
62 impl Chunk {
63     fn new(size: usize, is_copy: bool) -> Chunk {
64         Chunk {
65             data: RawVec::with_capacity(size),
66             fill: Cell::new(0),
67             is_copy: Cell::new(is_copy),
68         }
69     }
70
71     fn capacity(&self) -> usize {
72         self.data.cap()
73     }
74
75     unsafe fn as_ptr(&self) -> *const u8 {
76         self.data.ptr()
77     }
78 }
79
80 /// A slower reflection-based arena that can allocate objects of any type.
81 ///
82 /// This arena uses `Vec<u8>` as a backing store to allocate objects from. For
83 /// each allocated object, the arena stores a pointer to the type descriptor
84 /// followed by the object (potentially with alignment padding after each
85 /// element). When the arena is destroyed, it iterates through all of its
86 /// chunks, and uses the tydesc information to trace through the objects,
87 /// calling the destructors on them. One subtle point that needs to be
88 /// addressed is how to handle panics while running the user provided
89 /// initializer function. It is important to not run the destructor on
90 /// uninitialized objects, but how to detect them is somewhat subtle. Since
91 /// `alloc()` can be invoked recursively, it is not sufficient to simply exclude
92 /// the most recent object. To solve this without requiring extra space, we
93 /// use the low order bit of the tydesc pointer to encode whether the object
94 /// it describes has been fully initialized.
95 ///
96 /// As an optimization, objects with destructors are stored in different chunks
97 /// than objects without destructors. This reduces overhead when initializing
98 /// plain-old-data (`Copy` types) and means we don't need to waste time running
99 /// their destructors.
100 pub struct Arena<'longer_than_self> {
101     // The head is separated out from the list as a unbenchmarked
102     // microoptimization, to avoid needing to case on the list to access the
103     // head.
104     head: RefCell<Chunk>,
105     copy_head: RefCell<Chunk>,
106     chunks: RefCell<Vec<Chunk>>,
107     _marker: PhantomData<*mut &'longer_than_self ()>,
108 }
109
110 impl<'a> Arena<'a> {
111     /// Allocates a new Arena with 32 bytes preallocated.
112     pub fn new() -> Arena<'a> {
113         Arena::new_with_size(32)
114     }
115
116     /// Allocates a new Arena with `initial_size` bytes preallocated.
117     pub fn new_with_size(initial_size: usize) -> Arena<'a> {
118         Arena {
119             head: RefCell::new(Chunk::new(initial_size, false)),
120             copy_head: RefCell::new(Chunk::new(initial_size, true)),
121             chunks: RefCell::new(Vec::new()),
122             _marker: PhantomData,
123         }
124     }
125 }
126
127 impl<'longer_than_self> Drop for Arena<'longer_than_self> {
128     fn drop(&mut self) {
129         unsafe {
130             destroy_chunk(&*self.head.borrow());
131             for chunk in self.chunks.borrow().iter() {
132                 if !chunk.is_copy.get() {
133                     destroy_chunk(chunk);
134                 }
135             }
136         }
137     }
138 }
139
140 #[inline]
141 fn round_up(base: usize, align: usize) -> usize {
142     (base.checked_add(align - 1)).unwrap() & !(align - 1)
143 }
144
145 // Walk down a chunk, running the destructors for any objects stored
146 // in it.
147 unsafe fn destroy_chunk(chunk: &Chunk) {
148     let mut idx = 0;
149     let buf = chunk.as_ptr();
150     let fill = chunk.fill.get();
151
152     while idx < fill {
153         let tydesc_data = buf.offset(idx as isize) as *const usize;
154         let (tydesc, is_done) = un_bitpack_tydesc_ptr(*tydesc_data);
155         let (size, align) = ((*tydesc).size, (*tydesc).align);
156
157         let after_tydesc = idx + mem::size_of::<*const TyDesc>();
158
159         let start = round_up(after_tydesc, align);
160
161         if is_done {
162             ((*tydesc).drop_glue)(buf.offset(start as isize) as *const i8);
163         }
164
165         // Find where the next tydesc lives
166         idx = round_up(start + size, mem::align_of::<*const TyDesc>());
167     }
168 }
169
170 // We encode whether the object a tydesc describes has been
171 // initialized in the arena in the low bit of the tydesc pointer. This
172 // is necessary in order to properly do cleanup if a panic occurs
173 // during an initializer.
174 #[inline]
175 fn bitpack_tydesc_ptr(p: *const TyDesc, is_done: bool) -> usize {
176     p as usize | (is_done as usize)
177 }
178 #[inline]
179 fn un_bitpack_tydesc_ptr(p: usize) -> (*const TyDesc, bool) {
180     ((p & !1) as *const TyDesc, p & 1 == 1)
181 }
182
183 // HACK(eddyb) TyDesc replacement using a trait object vtable.
184 // This could be replaced in the future with a custom DST layout,
185 // or `&'static (drop_glue, size, align)` created by a `const fn`.
186 struct TyDesc {
187     drop_glue: fn(*const i8),
188     size: usize,
189     align: usize,
190 }
191
192 trait AllTypes {
193     fn dummy(&self) {}
194 }
195
196 impl<T: ?Sized> AllTypes for T {}
197
198 unsafe fn get_tydesc<T>() -> *const TyDesc {
199     use std::raw::TraitObject;
200
201     let ptr = &*(1 as *const T);
202
203     // Can use any trait that is implemented for all types.
204     let obj = mem::transmute::<&AllTypes, TraitObject>(ptr);
205     obj.vtable as *const TyDesc
206 }
207
208 impl<'longer_than_self> Arena<'longer_than_self> {
209     #[inline]
210     fn chunk_size(&self) -> usize {
211         self.copy_head.borrow().capacity()
212     }
213
214     // Functions for the POD part of the arena
215     #[cold]
216     fn alloc_copy_grow(&self, n_bytes: usize, align: usize) -> *const u8 {
217         // Allocate a new chunk.
218         let new_min_chunk_size = cmp::max(n_bytes, self.chunk_size());
219         let new_chunk = Chunk::new((new_min_chunk_size + 1).next_power_of_two(), true);
220         let mut copy_head = self.copy_head.borrow_mut();
221         let old_chunk = mem::replace(&mut *copy_head, new_chunk);
222         self.chunks.borrow_mut().push(old_chunk);
223
224         self.alloc_copy_inner(n_bytes, align)
225     }
226
227     #[inline]
228     fn alloc_copy_inner(&self, n_bytes: usize, align: usize) -> *const u8 {
229         let start = round_up(self.copy_head.borrow().fill.get(), align);
230         let chunk_size = self.chunk_size();
231
232         let end = start + n_bytes;
233         if end > chunk_size {
234             if !self.copy_head.borrow_mut().data.reserve_in_place(start, n_bytes) {
235                 return self.alloc_copy_grow(n_bytes, align);
236             }
237         }
238
239         let copy_head = self.copy_head.borrow();
240         copy_head.fill.set(end);
241
242         unsafe { copy_head.as_ptr().offset(start as isize) }
243     }
244
245     #[inline]
246     fn alloc_copy<T, F>(&self, op: F) -> &mut T
247         where F: FnOnce() -> T
248     {
249         unsafe {
250             let ptr = self.alloc_copy_inner(mem::size_of::<T>(), mem::align_of::<T>());
251             let ptr = ptr as *mut T;
252             ptr::write(&mut (*ptr), op());
253             &mut *ptr
254         }
255     }
256
257     // Functions for the non-POD part of the arena
258     fn alloc_noncopy_grow(&self, n_bytes: usize, align: usize) -> (*const u8, *const u8) {
259         // Allocate a new chunk.
260         let new_min_chunk_size = cmp::max(n_bytes, self.chunk_size());
261         let new_chunk = Chunk::new((new_min_chunk_size + 1).next_power_of_two(), false);
262         let mut head = self.head.borrow_mut();
263         let old_chunk = mem::replace(&mut *head, new_chunk);
264         self.chunks.borrow_mut().push(old_chunk);
265
266         self.alloc_noncopy_inner(n_bytes, align)
267     }
268
269     #[inline]
270     fn alloc_noncopy_inner(&self, n_bytes: usize, align: usize) -> (*const u8, *const u8) {
271         // Be careful to not maintain any `head` borrows active, because
272         // `alloc_noncopy_grow` borrows it mutably.
273         let (start, end, tydesc_start, head_capacity) = {
274             let head = self.head.borrow();
275             let fill = head.fill.get();
276
277             let tydesc_start = fill;
278             let after_tydesc = fill + mem::size_of::<*const TyDesc>();
279             let start = round_up(after_tydesc, align);
280             let end = start + n_bytes;
281
282             (start, end, tydesc_start, head.capacity())
283         };
284
285         if end > head_capacity {
286             return self.alloc_noncopy_grow(n_bytes, align);
287         }
288
289         let head = self.head.borrow();
290         head.fill.set(round_up(end, mem::align_of::<*const TyDesc>()));
291
292         unsafe {
293             let buf = head.as_ptr();
294             (buf.offset(tydesc_start as isize),
295              buf.offset(start as isize))
296         }
297     }
298
299     #[inline]
300     fn alloc_noncopy<T, F>(&self, op: F) -> &mut T
301         where F: FnOnce() -> T
302     {
303         unsafe {
304             let tydesc = get_tydesc::<T>();
305             let (ty_ptr, ptr) = self.alloc_noncopy_inner(mem::size_of::<T>(), mem::align_of::<T>());
306             let ty_ptr = ty_ptr as *mut usize;
307             let ptr = ptr as *mut T;
308             // Write in our tydesc along with a bit indicating that it
309             // has *not* been initialized yet.
310             *ty_ptr = bitpack_tydesc_ptr(tydesc, false);
311             // Actually initialize it
312             ptr::write(&mut (*ptr), op());
313             // Now that we are done, update the tydesc to indicate that
314             // the object is there.
315             *ty_ptr = bitpack_tydesc_ptr(tydesc, true);
316
317             &mut *ptr
318         }
319     }
320
321     /// Allocates a new item in the arena, using `op` to initialize the value,
322     /// and returns a reference to it.
323     #[inline]
324     pub fn alloc<T: 'longer_than_self, F>(&self, op: F) -> &mut T
325         where F: FnOnce() -> T
326     {
327         unsafe {
328             if intrinsics::needs_drop::<T>() {
329                 self.alloc_noncopy(op)
330             } else {
331                 self.alloc_copy(op)
332             }
333         }
334     }
335 }
336
337 #[test]
338 fn test_arena_destructors() {
339     let arena = Arena::new();
340     for i in 0..10 {
341         // Arena allocate something with drop glue to make sure it
342         // doesn't leak.
343         arena.alloc(|| Rc::new(i));
344         // Allocate something with funny size and alignment, to keep
345         // things interesting.
346         arena.alloc(|| [0u8, 1u8, 2u8]);
347     }
348 }
349
350 #[test]
351 #[should_panic]
352 fn test_arena_destructors_fail() {
353     let arena = Arena::new();
354     // Put some stuff in the arena.
355     for i in 0..10 {
356         // Arena allocate something with drop glue to make sure it
357         // doesn't leak.
358         arena.alloc(|| Rc::new(i));
359         // Allocate something with funny size and alignment, to keep
360         // things interesting.
361         arena.alloc(|| [0u8, 1, 2]);
362     }
363     // Now, panic while allocating
364     arena.alloc::<Rc<i32>, _>(|| {
365         panic!();
366     });
367 }
368
369 /// A faster arena that can hold objects of only one type.
370 pub struct TypedArena<T> {
371     /// A pointer to the next object to be allocated.
372     ptr: Cell<*mut T>,
373
374     /// A pointer to the end of the allocated area. When this pointer is
375     /// reached, a new chunk is allocated.
376     end: Cell<*mut T>,
377
378     /// A vector arena segments.
379     chunks: RefCell<Vec<TypedArenaChunk<T>>>,
380
381     /// Marker indicating that dropping the arena causes its owned
382     /// instances of `T` to be dropped.
383     _own: PhantomData<T>,
384 }
385
386 struct TypedArenaChunk<T> {
387     /// Pointer to the next arena segment.
388     storage: RawVec<T>,
389 }
390
391 impl<T> TypedArenaChunk<T> {
392     #[inline]
393     unsafe fn new(capacity: usize) -> TypedArenaChunk<T> {
394         TypedArenaChunk { storage: RawVec::with_capacity(capacity) }
395     }
396
397     /// Destroys this arena chunk.
398     #[inline]
399     unsafe fn destroy(&mut self, len: usize) {
400         // The branch on needs_drop() is an -O1 performance optimization.
401         // Without the branch, dropping TypedArena<u8> takes linear time.
402         if intrinsics::needs_drop::<T>() {
403             let mut start = self.start();
404             // Destroy all allocated objects.
405             for _ in 0..len {
406                 ptr::drop_in_place(start);
407                 start = start.offset(1);
408             }
409         }
410     }
411
412     // Returns a pointer to the first allocated object.
413     #[inline]
414     fn start(&self) -> *mut T {
415         self.storage.ptr()
416     }
417
418     // Returns a pointer to the end of the allocated space.
419     #[inline]
420     fn end(&self) -> *mut T {
421         unsafe {
422             if mem::size_of::<T>() == 0 {
423                 // A pointer as large as possible for zero-sized elements.
424                 !0 as *mut T
425             } else {
426                 self.start().offset(self.storage.cap() as isize)
427             }
428         }
429     }
430 }
431
432 const PAGE: usize = 4096;
433
434 impl<T> TypedArena<T> {
435     /// Creates a new `TypedArena` with preallocated space for many objects.
436     #[inline]
437     pub fn new() -> TypedArena<T> {
438         // Reserve at least one page.
439         let elem_size = cmp::max(1, mem::size_of::<T>());
440         TypedArena::with_capacity(PAGE / elem_size)
441     }
442
443     /// Creates a new `TypedArena` with preallocated space for the given number of
444     /// objects.
445     #[inline]
446     pub fn with_capacity(capacity: usize) -> TypedArena<T> {
447         unsafe {
448             let chunk = TypedArenaChunk::<T>::new(cmp::max(1, capacity));
449             TypedArena {
450                 ptr: Cell::new(chunk.start()),
451                 end: Cell::new(chunk.end()),
452                 chunks: RefCell::new(vec![chunk]),
453                 _own: PhantomData,
454             }
455         }
456     }
457
458     /// Allocates an object in the `TypedArena`, returning a reference to it.
459     #[inline]
460     pub fn alloc(&self, object: T) -> &mut T {
461         if self.ptr == self.end {
462             self.grow()
463         }
464
465         unsafe {
466             if mem::size_of::<T>() == 0 {
467                 self.ptr.set(intrinsics::arith_offset(self.ptr.get() as *mut u8, 1) as *mut T);
468                 let ptr = heap::EMPTY as *mut T;
469                 // Don't drop the object. This `write` is equivalent to `forget`.
470                 ptr::write(ptr, object);
471                 &mut *ptr
472             } else {
473                 let ptr = self.ptr.get();
474                 // Advance the pointer.
475                 self.ptr.set(self.ptr.get().offset(1));
476                 // Write into uninitialized memory.
477                 ptr::write(ptr, object);
478                 &mut *ptr
479             }
480         }
481     }
482
483     /// Grows the arena.
484     #[inline(never)]
485     #[cold]
486     fn grow(&self) {
487         unsafe {
488             let mut chunks = self.chunks.borrow_mut();
489             let prev_capacity = chunks.last().unwrap().storage.cap();
490             let new_capacity = prev_capacity.checked_mul(2).unwrap();
491             if chunks.last_mut().unwrap().storage.double_in_place() {
492                 self.end.set(chunks.last().unwrap().end());
493             } else {
494                 let chunk = TypedArenaChunk::<T>::new(new_capacity);
495                 self.ptr.set(chunk.start());
496                 self.end.set(chunk.end());
497                 chunks.push(chunk);
498             }
499         }
500     }
501 }
502
503 impl<T> Drop for TypedArena<T> {
504     #[unsafe_destructor_blind_to_params]
505     fn drop(&mut self) {
506         unsafe {
507             // Determine how much was filled.
508             let mut chunks_borrow = self.chunks.borrow_mut();
509             let mut last_chunk = chunks_borrow.pop().unwrap();
510             let start = last_chunk.start() as usize;
511             let end = self.ptr.get() as usize;
512             let diff = if mem::size_of::<T>() == 0 {
513                 // Avoid division by zero.
514                 end - start
515             } else {
516                 (end - start) / mem::size_of::<T>()
517             };
518
519             // Pass that to the `destroy` method.
520             last_chunk.destroy(diff);
521             // Destroy this chunk.
522             let _: RawVec<T> = mem::transmute(last_chunk);
523
524             for chunk in chunks_borrow.iter_mut() {
525                 let cap = chunk.storage.cap();
526                 chunk.destroy(cap);
527             }
528         }
529     }
530 }
531
532 unsafe impl<T: Send> Send for TypedArena<T> {}
533
534 #[cfg(test)]
535 mod tests {
536     extern crate test;
537     use self::test::Bencher;
538     use super::{Arena, TypedArena};
539
540     #[allow(dead_code)]
541     struct Point {
542         x: i32,
543         y: i32,
544         z: i32,
545     }
546
547     #[test]
548     fn test_arena_alloc_nested() {
549         struct Inner {
550             value: u8,
551         }
552         struct Outer<'a> {
553             inner: &'a Inner,
554         }
555         enum EI<'e> {
556             I(Inner),
557             O(Outer<'e>),
558         }
559
560         struct Wrap<'a>(TypedArena<EI<'a>>);
561
562         impl<'a> Wrap<'a> {
563             fn alloc_inner<F: Fn() -> Inner>(&self, f: F) -> &Inner {
564                 let r: &EI = self.0.alloc(EI::I(f()));
565                 if let &EI::I(ref i) = r {
566                     i
567                 } else {
568                     panic!("mismatch");
569                 }
570             }
571             fn alloc_outer<F: Fn() -> Outer<'a>>(&self, f: F) -> &Outer {
572                 let r: &EI = self.0.alloc(EI::O(f()));
573                 if let &EI::O(ref o) = r {
574                     o
575                 } else {
576                     panic!("mismatch");
577                 }
578             }
579         }
580
581         let arena = Wrap(TypedArena::new());
582
583         let result = arena.alloc_outer(|| {
584             Outer { inner: arena.alloc_inner(|| Inner { value: 10 }) }
585         });
586
587         assert_eq!(result.inner.value, 10);
588     }
589
590     #[test]
591     pub fn test_copy() {
592         let arena = TypedArena::new();
593         for _ in 0..100000 {
594             arena.alloc(Point { x: 1, y: 2, z: 3 });
595         }
596     }
597
598     #[bench]
599     pub fn bench_copy(b: &mut Bencher) {
600         let arena = TypedArena::new();
601         b.iter(|| arena.alloc(Point { x: 1, y: 2, z: 3 }))
602     }
603
604     #[bench]
605     pub fn bench_copy_nonarena(b: &mut Bencher) {
606         b.iter(|| {
607             let _: Box<_> = Box::new(Point {
608                 x: 1,
609                 y: 2,
610                 z: 3
611             });
612         })
613     }
614
615     #[bench]
616     pub fn bench_copy_old_arena(b: &mut Bencher) {
617         let arena = Arena::new();
618         b.iter(|| arena.alloc(|| Point { x: 1, y: 2, z: 3 }))
619     }
620
621     #[allow(dead_code)]
622     struct Noncopy {
623         string: String,
624         array: Vec<i32>,
625     }
626
627     #[test]
628     pub fn test_noncopy() {
629         let arena = TypedArena::new();
630         for _ in 0..100000 {
631             arena.alloc(Noncopy {
632                 string: "hello world".to_string(),
633                 array: vec![1, 2, 3, 4, 5],
634             });
635         }
636     }
637
638     #[bench]
639     pub fn bench_noncopy(b: &mut Bencher) {
640         let arena = TypedArena::new();
641         b.iter(|| {
642             arena.alloc(Noncopy {
643                 string: "hello world".to_string(),
644                 array: vec![1, 2, 3, 4, 5],
645             })
646         })
647     }
648
649     #[bench]
650     pub fn bench_noncopy_nonarena(b: &mut Bencher) {
651         b.iter(|| {
652             let _: Box<_> = Box::new(Noncopy {
653                 string: "hello world".to_string(),
654                 array: vec![1, 2, 3, 4, 5],
655             });
656         })
657     }
658
659     #[bench]
660     pub fn bench_noncopy_old_arena(b: &mut Bencher) {
661         let arena = Arena::new();
662         b.iter(|| {
663             arena.alloc(|| {
664                 Noncopy {
665                     string: "hello world".to_string(),
666                     array: vec![1, 2, 3, 4, 5],
667                 }
668             })
669         })
670     }
671
672     #[test]
673     pub fn test_zero_sized() {
674         let arena = TypedArena::new();
675         for _ in 0..100000 {
676             arena.alloc(());
677         }
678     }
679 }