Browse Source

Add refill algorithm to the bookkeeper in order to keep the arena in shape.

- Rename `provide` to `refill`.
ticki 8 years ago
parent
commit
78ca79c97d
3 changed files with 47 additions and 6 deletions
  1. 25 4
      src/arena.rs
  2. 21 1
      src/bk/pool.rs
  3. 1 1
      src/ptr.rs

+ 25 - 4
src/arena.rs

@@ -9,6 +9,9 @@ use core::{ptr, mem, marker};
 
 use take;
 
+// Derive the length newtype.
+usize_newtype!(pub Length);
+
 /// A linked-list of pointers.
 ///
 /// This is similar to a nodeless linked list. We use this internally to implement arenas.
@@ -86,6 +89,8 @@ pub struct Arena<T> {
     list: PointerList,
     /// Phantom data.
     _phantom: marker::PhantomData<T>,
+    /// The number of blocks currently in the arena.
+    len: Length,
 }
 
 impl<T> Arena<T> {
@@ -112,6 +117,9 @@ impl<T> Arena<T> {
     #[inline]
     pub fn alloc(&mut self, inner: T) -> Result<Jar<T>, ()> {
         if let Some(ptr) = self.list.pop() {
+            // Decrement the length.
+            self.len -= 1;
+
             // Note that this cast is valid due to the correctness of the `free` method (i.e. the
             // pointer is valid for `T`).
             let ptr = ptr.cast();
@@ -138,6 +146,9 @@ impl<T> Arena<T> {
     /// Free a jar to the arena.
     #[inline]
     pub fn free(&mut self, jar: Jar<T>) {
+        // Increment the length.
+        self.len += 1;
+
         unsafe {
             // LAST AUDIT: 2016-08-23 (Ticki).
 
@@ -146,19 +157,29 @@ impl<T> Arena<T> {
         }
     }
 
-    /// Provide this arena with some uninitialized segment.
+    /// Refill this arena with some uninitialized segment.
     ///
     /// This is used to fill the arena with memory from some source by essentially linking each
     /// piece together.
     #[inline]
-    pub fn provide(&mut self, new: Uninit<[T]>) {
+    pub fn refill(&mut self, new: Uninit<[T]>) {
         log!(DEBUG, "Providing {:?} to arena.", new.as_ptr().len());
 
-        for n in 0..new.as_ptr().len() {
+        // Increase the length.
+        let len = new.as_ptr().len();
+        self.len += len;
+
+        for n in 0..len {
             // Push the nth element to the inner pointer list.
             self.list.push(new.as_ptr().clone().cast::<T>().offset(n).cast());
         }
     }
+
+    /// Get the number of blocks currently in the arena.
+    #[inline]
+    pub fn len(&self) -> Length {
+        self.len
+    }
 }
 
 #[cfg(test)]
@@ -170,7 +191,7 @@ mod test {
     /// Helper method to make an artificial arena.
     fn make<T>() -> Arena<T> {
         let mut arena = Arena::new();
-        arena.provide(Block::sbrk(826));
+        arena.refill(Block::sbrk(826));
 
         arena
     }

+ 21 - 1
src/bk/pool.rs

@@ -2,6 +2,7 @@ use core::{cmp, mem};
 
 use arena::Arena;
 use bk::search::{self, Search};
+use ptr;
 use random;
 
 struct Pool {
@@ -10,6 +11,25 @@ struct Pool {
 }
 
 impl Pool {
+    pub fn update(&mut self) {
+        /// The threshold to be reached before a refill happens.
+        const REFILL_THRESHOLD: arena::Length = arena::Length(2);
+        /// The nodes which will be added during a refill.
+        const REFILL_NODES: usize = 64;
+
+        if self.arena.len() < REFILL_THRESHOLD {
+            // The length was below the refill threshold. To avoid infinite recursion (which could
+            // happen as a result of a refill-when-empty policy, due to the refill itself needing
+            // to allocate nodes), we have a threshold which symbolizes the maximum amount of arena
+            // allocation that can happen inbetween the `update` calls.
+
+            // Allocate the block to provide to the arena.
+            let alloc = self.alloc(REFILL_NODES * mem::size_of::<Node>(), mem::align_of::<Node>());
+            // Cast the block to a pointer and hand it over to the arena.
+            self.arena.refill(ptr::Uninit::new(ptr::Pointer::from(alloc).cast()));
+        }
+    }
+
     /// Search the block pool with a particular searcher.
     ///
     /// The outline of the algorithm is this: We start by shortcutting from the top level until we
@@ -26,7 +46,7 @@ impl Pool {
     ///     ------------------> [6] ==> [7] ----------> [9] -----------> NIL
     ///     ----------> [5] --> [6] ==> [7] ----------> [9] --> [10] --> NIL
     ///     --> [1] --> [5] --> [6] --> [7] ==> [8] --> [9] --> [10] --> NIL
-    fn search<S: Search>(&mut self, searcher: S) -> Result<Seek, ()> {
+    pub fn search<S: Search>(&mut self, searcher: S) -> Result<Seek, ()> {
         // We start by an uninitialized value, which we fill out.
         let mut seek = unsafe { mem::uninitialized() };
 

+ 1 - 1
src/ptr.rs

@@ -108,7 +108,7 @@ impl<'a, T> From<&'a mut T> for Pointer<T> {
     }
 }
 
-impl fmt::Debug for Block {
+impl fmt::Debug for Pointer {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         write!(f, "0x{:x}", *self as usize)
     }