#![allow(deprecated)]
use std::mem;
use std::sync;
#[deprecated(
since = "2.16",
note = "Please regenerate .rs files from .proto files to use newer APIs"
)]
pub struct Lazy<T> {
#[doc(hidden)]
pub lock: sync::Once,
#[doc(hidden)]
pub ptr: *const T,
}
impl<T> Lazy<T> {
pub const INIT: Lazy<T> = Lazy {
lock: sync::Once::new(),
ptr: 0 as *const T,
};
pub fn get<F>(&'static mut self, init: F) -> &'static T
where
F: FnOnce() -> T,
{
let lock: &sync::Once = unsafe { mem::transmute(&self.lock) };
lock.call_once(|| unsafe {
self.ptr = mem::transmute(Box::new(init()));
});
unsafe { &*self.ptr }
}
}
#[deprecated(
since = "2.11",
note = "Regenerate .proto files to use safer initializer"
)]
pub const ONCE_INIT: sync::Once = sync::Once::new();
#[cfg(test)]
mod test {
use std::sync::atomic::AtomicIsize;
use std::sync::atomic::Ordering;
use std::sync::Arc;
use std::sync::Barrier;
use std::thread;
use super::Lazy;
#[test]
fn many_threads_calling_get() {
const N_THREADS: usize = 32;
const N_ITERS_IN_THREAD: usize = 32;
const N_ITERS: usize = 16;
static mut LAZY: Lazy<String> = Lazy::INIT;
static CALL_COUNT: AtomicIsize = AtomicIsize::new(0);
let value = "Hello, world!".to_owned();
for _ in 0..N_ITERS {
unsafe {
LAZY = Lazy::INIT;
}
CALL_COUNT.store(0, Ordering::SeqCst);
let mut threads = vec![];
let barrier = Arc::new(Barrier::new(N_THREADS));
for _ in 0..N_THREADS {
let cloned_value_thread = value.clone();
let cloned_barrier = barrier.clone();
threads.push(thread::spawn(move || {
cloned_barrier.wait();
for _ in 0..N_ITERS_IN_THREAD {
assert_eq!(&cloned_value_thread, unsafe {
LAZY.get(|| {
CALL_COUNT.fetch_add(1, Ordering::SeqCst);
cloned_value_thread.clone()
})
});
}
}));
}
for thread in threads {
thread.join().unwrap();
}
assert_eq!(CALL_COUNT.load(Ordering::SeqCst), 1);
}
}
}