#![deny(missing_docs)]
use std::cell::UnsafeCell;
use std::sync::Mutex;
use std::sync::atomic::{AtomicBool, Ordering};
enum ThisOrThat<T, U> {
This(T),
That(U),
}
pub struct LazyTransform<T, U>
where T: Sync,
U: Sync
{
initialized: AtomicBool,
lock: Mutex<()>,
value: UnsafeCell<Option<ThisOrThat<T, U>>>,
}
impl<T, U> LazyTransform<T, U>
where T: Sync,
U: Sync
{
fn extract(&self) -> Option<&U> {
match unsafe { (*self.value.get()).as_ref() } {
None => None,
Some(&ThisOrThat::This(_)) => panic!(), Some(&ThisOrThat::That(ref that)) => Some(that),
}
}
}
impl<T, U> LazyTransform<T, U>
where T: Sync,
U: Sync
{
pub fn new(t: T) -> LazyTransform<T, U> {
LazyTransform {
initialized: AtomicBool::new(false),
lock: Mutex::new(()),
value: UnsafeCell::new(Some(ThisOrThat::This(t))),
}
}
pub fn get_or_create<F>(&self, f: F) -> &U
where F: FnOnce(T) -> U
{
if !self.initialized.load(Ordering::Acquire) {
let _lock = self.lock.lock().unwrap();
if !self.initialized.load(Ordering::Relaxed) {
let value = unsafe { &mut *self.value.get() };
let this = match value.take().unwrap() {
ThisOrThat::This(t) => t,
ThisOrThat::That(_) => panic!(), };
*value = Some(ThisOrThat::That(f(this)));
self.initialized.store(true, Ordering::Release);
} else {
}
}
self.extract().unwrap()
}
pub fn get(&self) -> Option<&U> {
if self.initialized.load(Ordering::Acquire) {
self.extract()
} else {
None
}
}
pub fn into_inner(self) -> Result<U, T> {
match unsafe { self.value.into_inner().unwrap() } {
ThisOrThat::This(t) => Err(t),
ThisOrThat::That(u) => Ok(u),
}
}
}
unsafe impl<T, U> Sync for LazyTransform<T, U>
where T: Sync,
U: Sync
{
}
impl<T, U> Default for LazyTransform<T, U>
where T: Sync + Default,
U: Sync
{
fn default() -> Self {
LazyTransform::new(T::default())
}
}
pub struct Lazy<T>
where T: Sync
{
inner: LazyTransform<(), T>,
}
impl<T> Lazy<T>
where T: Sync
{
pub fn new() -> Lazy<T> {
Self::default()
}
pub fn get_or_create<F>(&self, f: F) -> &T
where F: FnOnce() -> T
{
self.inner.get_or_create(|_| f())
}
pub fn get(&self) -> Option<&T> {
self.inner.get()
}
pub fn into_inner(self) -> Option<T> {
self.inner.into_inner().ok()
}
}
impl<T> Default for Lazy<T>
where T: Sync
{
fn default() -> Self {
Lazy { inner: LazyTransform::new(()) }
}
}
#[cfg(test)]
extern crate scoped_pool;
#[cfg(test)]
mod tests {
use scoped_pool::Pool;
use std::{thread, time};
use std::sync::atomic::{AtomicUsize, Ordering};
use super::{Lazy, LazyTransform};
#[test]
fn test_lazy() {
let lazy_value: Lazy<u8> = Lazy::new();
assert_eq!(lazy_value.get(), None);
let n = AtomicUsize::new(0);
let pool = Pool::new(100);
pool.scoped(|scope| {
for _ in 0..100 {
let lazy_ref = &lazy_value;
let n_ref = &n;
scope.execute(move || {
let ten_millis = time::Duration::from_millis(10);
thread::sleep(ten_millis);
let value = *lazy_ref.get_or_create(|| {
thread::sleep(ten_millis);
n_ref.fetch_add(1, Ordering::Relaxed);
42
});
assert_eq!(value, 42);
let value = lazy_ref.get();
assert_eq!(value, Some(&42));
});
}
});
assert_eq!(n.load(Ordering::SeqCst), 1);
}
#[test]
fn test_lazy_transform() {
let lazy_value: LazyTransform<u8, u8> = LazyTransform::new(21);
assert_eq!(lazy_value.get(), None);
let n = AtomicUsize::new(0);
let pool = Pool::new(100);
pool.scoped(|scope| {
for _ in 0..100 {
let lazy_ref = &lazy_value;
let n_ref = &n;
scope.execute(move || {
let ten_millis = time::Duration::from_millis(10);
thread::sleep(ten_millis);
let value = *lazy_ref.get_or_create(|v| {
thread::sleep(ten_millis);
n_ref.fetch_add(1, Ordering::Relaxed);
v * 2
});
assert_eq!(value, 42);
let value = lazy_ref.get();
assert_eq!(value, Some(&42));
});
}
});
assert_eq!(n.load(Ordering::SeqCst), 1);
}
}