#![deny(missing_docs)]
use std::cell::UnsafeCell;
use std::fmt;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Mutex;
#[derive(Clone)]
enum ThisOrThat<T, U> {
This(T),
That(U),
}
pub struct LazyTransform<T, U> {
initialized: AtomicBool,
lock: Mutex<()>,
value: UnsafeCell<Option<ThisOrThat<T, U>>>,
}
impl<T, U> LazyTransform<T, U> {
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> {
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 into_inner(self) -> Result<U, T> {
match self.value.into_inner().unwrap() {
ThisOrThat::This(t) => Err(t),
ThisOrThat::That(u) => Ok(u),
}
}
}
impl<T, U> LazyTransform<T, U> {
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
}
}
}
unsafe impl<T, U> Sync for LazyTransform<T, U>
where
T: Send,
U: Send + Sync,
{
}
impl<T, U> Clone for LazyTransform<T, U>
where
T: Clone,
U: Clone,
{
fn clone(&self) -> Self {
if self.initialized.load(Ordering::Acquire) {
Self {
initialized: true.into(),
lock: Mutex::default(),
value: UnsafeCell::new(unsafe {
(&*self.value.get()).clone()
}),
}
} else {
let _lock = self.lock.lock().unwrap();
Self {
initialized: self.initialized.load(Ordering::Relaxed).into(),
lock: Mutex::default(),
value: UnsafeCell::new(unsafe {
(&*self.value.get()).clone()
}),
}
}
}
fn clone_from(&mut self, source: &Self) {
if self.initialized.load(Ordering::Acquire) {
unsafe {
*self.value.get() = (&*source.value.get()).clone();
self.initialized.store(true, Ordering::Release);
}
} else {
let _lock = source.lock.lock().unwrap();
unsafe {
*self.value.get() = (&*source.value.get()).clone();
self.initialized.store(
source.initialized.load(Ordering::Relaxed),
Ordering::Relaxed,
);
}
}
}
}
impl<T, U> Default for LazyTransform<T, U>
where
T: Default,
{
fn default() -> Self {
LazyTransform::new(T::default())
}
}
#[derive(Clone)]
pub struct Lazy<T> {
inner: LazyTransform<(), T>,
}
impl<T> Lazy<T> {
pub fn new() -> Lazy<T> {
Self::default()
}
pub fn into_inner(self) -> Option<T> {
self.inner.into_inner().ok()
}
}
impl<T> Lazy<T> {
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()
}
}
impl<T> Default for Lazy<T> {
fn default() -> Self {
Lazy {
inner: LazyTransform::new(()),
}
}
}
impl<T> fmt::Debug for Lazy<T>
where
T: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if let Some(v) = self.get() {
f.write_fmt(format_args!("Lazy({:?})", v))
} else {
f.write_str("Lazy(<uninitialized>)")
}
}
}
#[cfg(test)]
extern crate rayon;
#[cfg(test)]
mod tests {
use super::{Lazy, LazyTransform};
use rayon::ThreadPoolBuilder;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::{thread, time};
#[test]
fn test_lazy() {
let lazy_value: Lazy<u8> = Lazy::new();
assert_eq!(lazy_value.get(), None);
let n = AtomicUsize::new(0);
let pool = ThreadPoolBuilder::new().num_threads(100).build().unwrap();
pool.scope(|scope| {
for _ in 0..100 {
let lazy_ref = &lazy_value;
let n_ref = &n;
scope.spawn(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 = ThreadPoolBuilder::new().num_threads(100).build().unwrap();
pool.scope(|scope| {
for _ in 0..100 {
let lazy_ref = &lazy_value;
let n_ref = &n;
scope.spawn(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);
}
}