futex 0.1.3

Linux futex-based lock implementations.
Documentation
use libc::{c_int, syscall, timespec};
use std::{ptr, io, i32};
use integer_atomics::{AtomicI32, AtomicU32};

const FUTEX_WAIT: c_int = 0;
const FUTEX_WAKE: c_int = 1;
const FUTEX_WAIT_BITSET: c_int = 9;
const FUTEX_WAKE_BITSET: c_int = 10;

#[inline(always)]
unsafe fn do_futex(uaddr: *mut c_int, futex_op: c_int, val: c_int, timeout: *const timespec, uaddr2: *mut c_int, val3: c_int) -> c_int {
    syscall(202/*SYS_futex*/, uaddr, futex_op, val, timeout, uaddr2, val3) as i32
}

#[inline(never)]
pub fn futex_wait(futex: &AtomicI32, val: i32) -> io::Result<()> {
    let ret = unsafe { do_futex(futex as *const _ as *mut i32,
                                FUTEX_WAIT,
                                val,
                                ptr::null(),
                                ptr::null_mut(),
                                0) };
    match ret {
        0 => Ok(()),
        -1 => Err(io::Error::last_os_error()),
        _ => unreachable!(),
    }
}

#[inline(never)]
pub fn futex_wake(futex: &AtomicI32, count: i32) -> io::Result<i32> {
    let ret = unsafe { do_futex(futex as *const _ as *mut i32,
                                FUTEX_WAKE,
                                count,
                                ptr::null(),
                                ptr::null_mut(),
                                0) };
    if ret == -1 {
        Err(io::Error::last_os_error())
    } else {
        Ok(ret)
    }
}

#[inline(never)]
pub fn futex_wait_bitset(futex: &AtomicU32, val: u32, mask: i32) {
    assert!(mask != 0);
    let ret = unsafe { do_futex(futex as *const _ as *mut i32,
                                FUTEX_WAIT_BITSET,
                                val as i32,
                                ptr::null(),
                                ptr::null_mut(),
                                mask) };

    if ret == -1 {
        match io::Error::last_os_error().kind() {
            io::ErrorKind::WouldBlock => (),
            io::ErrorKind::Interrupted => (),
            _ => unreachable!(),
        }
    } else if ret != 0 {
        unreachable!();
    }
}

#[inline(never)]
pub fn futex_wake_bitset(futex: &AtomicU32, count: u32, mask: i32) -> i32 {
    assert!(mask != 0);
    let ret = unsafe { do_futex(futex as *const _ as *mut i32,
                                FUTEX_WAKE_BITSET,
                                count as i32,
                                ptr::null(),
                                ptr::null_mut(),
                                mask) };
    if ret == -1 {
        unreachable!();
    }

    ret
}