#![deny(missing_docs, warnings)]
#![cfg_attr(test, feature(test))]
#[cfg(test)]
extern crate test;
use std::sync::{Mutex, MutexGuard};
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering::SeqCst;
use std::cell::UnsafeCell;
use std::ops::{Deref, DerefMut};
use std::mem;
const UNUSED: usize = 0;
const LOCKED: usize = 1;
const FREE: usize = 2;
pub struct OnceMutex<T> {
lock: Mutex<()>,
state: AtomicUsize,
data: UnsafeCell<T>
}
unsafe impl<T: Send> Send for OnceMutex<T> {}
unsafe impl<T: Sync> Sync for OnceMutex<T> {}
impl<T: Send + Sync> OnceMutex<T> {
pub fn new(x: T) -> OnceMutex<T> {
OnceMutex {
lock: Mutex::new(()),
state: AtomicUsize::new(UNUSED),
data: UnsafeCell::new(x)
}
}
pub fn lock(&self) -> Option<OnceMutexGuard<T>> {
match self.state.compare_and_swap(UNUSED, LOCKED, SeqCst) {
UNUSED => {
Some(OnceMutexGuard::new(self))
},
_ => None
}
}
pub fn wait(&self) {
if self.locked() { let _ = self.lock.lock(); }
}
pub fn into_inner(self) -> T {
unsafe { self.data.into_inner() }
}
pub fn locked(&self) -> bool {
self.state.load(SeqCst) == LOCKED
}
}
impl<T: Send + Sync> Deref for OnceMutex<T> {
type Target = T;
fn deref(&self) -> &T {
if LOCKED == self.state.compare_and_swap(UNUSED, FREE, SeqCst) {
self.wait();
}
debug_assert_eq!(self.state.load(SeqCst), FREE);
unsafe { mem::transmute(self.data.get()) }
}
}
impl<T: Send + Sync> DerefMut for OnceMutex<T> {
fn deref_mut(&mut self) -> &mut T {
debug_assert!(self.state.load(SeqCst) != LOCKED);
unsafe { mem::transmute(self.data.get()) }
}
}
pub struct OnceMutexGuard<'a, T: 'a> {
parent: &'a OnceMutex<T>,
_lock: MutexGuard<'a, ()>
}
impl<'a, T> OnceMutexGuard<'a, T> {
fn new(mutex: &'a OnceMutex<T>) -> OnceMutexGuard<'a, T> {
OnceMutexGuard {
parent: mutex,
_lock: mutex.lock.lock().unwrap()
}
}
}
impl<'a, T: Send + Sync> DerefMut for OnceMutexGuard<'a, T> {
fn deref_mut(&mut self) -> &mut T {
unsafe { mem::transmute(self.parent.data.get()) }
}
}
impl<'a, T: Send + Sync> Deref for OnceMutexGuard<'a, T> {
type Target = T;
fn deref(&self) -> &T {
unsafe { mem::transmute(self.parent.data.get()) }
}
}
impl<'a, T> Drop for OnceMutexGuard<'a, T> {
fn drop(&mut self) {
self.parent.state.store(FREE, SeqCst);
}
}
fn _assert_send_sync() {
fn _assert_send<T: Send>() {}
fn _assert_sync<T: Sync>() {}
_assert_send::<OnceMutex<Vec<u8>>>();
_assert_sync::<OnceMutex<Vec<u8>>>();
}
#[cfg(test)]
mod tests {
use super::{OnceMutex, FREE, UNUSED, LOCKED};
use std::sync::atomic::Ordering::SeqCst;
use std::sync::Mutex;
use {test};
#[test]
fn test_once_mutex_locks_only_once() {
let mutex = OnceMutex::new("hello");
assert!(mutex.lock().is_some());
assert!(mutex.lock().is_none());
assert!(mutex.lock().is_none());
}
#[test]
fn test_once_mutex_states() {
let mutex = OnceMutex::new("hello");
assert_eq!(mutex.state.load(SeqCst), UNUSED);
let lock = mutex.lock();
assert_eq!(mutex.state.load(SeqCst), LOCKED);
drop(lock);
assert_eq!(mutex.state.load(SeqCst), FREE);
}
#[test]
fn test_once_mutex_deref() {
let mutex = OnceMutex::new("hello");
*mutex;
assert_eq!(mutex.state.load(SeqCst), FREE);
}
#[bench]
fn bench_once_mutex_locking(bencher: &mut test::Bencher) {
let mutex = OnceMutex::new(5);
bencher.iter(|| {
::test::black_box(mutex.lock());
mutex.state.store(UNUSED, SeqCst);
});
}
#[bench]
fn bench_once_mutex_access(bencher: &mut test::Bencher) {
let mutex = OnceMutex::new(5);
bencher.iter(|| ::test::black_box(*mutex));
}
#[bench]
fn bench_mutex_locking(bencher: &mut test::Bencher) {
let mutex = Mutex::new(5);
bencher.iter(|| ::test::black_box(mutex.lock()));
}
}