liblightning 0.0.2

High-performance general-purpose stackful coroutine library.
Documentation
use std::ptr::null_mut;
use libc;

lazy_static! {
    pub static ref PAGE_SIZE: usize = {
        unsafe {
            libc::sysconf(libc::_SC_PAGESIZE) as usize
        }
    };
}

fn round_up_stack_size(size: usize) -> usize {
    let page_size: usize = *PAGE_SIZE;

    let rem = size % page_size;
    if rem > 0 {
        size - rem + page_size
    } else {
        size
    }
}

pub unsafe fn setup_stack_guard_page(stack: *mut [u8]) {
    let stack = &mut *stack;
    let ret = libc::mprotect(
        &mut stack[0] as *mut u8 as *mut libc::c_void,
        *PAGE_SIZE,
        libc::PROT_NONE
    );
    if ret != 0 {
        panic!("mprotect failed");
    }
}

pub fn setup_stack(size: usize) -> *mut [u8] {
    if size == 0 {
        panic!("stack size must be greater than zero");
    }

    let size = round_up_stack_size(size);

    let stack = unsafe { libc::mmap(
        null_mut(),
        size,
        libc::PROT_READ | libc::PROT_WRITE,
        libc::MAP_PRIVATE | libc::MAP_ANONYMOUS,
        -1,
        0
    ) as *mut u8 };
    if stack.is_null() {
        panic!("mmap failed");
    }

    unsafe {
        ::std::slice::from_raw_parts_mut(stack, size) as *mut [u8]
    }
}

pub unsafe fn free_stack(stack: *mut [u8]) {
    let stack = &mut *stack;
    let ptr = &mut stack[0] as *mut u8;
    let size = stack.len();
    let ret = libc::munmap(ptr as *mut libc::c_void, size);
    if ret != 0 {
        panic!("munmap failed");
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn stack_size_should_be_rounded_up() {
        let page_size: usize = *PAGE_SIZE;
        assert_eq!(round_up_stack_size(123), page_size);
        assert_eq!(round_up_stack_size(page_size), page_size);
        assert_eq!(round_up_stack_size(page_size + 1), page_size * 2);
        assert_eq!(round_up_stack_size(page_size * 2 - 1), page_size * 2);
        assert_eq!(round_up_stack_size(page_size * 2), page_size * 2);
        assert_eq!(round_up_stack_size(page_size * 2 + 1), page_size * 3);
    }

    #[test]
    fn stack_should_be_writable() {
        const LEN: usize = 20000;

        let stack = setup_stack(LEN);
        for i in 0..LEN {
            unsafe {
                (&mut *stack)[i] = 42;
            }
        }
        unsafe {
            free_stack(stack);
        }
    }

    #[test]
    fn setup_stack_guard_page_should_succeed() {
        let stack = setup_stack(*PAGE_SIZE);
        unsafe {
            setup_stack_guard_page(stack);
            free_stack(stack);
        }
    }
}