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}