try_lazy_init/lib.rs
1#![doc(html_root_url = "https://docs.rs/try-lazy-init/0.0.2")]
2#![warn(clippy::pedantic)]
3#![allow(clippy::semicolon_if_nothing_returned)]
4#![deny(missing_docs)]
5
6//! A crate for things that are
7//! 1) Lazily initialized
8//! 2) Expensive to create
9//! 3) Immutable after creation
10//! 4) Used on multiple threads
11//!
12//! `Lazy<T>` is better than `Mutex<Option<T>>` because after creation accessing
13//! `T` does not require any locking, just a single boolean load with
14//! `Ordering::Acquire` (which on x86 is just a compiler barrier, not an actual
15//! memory barrier).
16
17#[cfg(doctest)]
18pub mod readme {
19 doc_comment::doctest!("../README.md");
20}
21
22use std::{
23 cell::UnsafeCell,
24 fmt,
25 sync::{
26 atomic::{AtomicBool, Ordering},
27 Mutex,
28 },
29};
30
31#[derive(Clone)]
32enum ThisOrThat<T, U> {
33 This(T),
34 That(U),
35}
36
37/// `LazyTransform<T, U>` is a synchronized holder type, that holds a value of
38/// type T until it is lazily converted into a value of type U.
39pub struct LazyTransform<T, U> {
40 initialized: AtomicBool,
41 lock: Mutex<()>,
42 value: UnsafeCell<Option<ThisOrThat<T, U>>>,
43}
44
45// Implementation details.
46impl<T, U> LazyTransform<T, U> {
47 fn extract(&self) -> Option<&U> {
48 // Make sure we're initialized first!
49 match unsafe { (*self.value.get()).as_ref() } {
50 None => None,
51 Some(&ThisOrThat::This(_)) => panic!(), // Should already be initialized!
52 Some(&ThisOrThat::That(ref that)) => Some(that),
53 }
54 }
55}
56
57// Public API.
58impl<T, U> LazyTransform<T, U> {
59 /// Construct a new, untransformed `LazyTransform<T, U>` with an argument of
60 /// type T.
61 pub fn new(t: T) -> LazyTransform<T, U> {
62 LazyTransform {
63 initialized: AtomicBool::new(false),
64 lock: Mutex::new(()),
65 value: UnsafeCell::new(Some(ThisOrThat::This(t))),
66 }
67 }
68
69 /// Unwrap the contained value, returning `Ok(U)` if the `LazyTransform<T, U>` has been transformed.
70 ///
71 /// # Errors
72 ///
73 /// Iff this instance has not been transformed yet.
74 ///
75 /// # Panics
76 ///
77 /// Iff this instance has been poisoned during transformation.
78 pub fn into_inner(self) -> Result<U, T> {
79 // We don't need to inspect `self.initialized` since `self` is owned
80 // so it is guaranteed that no other threads are accessing its data.
81 match self.value.into_inner().unwrap() {
82 ThisOrThat::This(t) => Err(t),
83 ThisOrThat::That(u) => Ok(u),
84 }
85 }
86
87 /// Unwrap the contained value, returning `Ok(Ok(U))` iff the `LazyTransform<T, U>` has been transformed.
88 ///
89 /// # Errors
90 ///
91 /// Iff this instance has neither been transformed yet nor poisoned, `Err(Some(T))` is returned.
92 ///
93 /// Iff this instance has been poisoned *by error* during a call to [`.get_or_create_or_poison`](`LazyTransform::get_or_create_or_poison`), `Err(None)` is returned.
94 ///
95 /// # Panics
96 ///
97 /// Iff this instance has been poisoned *by a panic* during transformation.
98 pub fn try_into_inner(self) -> Result<U, Option<T>> {
99 // We don't need to inspect `self.initialized` since `self` is owned
100 // so it is guaranteed that no other threads are accessing its data.
101 match self.value.into_inner() {
102 None => Err(None),
103 Some(ThisOrThat::This(t)) => Err(Some(t)),
104 Some(ThisOrThat::That(u)) => Ok(u),
105 }
106 }
107}
108
109// Public API.
110impl<T, U> LazyTransform<T, U> {
111 /// Get a reference to the transformed value, invoking `f` to transform it
112 /// if the `LazyTransform<T, U>` has yet to be transformed. It is
113 /// guaranteed that if multiple calls to `get_or_create` race, only one
114 /// will invoke its closure, and every call will receive a reference to the
115 /// newly transformed value.
116 ///
117 /// The closure can only ever be called once, so think carefully about what
118 /// transformation you want to apply!
119 ///
120 /// # Panics
121 ///
122 /// This method will panic if the instance has been poisoned during a previous transformation attempt.
123 ///
124 /// The method **may** panic (or deadlock) upon reentrance.
125 pub fn get_or_create<F>(&self, f: F) -> &U
126 where
127 F: FnOnce(T) -> U,
128 {
129 // In addition to being correct, this pattern is vouched for by Hans Boehm
130 // (http://schd.ws/hosted_files/cppcon2016/74/HansWeakAtomics.pdf Page 27)
131 if !self.initialized.load(Ordering::Acquire) {
132 // We *may* not be initialized. We have to block to be certain.
133 let _lock = self.lock.lock().unwrap();
134 #[allow(clippy::if_not_else)]
135 if !self.initialized.load(Ordering::Relaxed) {
136 // Ok, we're definitely uninitialized.
137 // Safe to fiddle with the UnsafeCell now, because we're locked,
138 // and there can't be any outstanding references.
139 let value = unsafe { &mut *self.value.get() };
140 let this = match value.take().unwrap() {
141 ThisOrThat::This(t) => t,
142 ThisOrThat::That(_) => panic!(), // Can't already be initialized!
143 };
144 *value = Some(ThisOrThat::That(f(this)));
145 self.initialized.store(true, Ordering::Release);
146 } else {
147 // We raced, and someone else initialized us. We can fall
148 // through now.
149 }
150 }
151
152 // We're initialized, our value is immutable, no synchronization needed.
153 self.extract().unwrap()
154 }
155
156 /// Try to get a reference to the transformed value, invoking a fallible `f` to
157 /// transform it if the `LazyTransform<T, U>` has yet to be transformed.
158 /// It is guaranteed that if multiple calls to `get_or_create` race, only one
159 /// will **successfully** invoke its closure, and every call will receive a
160 /// reference to the newly transformed value.
161 ///
162 /// The closure can only ever be successfully called once, so think carefully
163 /// about what transformation you want to apply!
164 ///
165 /// # Errors
166 ///
167 /// Iff `f` returns a [`Result::Err`], this error is returned verbatim.
168 ///
169 /// # Panics
170 ///
171 /// This method will panic if the instance has been poisoned during a previous transformation attempt.
172 ///
173 /// The method **may** panic (or deadlock) upon reentrance.
174 pub fn try_get_or_create<F, E>(&self, f: F) -> Result<&U, E>
175 where
176 T: Clone,
177 F: FnOnce(T) -> Result<U, E>,
178 {
179 // In addition to being correct, this pattern is vouched for by Hans Boehm
180 // (http://schd.ws/hosted_files/cppcon2016/74/HansWeakAtomics.pdf Page 27)
181 #[allow(clippy::if_not_else)]
182 if !self.initialized.load(Ordering::Acquire) {
183 // We *may* not be initialized. We have to block to be certain.
184 let _lock = self.lock.lock().unwrap();
185 if !self.initialized.load(Ordering::Relaxed) {
186 // Ok, we're definitely uninitialized.
187 // Safe to fiddle with the UnsafeCell now, because we're locked,
188 // and there can't be any outstanding references.
189 //
190 // However, since this function can return early without poisoning this instance,
191 // `self.value` must stay valid until overwritten with `f`'s `Ok`.
192 let value = unsafe { &mut *self.value.get() };
193 let this = match value.as_ref().unwrap() {
194 ThisOrThat::This(t) => t.clone(),
195 ThisOrThat::That(_) => panic!(), // Can't already be initialized!
196 };
197 *value = Some(ThisOrThat::That(f(this)?));
198 self.initialized.store(true, Ordering::Release);
199 } else {
200 // We raced, and someone else initialized us. We can fall
201 // through now.
202 }
203 }
204
205 // We're initialized, our value is immutable, no synchronization needed.
206 Ok(self.extract().unwrap())
207 }
208
209 /// Try to get a reference to the transformed value, invoking a fallible `f` to
210 /// transform it if the `LazyTransform<T, U>` has yet to be transformed.
211 /// It is guaranteed that if multiple calls to `get_or_create` race, only one
212 /// will invoke its closure, and every call will receive a reference to the
213 /// newly transformed value.
214 ///
215 /// The closure can only ever be called once, so think carefully
216 /// about what transformation you want to apply!
217 ///
218 /// # Errors
219 ///
220 /// Iff this instance is poisoned, *except by panics*, <code>[Err](`Err`)([None])</code> is returned.
221 ///
222 /// Iff `f` returns a [`Result::Err`], this error is returned wrapped in [`Some`].
223 ///
224 /// # Panics
225 ///
226 /// This method will panic if the instance has been poisoned *due to a panic* during a previous transformation attempt.
227 ///
228 /// The method **may** panic (or deadlock) upon reentrance.
229 pub fn get_or_create_or_poison<F, E>(&self, f: F) -> Result<&U, Option<E>>
230 where
231 F: FnOnce(T) -> Result<U, E>,
232 {
233 // In addition to being correct, this pattern is vouched for by Hans Boehm
234 // (http://schd.ws/hosted_files/cppcon2016/74/HansWeakAtomics.pdf Page 27)
235 #[allow(clippy::if_not_else)]
236 if !self.initialized.load(Ordering::Acquire) {
237 // We *may* not be initialized. We have to block to be certain.
238 let _lock = self.lock.lock().unwrap();
239 if !self.initialized.load(Ordering::Relaxed) {
240 // Ok, we're definitely uninitialized.
241 // Safe to fiddle with the UnsafeCell now, because we're locked,
242 // and there can't be any outstanding references.
243 //
244 // However, since this function can return early without poisoning `self.lock`,
245 // `self.value` is first overwritten with `None` to mark the instance as poisoned-by-error.
246 let value = unsafe { &mut *self.value.get() };
247 let this = match value.take() {
248 None => return Err(None), // Poisoned by previous error.
249 Some(ThisOrThat::This(t)) => t,
250 Some(ThisOrThat::That(_)) => panic!(), // Can't already be initialized!
251 };
252 *value = Some(ThisOrThat::That(f(this)?));
253 self.initialized.store(true, Ordering::Release);
254 } else {
255 // We raced, and someone else initialized us. We can fall
256 // through now.
257 }
258 }
259
260 // We're initialized, our value is immutable, no synchronization needed.
261 Ok(self.extract().unwrap())
262 }
263
264 /// Get a reference to the transformed value, returning `Some(&U)` if the
265 /// `LazyTransform<T, U>` has been transformed or `None` if it has not. It
266 /// is guaranteed that if a reference is returned it is to the transformed
267 /// value inside the the `LazyTransform<T>`.
268 pub fn get(&self) -> Option<&U> {
269 if self.initialized.load(Ordering::Acquire) {
270 // We're initialized, our value is immutable, no synchronization needed.
271 self.extract()
272 } else {
273 None
274 }
275 }
276}
277
278// As `T` is only ever accessed when locked, it's enough if it's `Send` for `Self` to be `Sync`.
279unsafe impl<T, U> Sync for LazyTransform<T, U>
280where
281 T: Send,
282 U: Send + Sync,
283{
284}
285
286impl<T, U> Clone for LazyTransform<T, U>
287where
288 T: Clone,
289 U: Clone,
290{
291 fn clone(&self) -> Self {
292 // Overall, this method is very similar to `get_or_create` and uses the same
293 // soundness reasoning.
294
295 if self.initialized.load(Ordering::Acquire) {
296 Self {
297 initialized: true.into(),
298 lock: Mutex::default(),
299 value: UnsafeCell::new(unsafe {
300 // SAFETY:
301 // Everything is initialized and immutable here, so lockless cloning is safe.
302 (&*self.value.get()).clone()
303 }),
304 }
305 } else {
306 // We *may* not be initialized. We have to block here before accessing `value`,
307 // which also synchronises the `initialized` load.
308 let _lock = self.lock.lock().unwrap();
309 Self {
310 initialized: self.initialized.load(Ordering::Relaxed).into(),
311 lock: Mutex::default(),
312 value: UnsafeCell::new(unsafe {
313 // SAFETY:
314 // Exclusive access while `_lock` is held.
315 (&*self.value.get()).clone()
316 }),
317 }
318 }
319 }
320
321 fn clone_from(&mut self, source: &Self) {
322 // Overall, this method is very similar to `get_or_create` and uses the same
323 // soundness reasoning. It's implemented explicitly here to avoid a `Mutex` drop/new.
324
325 if self.initialized.load(Ordering::Acquire) {
326 unsafe {
327 // SAFETY:
328 // Everything is initialized and immutable here, so lockless cloning is safe.
329 // It's still important to store `initialized` with correct ordering, though.
330 *self.value.get() = (&*source.value.get()).clone();
331 self.initialized.store(true, Ordering::Release);
332 }
333 } else {
334 // `source` *may* not be initialized. We have to block here before accessing `value`,
335 // which also synchronises the `initialized` load (and incidentally also the `initialized`
336 // store due to the exclusive reference to `self`, so that can be `Relaxed` here too).
337 let _lock = source.lock.lock().unwrap();
338 unsafe {
339 // SAFETY:
340 // Exclusive access to `source` while `_lock` is held.
341 *self.value.get() = (&*source.value.get()).clone();
342 self.initialized.store(
343 source.initialized.load(Ordering::Relaxed),
344 Ordering::Relaxed,
345 );
346 }
347 }
348 }
349}
350
351impl<T, U> Default for LazyTransform<T, U>
352where
353 T: Default,
354{
355 fn default() -> Self {
356 LazyTransform::new(T::default())
357 }
358}
359
360/// `Lazy<T>` is a lazily initialized synchronized holder type. You can think
361/// of it as a `LazyTransform` where the initial type doesn't exist.
362#[derive(Clone)]
363pub struct Lazy<T> {
364 inner: LazyTransform<(), T>,
365}
366
367impl<T> Lazy<T> {
368 /// Construct a new, uninitialized `Lazy<T>`.
369 #[must_use]
370 pub fn new() -> Lazy<T> {
371 Self::default()
372 }
373
374 /// Unwrap the contained value, returning `Some` if the `Lazy<T>` has been initialized
375 /// or `None` if it has not.
376 pub fn into_inner(self) -> Option<T> {
377 self.inner.into_inner().ok()
378 }
379}
380
381impl<T> Lazy<T> {
382 /// Get a reference to the contained value, invoking `f` to create it
383 /// if the `Lazy<T>` is uninitialized. It is guaranteed that if multiple
384 /// calls to `get_or_create` race, only one will invoke its closure, and
385 /// every call will receive a reference to the newly created value.
386 ///
387 /// The value stored in the `Lazy<T>` is immutable after the closure returns
388 /// it, so think carefully about what you want to put inside!
389 pub fn get_or_create<F>(&self, f: F) -> &T
390 where
391 F: FnOnce() -> T,
392 {
393 self.inner.get_or_create(|_| f())
394 }
395
396 /// Tries to get a reference to the contained value, invoking `f` to create it
397 /// if the `Lazy<T>` is uninitialized. It is guaranteed that if multiple
398 /// calls to `get_or_create` race, only one will **successfully** invoke its
399 /// closure, and every call will receive a reference to the newly created value.
400 ///
401 /// The value stored in the `Lazy<T>` is immutable after the closure succeeds
402 /// and returns it, so think carefully about what you want to put inside!
403 ///
404 /// # Errors
405 ///
406 /// Iff `f` returns a [`Result::Err`], this error is returned verbatim.
407 pub fn try_get_or_create<F, E>(&self, f: F) -> Result<&T, E>
408 where
409 F: FnOnce() -> Result<T, E>,
410 {
411 self.inner.try_get_or_create(|_| f())
412 }
413
414 /// Get a reference to the contained value, returning `Some(ref)` if the
415 /// `Lazy<T>` has been initialized or `None` if it has not. It is
416 /// guaranteed that if a reference is returned it is to the value inside
417 /// the `Lazy<T>`.
418 pub fn get(&self) -> Option<&T> {
419 self.inner.get()
420 }
421}
422
423// `#[derive(Default)]` automatically adds `T: Default` trait bound, but that
424// is too restrictive, because `Lazy<T>` always has a default value for any `T`.
425impl<T> Default for Lazy<T> {
426 fn default() -> Self {
427 Lazy {
428 inner: LazyTransform::new(()),
429 }
430 }
431}
432
433impl<T> fmt::Debug for Lazy<T>
434where
435 T: fmt::Debug,
436{
437 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
438 if let Some(v) = self.get() {
439 f.write_fmt(format_args!("Lazy({:?})", v))
440 } else {
441 f.write_str("Lazy(<uninitialized>)")
442 }
443 }
444}
445
446#[cfg(test)]
447extern crate scoped_pool;
448
449#[cfg(test)]
450mod tests {
451
452 use super::{Lazy, LazyTransform};
453 use scoped_pool::Pool;
454 use std::{
455 sync::atomic::{AtomicUsize, Ordering},
456 thread, time,
457 };
458
459 #[test]
460 fn test_lazy() {
461 let lazy_value: Lazy<u8> = Lazy::new();
462
463 assert_eq!(lazy_value.get(), None);
464
465 let n = AtomicUsize::new(0);
466
467 let pool = Pool::new(100);
468 pool.scoped(|scope| {
469 for _ in 0..100 {
470 let lazy_ref = &lazy_value;
471 let n_ref = &n;
472 scope.execute(move || {
473 let ten_millis = time::Duration::from_millis(10);
474 thread::sleep(ten_millis);
475
476 let value = *lazy_ref.get_or_create(|| {
477 // Make everybody else wait on me, because I'm a jerk.
478 thread::sleep(ten_millis);
479
480 // Make this relaxed so it doesn't interfere with
481 // Lazy internals at all.
482 n_ref.fetch_add(1, Ordering::Relaxed);
483
484 42
485 });
486 assert_eq!(value, 42);
487
488 let value = lazy_ref.get();
489 assert_eq!(value, Some(&42));
490 });
491 }
492 });
493
494 assert_eq!(n.load(Ordering::SeqCst), 1);
495 }
496
497 #[test]
498 fn test_lazy_fallible() {
499 let lazy_value: Lazy<u8> = Lazy::new();
500
501 lazy_value.try_get_or_create(|| Err(())).unwrap_err();
502 assert_eq!(lazy_value.get(), None);
503
504 let n = AtomicUsize::new(0);
505
506 let pool = Pool::new(100);
507 pool.scoped(|scope| {
508 for _ in 0..100 {
509 let lazy_ref = &lazy_value;
510 let n_ref = &n;
511 scope.execute(move || {
512 let ten_millis = time::Duration::from_millis(10);
513 thread::sleep(ten_millis);
514
515 let value = *lazy_ref
516 .try_get_or_create(|| {
517 // Make everybody else wait on me, because I'm a jerk.
518 thread::sleep(ten_millis);
519
520 // Make this relaxed so it doesn't interfere with
521 // Lazy internals at all.
522 n_ref.fetch_add(1, Ordering::Relaxed);
523
524 Result::<_, ()>::Ok(42)
525 })
526 .unwrap();
527 assert_eq!(value, 42);
528
529 let value = lazy_ref.get();
530 assert_eq!(value, Some(&42));
531 });
532 }
533 });
534
535 assert_eq!(n.load(Ordering::SeqCst), 1);
536 }
537
538 #[test]
539 fn test_lazy_transform() {
540 let lazy_value: LazyTransform<u8, u8> = LazyTransform::new(21);
541
542 assert_eq!(lazy_value.get(), None);
543
544 let n = AtomicUsize::new(0);
545
546 let pool = Pool::new(100);
547 pool.scoped(|scope| {
548 for _ in 0..100 {
549 let lazy_ref = &lazy_value;
550 let n_ref = &n;
551 scope.execute(move || {
552 let ten_millis = time::Duration::from_millis(10);
553 thread::sleep(ten_millis);
554
555 let value = *lazy_ref.get_or_create(|v| {
556 // Make everybody else wait on me, because I'm a jerk.
557 thread::sleep(ten_millis);
558
559 // Make this relaxed so it doesn't interfere with
560 // Lazy internals at all.
561 n_ref.fetch_add(1, Ordering::Relaxed);
562
563 v * 2
564 });
565 assert_eq!(value, 42);
566
567 let value = lazy_ref.get();
568 assert_eq!(value, Some(&42));
569 });
570 }
571 });
572
573 assert_eq!(n.load(Ordering::SeqCst), 1);
574 }
575
576 #[test]
577 fn test_lazy_transform_fallible() {
578 let lazy_value: LazyTransform<u8, u8> = LazyTransform::new(21);
579
580 lazy_value.try_get_or_create(|_| Err(())).unwrap_err();
581 assert_eq!(lazy_value.get(), None);
582
583 let n = AtomicUsize::new(0);
584
585 let pool = Pool::new(100);
586 pool.scoped(|scope| {
587 for _ in 0..100 {
588 let lazy_ref = &lazy_value;
589 let n_ref = &n;
590 scope.execute(move || {
591 let ten_millis = time::Duration::from_millis(10);
592 thread::sleep(ten_millis);
593
594 let value = *lazy_ref
595 .try_get_or_create(|v| {
596 // Make everybody else wait on me, because I'm a jerk.
597 thread::sleep(ten_millis);
598
599 // Make this relaxed so it doesn't interfere with
600 // Lazy internals at all.
601 n_ref.fetch_add(1, Ordering::Relaxed);
602
603 Result::<_, ()>::Ok(v * 2)
604 })
605 .unwrap();
606 assert_eq!(value, 42);
607
608 let value = lazy_ref.get();
609 assert_eq!(value, Some(&42));
610 });
611 }
612 });
613
614 assert_eq!(n.load(Ordering::SeqCst), 1);
615 }
616}