use std::i32;
use std::sync::atomic::{AtomicU32, Ordering};
use sys::{futex_wait_bitset, futex_wake_bitset};
pub struct RwFutex2 {
futex: AtomicU32,
}
const M_WRITERS: u32 = 0b11000000000000000000000000000000;
const M_READERS_QUEUED: u32 = 0b00111111110000000000000000000000;
const M_READERS: u32 = 0b00000000001111111111111111111111;
const F_WRITE: u32 = M_WRITERS; const E_WRITERS_LOCK: u32 = 0b01000000000000000000000000000000;
const E_WRITERS_OVER: u32 = 0b11000000000000000000000000000000;
const E_WRITERS_RESV: u32 = 0b10000000000000000000000000000000;
const ONE_READER: u32 = 1;
const ID_READER: i32 = 1;
const ID_WRITER: i32 = 2;
impl RwFutex2 {
pub fn new() -> RwFutex2 {
RwFutex2 {
futex: AtomicU32::new(0),
}
}
pub fn acquire_read(&self) {
loop {
let mut val = self.futex.fetch_add(ONE_READER, Ordering::Acquire);
if val & F_WRITE == 0 {
break;
}
val = self.futex.fetch_add(M_READERS, Ordering::Acquire); val += M_READERS;
if val & F_WRITE == 0 {
continue;
} else if (val & M_READERS == 0) && (val & E_WRITERS_LOCK != 0) {
futex_wake_bitset(&self.futex, 1, ID_WRITER);
}
futex_wait_bitset(&self.futex, val, ID_READER);
}
}
pub fn acquire_write(&self) -> bool {
let mut have_lock = false;
loop {
let mut val = self.futex.fetch_or(E_WRITERS_LOCK, Ordering::Acquire);
if !have_lock && (val & E_WRITERS_LOCK != 0) {
} else if val & M_WRITERS == E_WRITERS_RESV {
return true;
} else if val & M_READERS != 0 {
have_lock = true;
} else {
break;
}
val = self.futex.fetch_or(E_WRITERS_OVER, Ordering::Acquire);
if val & E_WRITERS_LOCK == 0 {
break;
}
futex_wait_bitset(&self.futex, val, ID_WRITER);
}
false
}
pub fn release_read(&self) {
let val = self.futex.fetch_sub(ONE_READER, Ordering::Release);
println!("r{:08x}", val);
if (val & M_READERS == ONE_READER) && (val & E_WRITERS_LOCK != 0) {
let ret = futex_wake_bitset(&self.futex, 1, ID_WRITER);
assert_eq!(ret, 1);
}
}
pub fn release_write(&self, resv: bool) {
let val = self.futex.fetch_and(!E_WRITERS_LOCK, Ordering::Release);
println!("w{:08x}", val);
if (val & M_WRITERS == E_WRITERS_OVER) || resv {
if futex_wake_bitset(&self.futex, 1, ID_WRITER) == 1 {
return;
}
}
let val = self.futex.fetch_and(M_READERS | E_WRITERS_LOCK, Ordering::Release);
if val & E_WRITERS_LOCK != 0 {
let readers = val & M_READERS_QUEUED;
if readers != 0 {
let val = self.futex.fetch_add(readers, Ordering::Relaxed);
if val & E_WRITERS_LOCK != 0 {
return;
}
}
}
if val & M_READERS_QUEUED != 0 {
let ret = futex_wake_bitset(&self.futex, i32::MAX as u32, ID_READER) as u32;
let _ = ret;
}
}
}