pub mod condvar;
pub mod futex;
pub mod raw_mutex;
pub mod raw_rwlock;
pub use condvar::{Condvar, WaitTimeoutResult};
pub use raw_mutex::NoxuRawMutex;
pub use raw_rwlock::NoxuRawRwLock;
pub use lock_api;
pub type Mutex<T> = lock_api::Mutex<NoxuRawMutex, T>;
pub type MutexGuard<'a, T> = lock_api::MutexGuard<'a, NoxuRawMutex, T>;
pub type RawMutex = NoxuRawMutex;
pub type RwLockReadGuard<'a, T> =
lock_api::RwLockReadGuard<'a, NoxuRawRwLock, T>;
pub type RwLockWriteGuard<'a, T> =
lock_api::RwLockWriteGuard<'a, NoxuRawRwLock, T>;
pub struct RwLock<T>(lock_api::RwLock<NoxuRawRwLock, T>);
impl<T> RwLock<T> {
#[inline]
pub fn new(val: T) -> Self {
RwLock(lock_api::RwLock::new(val))
}
#[inline]
pub fn read(&self) -> RwLockReadGuard<'_, T> {
self.0.read()
}
#[inline]
pub fn write(&self) -> RwLockWriteGuard<'_, T> {
self.0.write()
}
#[inline]
pub fn try_read(&self) -> Option<RwLockReadGuard<'_, T>> {
self.0.try_read()
}
#[inline]
pub fn try_write(&self) -> Option<RwLockWriteGuard<'_, T>> {
self.0.try_write()
}
#[inline]
pub fn try_read_for(
&self,
timeout: std::time::Duration,
) -> Option<RwLockReadGuard<'_, T>> {
self.0.try_read_for(timeout)
}
#[inline]
pub fn try_write_for(
&self,
timeout: std::time::Duration,
) -> Option<RwLockWriteGuard<'_, T>> {
self.0.try_write_for(timeout)
}
#[inline]
pub fn is_locked(&self) -> bool {
self.0.is_locked()
}
#[inline]
pub fn is_locked_exclusive(&self) -> bool {
unsafe { self.0.raw().is_write_locked() }
}
#[inline]
pub fn get_n_waiters(&self) -> usize {
unsafe { self.0.raw().get_n_waiters() }
}
#[inline]
pub fn reader_count(&self) -> u32 {
unsafe { self.0.raw().reader_count() }
}
#[inline]
pub fn raw(&self) -> &NoxuRawRwLock {
unsafe { self.0.raw() }
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::Arc;
use std::time::Duration;
#[test]
fn mutex_basic() {
let m = Mutex::new(0i32);
*m.lock() = 42;
assert_eq!(*m.lock(), 42);
}
#[test]
fn mutex_try_lock() {
let m = Arc::new(Mutex::new(()));
let g = m.lock();
let m2 = m.clone();
let failed =
std::thread::spawn(move || m2.try_lock().is_none()).join().unwrap();
assert!(failed);
drop(g);
assert!(m.try_lock().is_some());
}
#[test]
fn mutex_try_lock_for_timeout() {
let m = Arc::new(Mutex::new(()));
let g = m.lock();
let m2 = m.clone();
let timed_out = std::thread::spawn(move || {
m2.try_lock_for(Duration::from_millis(30)).is_none()
})
.join()
.unwrap();
assert!(timed_out);
drop(g);
}
#[test]
fn mutex_get_n_waiters() {
let m = Arc::new(Mutex::new(()));
let _g = m.lock();
let m2 = m.clone();
let barrier = Arc::new(std::sync::Barrier::new(2));
let b2 = barrier.clone();
let handle = std::thread::spawn(move || {
b2.wait();
let _g2 = m2.lock();
});
barrier.wait();
std::thread::sleep(Duration::from_millis(10));
assert!(unsafe { m.raw().get_n_waiters() } >= 1 || m.is_locked());
drop(_g);
handle.join().unwrap();
}
#[test]
fn mutex_force_unlock() {
let m = Mutex::new(());
let _g = m.lock();
unsafe { m.force_unlock() };
assert!(m.try_lock().is_some());
}
#[test]
fn rwlock_basic_read_write() {
let rw = RwLock::new(0i32);
*rw.write() = 99;
assert_eq!(*rw.read(), 99);
}
#[test]
fn rwlock_multiple_readers() {
let rw = Arc::new(RwLock::new(42i32));
let rw2 = rw.clone();
let g1 = rw.read();
let handle = std::thread::spawn(move || {
let g2 = rw2.read();
*g2
});
assert_eq!(*g1, 42);
assert_eq!(handle.join().unwrap(), 42);
}
#[test]
fn rwlock_exclusive_blocks_readers() {
let rw = Arc::new(RwLock::new(()));
let _wg = rw.write();
let rw2 = rw.clone();
let failed = std::thread::spawn(move || rw2.try_read().is_none())
.join()
.unwrap();
assert!(failed);
}
#[test]
fn rwlock_is_locked_exclusive() {
let rw = RwLock::new(());
assert!(!rw.is_locked_exclusive());
let _wg = rw.write();
assert!(rw.is_locked_exclusive());
}
#[test]
fn rwlock_try_write_for_timeout() {
let rw = Arc::new(RwLock::new(()));
let _wg = rw.write();
let rw2 = rw.clone();
let timed_out = std::thread::spawn(move || {
rw2.try_write_for(Duration::from_millis(30)).is_none()
})
.join()
.unwrap();
assert!(timed_out);
}
#[test]
fn rwlock_try_read_for_timeout() {
let rw = Arc::new(RwLock::new(()));
let _wg = rw.write();
let rw2 = rw.clone();
let timed_out = std::thread::spawn(move || {
rw2.try_read_for(Duration::from_millis(30)).is_none()
})
.join()
.unwrap();
assert!(timed_out);
}
#[test]
fn raw_mutex_const_init_and_lock_unlock() {
use lock_api::RawMutex as RawMutexTrait;
let raw = NoxuRawMutex::INIT;
assert!(!raw.is_locked());
raw.lock();
assert!(raw.is_locked());
unsafe { raw.unlock() };
assert!(!raw.is_locked());
}
}