#![deny(missing_docs)]
#![cfg_attr(feature = "nightly", feature(plugin))]
#![cfg_attr(feature = "clippy", plugin(clippy))]
use std::cell::UnsafeCell;
use std::sync::atomic::{AtomicUsize, Ordering};
pub struct LazyCell<T> {
inner: UnsafeCell<Option<T>>,
}
impl<T> LazyCell<T> {
pub fn new() -> LazyCell<T> {
LazyCell { inner: UnsafeCell::new(None) }
}
pub fn fill(&self, t: T) -> Result<(), T> {
let mut slot = unsafe { &mut *self.inner.get() };
if slot.is_some() {
return Err(t);
}
*slot = Some(t);
Ok(())
}
pub fn filled(&self) -> bool {
self.borrow().is_some()
}
pub fn borrow(&self) -> Option<&T> {
unsafe { &*self.inner.get() }.as_ref()
}
pub fn into_inner(self) -> Option<T> {
unsafe { self.inner.into_inner() }
}
}
const NONE: usize = 0;
const LOCK: usize = 1;
const SOME: usize = 2;
pub struct AtomicLazyCell<T> {
inner: UnsafeCell<Option<T>>,
state: AtomicUsize,
}
impl<T> AtomicLazyCell<T> {
pub fn new() -> AtomicLazyCell<T> {
AtomicLazyCell {
inner: UnsafeCell::new(None),
state: AtomicUsize::new(NONE),
}
}
pub fn fill(&self, t: T) -> Result<(), T> {
if NONE != self.state.compare_and_swap(NONE, LOCK, Ordering::Acquire) {
return Err(t);
}
unsafe { *self.inner.get() = Some(t) };
if LOCK != self.state.compare_and_swap(LOCK, SOME, Ordering::Release) {
panic!("unable to release lock");
}
Ok(())
}
pub fn filled(&self) -> bool {
self.state.load(Ordering::Acquire) == SOME
}
pub fn borrow(&self) -> Option<&T> {
match self.state.load(Ordering::Acquire) {
SOME => unsafe { &*self.inner.get() }.as_ref(),
_ => None,
}
}
pub fn into_inner(self) -> Option<T> {
unsafe { self.inner.into_inner() }
}
}
unsafe impl<T: Sync> Sync for AtomicLazyCell<T> { }
unsafe impl<T: Send> Send for AtomicLazyCell<T> { }
#[cfg(test)]
mod tests {
use super::{LazyCell, AtomicLazyCell};
#[test]
fn test_borrow_from_empty() {
let lazycell: LazyCell<usize> = LazyCell::new();
let value = lazycell.borrow();
assert_eq!(value, None);
}
#[test]
fn test_fill_and_borrow() {
let lazycell = LazyCell::new();
assert!(!lazycell.filled());
lazycell.fill(1).unwrap();
assert!(lazycell.filled());
let value = lazycell.borrow();
assert_eq!(value, Some(&1));
}
#[test]
fn test_already_filled_error() {
let lazycell = LazyCell::new();
lazycell.fill(1).unwrap();
assert_eq!(lazycell.fill(1), Err(1));
}
#[test]
fn test_into_inner() {
let lazycell = LazyCell::new();
lazycell.fill(1).unwrap();
let value = lazycell.into_inner();
assert_eq!(value, Some(1));
}
#[test]
fn test_atomic_borrow_from_empty() {
let lazycell: AtomicLazyCell<usize> = AtomicLazyCell::new();
let value = lazycell.borrow();
assert_eq!(value, None);
}
#[test]
fn test_atomic_fill_and_borrow() {
let lazycell = AtomicLazyCell::new();
assert!(!lazycell.filled());
lazycell.fill(1).unwrap();
assert!(lazycell.filled());
let value = lazycell.borrow();
assert_eq!(value, Some(&1));
}
#[test]
fn test_atomic_already_filled_panic() {
let lazycell = AtomicLazyCell::new();
lazycell.fill(1).unwrap();
assert_eq!(1, lazycell.fill(1).unwrap_err());
}
#[test]
fn test_atomic_into_inner() {
let lazycell = AtomicLazyCell::new();
lazycell.fill(1).unwrap();
let value = lazycell.into_inner();
assert_eq!(value, Some(1));
}
}