use prelude::*;
use core::ptr;
use core::convert::TryInto;
use shim::{syscalls, config};
use {sync, fail};
static BRK_MUTEX: Mutex<BrkState> = Mutex::new(BrkState {
current_brk: None,
});
struct BrkState {
current_brk: Option<Pointer<u8>>,
}
pub struct BrkLock {
state: sync::MutexGuard<'static, BrkState>,
}
impl BrkLock {
unsafe fn sbrk(&mut self, size: isize) -> Result<Pointer<u8>, ()> {
log!(NOTE, "Incrementing the program break by {} bytes.", size);
let expected_brk = self.current_brk().offset(size);
let old_brk = Pointer::new(syscalls::brk(*expected_brk as *const u8) as *mut u8);
log!(INTERNAL, "Program break set.");
if expected_brk == old_brk {
self.state.current_brk = Some(expected_brk.clone());
Ok(old_brk)
} else {
Err(())
}
}
#[allow(cast_possible_wrap)]
pub fn release(&mut self, block: Block) -> Result<(), Block> {
if self.current_brk() == Pointer::from(block.empty_right()) {
log!(DEBUG, "Releasing {:?} to the OS.", block);
let res = unsafe {
self.sbrk(-(block.size() as isize))
};
debug_assert!(res.is_ok(), "Failed to set the program break back.");
Ok(())
} else {
log!(DEBUG, "Unable to release {:?} to the OS.", block);
Err(block)
}
}
fn current_brk(&mut self) -> Pointer<u8> {
if let Some(ref cur) = self.state.current_brk {
let res = cur.clone();
debug_assert!(res == current_brk(), "The cached program break is out of sync with the \
actual program break. Are you interfering with BRK? If so, prefer the \
provided 'sbrk' instead, then.");
return res;
}
let cur = current_brk();
self.state.current_brk = Some(cur.clone());
cur
}
pub fn canonical_brk(&mut self, size: usize, align: usize) -> (Block, Block, Block) {
let brk_size = size + config::extra_brk(size) + align;
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();
let (res, excessive) = rest.split(size);
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)
}
}
pub fn lock() -> BrkLock {
BrkLock {
state: BRK_MUTEX.lock(),
}
}
pub unsafe extern fn sbrk(size: isize) -> *mut u8 {
*lock().sbrk(size).unwrap_or_else(|()| Pointer::new(!0 as *mut u8))
}
fn current_brk() -> Pointer<u8> {
unsafe {
Pointer::new(syscalls::brk(ptr::null()) as *mut u8)
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_ordered() {
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);
}
}
}