|  | @@ -1,10 +1,126 @@
 | 
	
		
			
				|  |  |  //! BRK abstractions.
 | 
	
		
			
				|  |  |  //!
 | 
	
		
			
				|  |  | -//! This module provides safe abstractions over SBRK.
 | 
	
		
			
				|  |  | +//! This module provides safe abstractions over BRK.
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  use prelude::*;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -use core::cmp;
 | 
	
		
			
				|  |  | +use core::{cmp, ptr};
 | 
	
		
			
				|  |  | +use core::convert::TryInto;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +use {sync, sys, fail};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/// The BRK mutex.
 | 
	
		
			
				|  |  | +///
 | 
	
		
			
				|  |  | +/// This is used for avoiding data races in multiple allocator.
 | 
	
		
			
				|  |  | +static BRK_MUTEX: Mutex<BrkState> = Mutex::new(BrkState {
 | 
	
		
			
				|  |  | +    brk_end: None,
 | 
	
		
			
				|  |  | +});
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/// A cache of the BRK state.
 | 
	
		
			
				|  |  | +///
 | 
	
		
			
				|  |  | +/// To avoid keeping asking the OS for information whenever needed, we cache it.
 | 
	
		
			
				|  |  | +struct BrkState {
 | 
	
		
			
				|  |  | +    /// The program break's end
 | 
	
		
			
				|  |  | +    brk_end: Option<Pointer<u8>>,
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/// A BRK lock.
 | 
	
		
			
				|  |  | +pub struct BrkLock {
 | 
	
		
			
				|  |  | +    /// The inner lock.
 | 
	
		
			
				|  |  | +    guard: sync::MutexGuard<'static, BrkState>,
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +impl BrkLock {
 | 
	
		
			
				|  |  | +    /// BRK new space.
 | 
	
		
			
				|  |  | +    ///
 | 
	
		
			
				|  |  | +    /// The first block represents the aligner segment (that is the precursor aligning the middle
 | 
	
		
			
				|  |  | +    /// block to `align`), the second one is the result and is of exactly size `size`. The last
 | 
	
		
			
				|  |  | +    /// block is the excessive space.
 | 
	
		
			
				|  |  | +    ///
 | 
	
		
			
				|  |  | +    /// # Failure
 | 
	
		
			
				|  |  | +    ///
 | 
	
		
			
				|  |  | +    /// This method calls the OOM handler if it is unable to acquire the needed space.
 | 
	
		
			
				|  |  | +    // TODO: This method is possibly unsafe.
 | 
	
		
			
				|  |  | +    pub fn canonical_brk(&mut self, size: usize, align: usize) -> (Block, Block, Block) {
 | 
	
		
			
				|  |  | +        // Calculate the canonical size (extra space is allocated to limit the number of system calls).
 | 
	
		
			
				|  |  | +        let brk_size = canonicalize_space(size) + align;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        // Use SBRK to allocate extra data segment. The alignment is used as precursor for our
 | 
	
		
			
				|  |  | +        // allocated block. This ensures that it is properly memory aligned to the requested value.
 | 
	
		
			
				|  |  | +        // TODO: Audit the casts.
 | 
	
		
			
				|  |  | +        let (alignment_block, rest) = unsafe {
 | 
	
		
			
				|  |  | +            Block::from_raw_parts(
 | 
	
		
			
				|  |  | +                self.sbrk(brk_size.try_into().unwrap()).unwrap_or_else(|()| fail::oom()),
 | 
	
		
			
				|  |  | +                brk_size,
 | 
	
		
			
				|  |  | +            )
 | 
	
		
			
				|  |  | +        }.align(align).unwrap();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        // Split the block to leave the excessive space.
 | 
	
		
			
				|  |  | +        let (res, excessive) = rest.split(size);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        // Make some assertions.
 | 
	
		
			
				|  |  | +        debug_assert!(res.aligned_to(align), "Alignment failed.");
 | 
	
		
			
				|  |  | +        debug_assert!(res.size() + alignment_block.size() + excessive.size() == brk_size, "BRK memory leak.");
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        (alignment_block, res, excessive)
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    /// Extend the program break.
 | 
	
		
			
				|  |  | +    ///
 | 
	
		
			
				|  |  | +    /// # Safety
 | 
	
		
			
				|  |  | +    ///
 | 
	
		
			
				|  |  | +    /// Due to being able shrink the program break, this method is unsafe.
 | 
	
		
			
				|  |  | +    unsafe fn sbrk(&mut self, size: isize) -> Result<Pointer<u8>, ()> {
 | 
	
		
			
				|  |  | +        // Calculate the new program break. To avoid making multiple syscalls, we make use of the
 | 
	
		
			
				|  |  | +        // state cache.
 | 
	
		
			
				|  |  | +        let new_brk = self.guard.brk_end
 | 
	
		
			
				|  |  | +            .clone()
 | 
	
		
			
				|  |  | +            .unwrap_or_else(current_brk)
 | 
	
		
			
				|  |  | +            .offset(size);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        // Break it to me, babe!
 | 
	
		
			
				|  |  | +        let old_brk = Pointer::new(sys::brk(*new_brk as *const u8) as *mut u8);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        if new_brk == old_brk && size != 0 {
 | 
	
		
			
				|  |  | +            // BRK failed. This syscall is rather weird, but whenever it fails (e.g. OOM) it
 | 
	
		
			
				|  |  | +            // returns the old (unchanged) break.
 | 
	
		
			
				|  |  | +            Err(())
 | 
	
		
			
				|  |  | +        } else {
 | 
	
		
			
				|  |  | +            // Update the program break cache.
 | 
	
		
			
				|  |  | +            self.guard.brk_end = Some(old_brk.clone());
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            // Return the old break.
 | 
	
		
			
				|  |  | +            Ok(old_brk)
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/// Lock the BRK lock to allow manipulating the program break.
 | 
	
		
			
				|  |  | +pub fn lock() -> BrkLock {
 | 
	
		
			
				|  |  | +    BrkLock {
 | 
	
		
			
				|  |  | +        guard: BRK_MUTEX.lock(),
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/// `SBRK` symbol which can coexist with the allocator.
 | 
	
		
			
				|  |  | +///
 | 
	
		
			
				|  |  | +/// `SBRK`-ing directly (from the `BRK` syscall or libc) might make the state inconsistent. This
 | 
	
		
			
				|  |  | +/// function makes sure that's not happening.
 | 
	
		
			
				|  |  | +///
 | 
	
		
			
				|  |  | +/// With the exception of being able to coexist, it follows the same rules. Refer to the relevant
 | 
	
		
			
				|  |  | +/// documentation.
 | 
	
		
			
				|  |  | +///
 | 
	
		
			
				|  |  | +/// # Failure
 | 
	
		
			
				|  |  | +///
 | 
	
		
			
				|  |  | +/// On failure the maximum pointer (`!0 as *mut u8`) is returned.
 | 
	
		
			
				|  |  | +pub unsafe extern fn sbrk(size: isize) -> *mut u8 {
 | 
	
		
			
				|  |  | +    *lock().sbrk(size).unwrap_or_else(|()| Pointer::new(!0 as *mut u8))
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/// Get the current program break.
 | 
	
		
			
				|  |  | +fn current_brk() -> Pointer<u8> {
 | 
	
		
			
				|  |  | +    unsafe { Pointer::new(sys::brk(ptr::null()) as *mut u8) }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  /// Canonicalize a BRK request.
 | 
	
		
			
				|  |  |  ///
 | 
	
	
		
			
				|  | @@ -37,38 +153,25 @@ fn canonicalize_space(min: usize) -> usize {
 | 
	
		
			
				|  |  |      res
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -/// BRK new space.
 | 
	
		
			
				|  |  | -///
 | 
	
		
			
				|  |  | -/// The first block represents the aligner segment (that is the precursor aligning the middle
 | 
	
		
			
				|  |  | -/// block to `align`), the second one is the result and is of exactly size `size`. The last
 | 
	
		
			
				|  |  | -/// block is the excessive space.
 | 
	
		
			
				|  |  | -pub fn get(size: usize, align: usize) -> (Block, Block, Block) {
 | 
	
		
			
				|  |  | -    // Calculate the canonical size (extra space is allocated to limit the number of system calls).
 | 
	
		
			
				|  |  | -    let brk_size = canonicalize_space(size) + align;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    // Use SBRK to allocate extra data segment. The alignment is used as precursor for our
 | 
	
		
			
				|  |  | -    // allocated block. This ensures that it is properly memory aligned to the requested value.
 | 
	
		
			
				|  |  | -    let (alignment_block, rest) = Block::brk(brk_size).align(align).unwrap();
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    // Split the block to leave the excessive space.
 | 
	
		
			
				|  |  | -    let (res, excessive) = rest.split(size);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    // Make some assertions.
 | 
	
		
			
				|  |  | -    debug_assert!(res.aligned_to(align), "Alignment failed.");
 | 
	
		
			
				|  |  | -    debug_assert!(res.size() + alignment_block.size() + excessive.size() == brk_size, "BRK memory leak.");
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    (alignment_block, res, excessive)
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |  #[cfg(test)]
 | 
	
		
			
				|  |  |  mod test {
 | 
	
		
			
				|  |  |      use super::*;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      #[test]
 | 
	
		
			
				|  |  |      fn test_ordered() {
 | 
	
		
			
				|  |  | -        let brk = get(20, 1);
 | 
	
		
			
				|  |  | +        let brk = lock().canonical_brk(20, 1);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          assert!(brk.0 <= brk.1);
 | 
	
		
			
				|  |  |          assert!(brk.1 <= brk.2);
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    #[test]
 | 
	
		
			
				|  |  | +    fn test_brk_grow_up() {
 | 
	
		
			
				|  |  | +        unsafe {
 | 
	
		
			
				|  |  | +            let brk1 = lock().sbrk(5).unwrap();
 | 
	
		
			
				|  |  | +            let brk2 = lock().sbrk(100).unwrap();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            assert!(*brk1 < *brk2);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  |  }
 |