use std::sync::{Mutex, Condvar, MutexGuard};
pub struct RawSharedMutex {
state: Mutex<State>,
readers: Condvar,
both: Condvar,
}
impl RawSharedMutex {
#[inline]
pub fn new() -> RawSharedMutex {
RawSharedMutex {
state: Mutex::new(State::new()),
readers: Condvar::new(),
both: Condvar::new()
}
}
#[inline]
pub fn is(&self, other: &Self) -> bool {
self as *const Self == other as *const Self
}
#[inline]
pub fn read(&self) {
self.read_from(self.state.lock().unwrap())
}
fn read_from(&self, mut state_lock: MutexGuard<State>) {
while state_lock.is_writer_active() || state_lock.has_max_readers() {
state_lock = self.both.wait(state_lock).unwrap();
}
state_lock.add_reader();
}
pub fn try_read(&self) -> bool {
let mut state_lock = self.state.lock().unwrap();
if !state_lock.is_writer_active() && !state_lock.has_max_readers() {
state_lock.add_reader();
true
} else {
false }
}
#[inline]
pub fn write(&self) {
self.write_from(self.state.lock().unwrap())
}
fn write_from(&self, mut state_lock: MutexGuard<State>) {
while state_lock.is_writer_active() {
state_lock = self.both.wait(state_lock).unwrap();
}
state_lock.set_writer_active();
while state_lock.readers() != 0 {
state_lock = self.readers.wait(state_lock).unwrap();
}
debug_assert!(state_lock.is_writer_active() && state_lock.readers() == 0,
"State not empty on write lock! State = {:?}", *state_lock);
}
pub fn try_write(&self) -> bool {
let mut state_lock = self.state.lock().unwrap();
if !state_lock.is_writer_active() && state_lock.readers() == 0 {
state_lock.set_writer_active();
true
} else {
false }
}
#[inline]
pub fn unlock_read(&self) {
let _ = self.unlock_read_to();
}
fn unlock_read_to(&self) -> MutexGuard<State> {
let mut state_lock = self.state.lock().unwrap();
state_lock.remove_reader();
if state_lock.is_writer_active() {
if state_lock.readers() == 0 {
self.readers.notify_one();
}
} else if state_lock.near_max_readers() {
self.both.notify_one()
}
state_lock
}
#[inline]
pub fn unlock_write(&self) {
let _ = self.unlock_write_to();
}
#[inline]
fn unlock_write_to(&self) -> MutexGuard<State> {
let mut state_lock = self.state.lock().unwrap();
*state_lock = State::new();
self.both.notify_all();
state_lock
}
#[inline]
pub fn wait_from_read_to_write(&self, cond: &Condvar) {
let state_lock = self.unlock_read_to();
let state_lock = cond.wait(state_lock).unwrap();
self.write_from(state_lock);
}
#[inline]
pub fn wait_from_read_to_read(&self, cond: &Condvar) {
let state_lock = self.unlock_read_to();
let state_lock = cond.wait(state_lock).unwrap();
self.read_from(state_lock);
}
#[inline]
pub fn wait_from_write_to_read(&self, cond: &Condvar) {
let state_lock = self.unlock_write_to();
let state_lock = cond.wait(state_lock).unwrap();
self.read_from(state_lock);
}
#[inline]
pub fn wait_from_write_to_write(&self, cond: &Condvar) {
let state_lock = self.unlock_write_to();
let state_lock = cond.wait(state_lock).unwrap();
self.write_from(state_lock);
}
}
#[derive(Debug)]
struct State(usize);
#[cfg(target_pointer_width = "64")]
const USIZE_BITS: u8 = 64;
#[cfg(target_pointer_width = "32")]
const USIZE_BITS: u8 = 32;
const WRITER_ACTIVE: usize = 1 << USIZE_BITS - 1;
const READERS_MASK: usize = !WRITER_ACTIVE;
impl State {
#[inline]
fn new() -> Self { State(0) }
#[inline]
fn is_writer_active(&self) -> bool { self.0 & WRITER_ACTIVE != 0 }
#[inline]
fn set_writer_active(&mut self) { self.0 |= WRITER_ACTIVE }
#[inline]
fn readers(&self) -> usize { self.0 & READERS_MASK }
#[inline]
fn has_max_readers(&self) -> bool { self.readers() == READERS_MASK }
#[inline]
fn near_max_readers(&self) -> bool { self.readers() == READERS_MASK - 1 }
#[inline]
fn add_reader(&mut self) { self.0 += 1 }
#[inline]
fn remove_reader(&mut self) { self.0 -= 1 }
}
#[cfg(test)]
mod test {
use raw::RawSharedMutex;
#[test]
fn test_raw_is() {
let mutex1 = RawSharedMutex::new();
let mutex2 = RawSharedMutex::new();
assert!(mutex1.is(&mutex1));
assert!(!mutex1.is(&mutex2));
}
}