lazy_init/lib.rs
1#![deny(missing_docs)]
2
3//! A crate for things that are
4//! 1) Lazily initialized
5//! 2) Expensive to create
6//! 3) Immutable after creation
7//! 4) Used on multiple threads
8//!
9//! `Lazy<T>` is better than `Mutex<Option<T>>` because after creation accessing
10//! `T` does not require any locking, just a single boolean load with
11//! `Ordering::Acquire` (which on x86 is just a compiler barrier, not an actual
12//! memory barrier).
13
14use std::cell::UnsafeCell;
15use std::fmt;
16use std::sync::atomic::{AtomicBool, Ordering};
17use std::sync::Mutex;
18
19#[derive(Clone)]
20enum ThisOrThat<T, U> {
21 This(T),
22 That(U),
23}
24
25/// `LazyTransform<T, U>` is a synchronized holder type, that holds a value of
26/// type T until it is lazily converted into a value of type U.
27pub struct LazyTransform<T, U> {
28 initialized: AtomicBool,
29 lock: Mutex<()>,
30 value: UnsafeCell<Option<ThisOrThat<T, U>>>,
31}
32
33// Implementation details.
34impl<T, U> LazyTransform<T, U> {
35 fn extract(&self) -> Option<&U> {
36 // Make sure we're initialized first!
37 match unsafe { (*self.value.get()).as_ref() } {
38 None => None,
39 Some(&ThisOrThat::This(_)) => panic!(), // Should already be initialized!
40 Some(&ThisOrThat::That(ref that)) => Some(that),
41 }
42 }
43}
44
45// Public API.
46impl<T, U> LazyTransform<T, U> {
47 /// Construct a new, untransformed `LazyTransform<T, U>` with an argument of
48 /// type T.
49 pub fn new(t: T) -> LazyTransform<T, U> {
50 LazyTransform {
51 initialized: AtomicBool::new(false),
52 lock: Mutex::new(()),
53 value: UnsafeCell::new(Some(ThisOrThat::This(t))),
54 }
55 }
56
57 /// Unwrap the contained value, returning `Ok(U)` if the `LazyTransform<T, U>` has been
58 /// transformed or `Err(T)` if it has not.
59 pub fn into_inner(self) -> Result<U, T> {
60 // We don't need to inspect `self.initialized` since `self` is owned
61 // so it is guaranteed that no other threads are accessing its data.
62 match self.value.into_inner().unwrap() {
63 ThisOrThat::This(t) => Err(t),
64 ThisOrThat::That(u) => Ok(u),
65 }
66 }
67}
68
69// Public API.
70impl<T, U> LazyTransform<T, U> {
71 /// Get a reference to the transformed value, invoking `f` to transform it
72 /// if the `LazyTransform<T, U>` has yet to be transformed. It is
73 /// guaranteed that if multiple calls to `get_or_create` race, only one
74 /// will invoke its closure, and every call will receive a reference to the
75 /// newly transformed value.
76 ///
77 /// The closure can only ever be called once, so think carefully about what
78 /// transformation you want to apply!
79 pub fn get_or_create<F>(&self, f: F) -> &U
80 where
81 F: FnOnce(T) -> U,
82 {
83 // In addition to being correct, this pattern is vouched for by Hans Boehm
84 // (http://schd.ws/hosted_files/cppcon2016/74/HansWeakAtomics.pdf Page 27)
85 if !self.initialized.load(Ordering::Acquire) {
86 // We *may* not be initialized. We have to block to be certain.
87 let _lock = self.lock.lock().unwrap();
88 if !self.initialized.load(Ordering::Relaxed) {
89 // Ok, we're definitely uninitialized.
90 // Safe to fiddle with the UnsafeCell now, because we're locked,
91 // and there can't be any outstanding references.
92 let value = unsafe { &mut *self.value.get() };
93 let this = match value.take().unwrap() {
94 ThisOrThat::This(t) => t,
95 ThisOrThat::That(_) => panic!(), // Can't already be initialized!
96 };
97 *value = Some(ThisOrThat::That(f(this)));
98 self.initialized.store(true, Ordering::Release);
99 } else {
100 // We raced, and someone else initialized us. We can fall
101 // through now.
102 }
103 }
104
105 // We're initialized, our value is immutable, no synchronization needed.
106 self.extract().unwrap()
107 }
108
109 /// Get a reference to the transformed value, returning `Some(&U)` if the
110 /// `LazyTransform<T, U>` has been transformed or `None` if it has not. It
111 /// is guaranteed that if a reference is returned it is to the transformed
112 /// value inside the the `LazyTransform<T>`.
113 pub fn get(&self) -> Option<&U> {
114 if self.initialized.load(Ordering::Acquire) {
115 // We're initialized, our value is immutable, no synchronization needed.
116 self.extract()
117 } else {
118 None
119 }
120 }
121}
122
123// As `T` is only ever accessed when locked, it's enough if it's `Send` for `Self` to be `Sync`.
124unsafe impl<T, U> Sync for LazyTransform<T, U>
125where
126 T: Send,
127 U: Send + Sync,
128{
129}
130
131impl<T, U> Clone for LazyTransform<T, U>
132where
133 T: Clone,
134 U: Clone,
135{
136 fn clone(&self) -> Self {
137 // Overall, this method is very similar to `get_or_create` and uses the same
138 // soundness reasoning.
139
140 if self.initialized.load(Ordering::Acquire) {
141 Self {
142 initialized: true.into(),
143 lock: Mutex::default(),
144 value: UnsafeCell::new(unsafe {
145 // SAFETY:
146 // Everything is initialized and immutable here, so lockless cloning is safe.
147 (&*self.value.get()).clone()
148 }),
149 }
150 } else {
151 // We *may* not be initialized. We have to block here before accessing `value`,
152 // which also synchronises the `initialized` load.
153 let _lock = self.lock.lock().unwrap();
154 Self {
155 initialized: self.initialized.load(Ordering::Relaxed).into(),
156 lock: Mutex::default(),
157 value: UnsafeCell::new(unsafe {
158 // SAFETY:
159 // Exclusive access while `_lock` is held.
160 (&*self.value.get()).clone()
161 }),
162 }
163 }
164 }
165
166 fn clone_from(&mut self, source: &Self) {
167 // Overall, this method is very similar to `get_or_create` and uses the same
168 // soundness reasoning. It's implemented explicitly here to avoid a `Mutex` drop/new.
169
170 if self.initialized.load(Ordering::Acquire) {
171 unsafe {
172 // SAFETY:
173 // Everything is initialized and immutable here, so lockless cloning is safe.
174 // It's still important to store `initialized` with correct ordering, though.
175 *self.value.get() = (&*source.value.get()).clone();
176 self.initialized.store(true, Ordering::Release);
177 }
178 } else {
179 // `source` *may* not be initialized. We have to block here before accessing `value`,
180 // which also synchronises the `initialized` load (and incidentally also the `initialized`
181 // store due to the exclusive reference to `self`, so that can be `Relaxed` here too).
182 let _lock = source.lock.lock().unwrap();
183 unsafe {
184 // SAFETY:
185 // Exclusive access to `source` while `_lock` is held.
186 *self.value.get() = (&*source.value.get()).clone();
187 self.initialized.store(
188 source.initialized.load(Ordering::Relaxed),
189 Ordering::Relaxed,
190 );
191 }
192 }
193 }
194}
195
196impl<T, U> Default for LazyTransform<T, U>
197where
198 T: Default,
199{
200 fn default() -> Self {
201 LazyTransform::new(T::default())
202 }
203}
204
205/// `Lazy<T>` is a lazily initialized synchronized holder type. You can think
206/// of it as a `LazyTransform` where the initial type doesn't exist.
207#[derive(Clone)]
208pub struct Lazy<T> {
209 inner: LazyTransform<(), T>,
210}
211
212impl<T> Lazy<T> {
213 /// Construct a new, uninitialized `Lazy<T>`.
214 pub fn new() -> Lazy<T> {
215 Self::default()
216 }
217
218 /// Unwrap the contained value, returning `Some` if the `Lazy<T>` has been initialized
219 /// or `None` if it has not.
220 pub fn into_inner(self) -> Option<T> {
221 self.inner.into_inner().ok()
222 }
223}
224
225impl<T> Lazy<T> {
226 /// Get a reference to the contained value, invoking `f` to create it
227 /// if the `Lazy<T>` is uninitialized. It is guaranteed that if multiple
228 /// calls to `get_or_create` race, only one will invoke its closure, and
229 /// every call will receive a reference to the newly created value.
230 ///
231 /// The value stored in the `Lazy<T>` is immutable after the closure returns
232 /// it, so think carefully about what you want to put inside!
233 pub fn get_or_create<F>(&self, f: F) -> &T
234 where
235 F: FnOnce() -> T,
236 {
237 self.inner.get_or_create(|_| f())
238 }
239
240 /// Get a reference to the contained value, returning `Some(ref)` if the
241 /// `Lazy<T>` has been initialized or `None` if it has not. It is
242 /// guaranteed that if a reference is returned it is to the value inside
243 /// the `Lazy<T>`.
244 pub fn get(&self) -> Option<&T> {
245 self.inner.get()
246 }
247}
248
249// `#[derive(Default)]` automatically adds `T: Default` trait bound, but that
250// is too restrictive, because `Lazy<T>` always has a default value for any `T`.
251impl<T> Default for Lazy<T> {
252 fn default() -> Self {
253 Lazy {
254 inner: LazyTransform::new(()),
255 }
256 }
257}
258
259impl<T> fmt::Debug for Lazy<T>
260where
261 T: fmt::Debug,
262{
263 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
264 if let Some(v) = self.get() {
265 f.write_fmt(format_args!("Lazy({:?})", v))
266 } else {
267 f.write_str("Lazy(<uninitialized>)")
268 }
269 }
270}
271
272#[cfg(test)]
273extern crate rayon;
274
275#[cfg(test)]
276mod tests {
277
278 use super::{Lazy, LazyTransform};
279 use rayon::ThreadPoolBuilder;
280 use std::sync::atomic::{AtomicUsize, Ordering};
281 use std::{thread, time};
282
283 #[test]
284 fn test_lazy() {
285 let lazy_value: Lazy<u8> = Lazy::new();
286
287 assert_eq!(lazy_value.get(), None);
288
289 let n = AtomicUsize::new(0);
290
291 let pool = ThreadPoolBuilder::new().num_threads(100).build().unwrap();
292 pool.scope(|scope| {
293 for _ in 0..100 {
294 let lazy_ref = &lazy_value;
295 let n_ref = &n;
296 scope.spawn(move |_| {
297 let ten_millis = time::Duration::from_millis(10);
298 thread::sleep(ten_millis);
299
300 let value = *lazy_ref.get_or_create(|| {
301 // Make everybody else wait on me, because I'm a jerk.
302 thread::sleep(ten_millis);
303
304 // Make this relaxed so it doesn't interfere with
305 // Lazy internals at all.
306 n_ref.fetch_add(1, Ordering::Relaxed);
307
308 42
309 });
310 assert_eq!(value, 42);
311
312 let value = lazy_ref.get();
313 assert_eq!(value, Some(&42));
314 });
315 }
316 });
317
318 assert_eq!(n.load(Ordering::SeqCst), 1);
319 }
320
321 #[test]
322 fn test_lazy_transform() {
323 let lazy_value: LazyTransform<u8, u8> = LazyTransform::new(21);
324
325 assert_eq!(lazy_value.get(), None);
326
327 let n = AtomicUsize::new(0);
328
329 let pool = ThreadPoolBuilder::new().num_threads(100).build().unwrap();
330 pool.scope(|scope| {
331 for _ in 0..100 {
332 let lazy_ref = &lazy_value;
333 let n_ref = &n;
334 scope.spawn(move |_| {
335 let ten_millis = time::Duration::from_millis(10);
336 thread::sleep(ten_millis);
337
338 let value = *lazy_ref.get_or_create(|v| {
339 // Make everybody else wait on me, because I'm a jerk.
340 thread::sleep(ten_millis);
341
342 // Make this relaxed so it doesn't interfere with
343 // Lazy internals at all.
344 n_ref.fetch_add(1, Ordering::Relaxed);
345
346 v * 2
347 });
348 assert_eq!(value, 42);
349
350 let value = lazy_ref.get();
351 assert_eq!(value, Some(&42));
352 });
353 }
354 });
355
356 assert_eq!(n.load(Ordering::SeqCst), 1);
357 }
358}