2 A lazily initialized value for safe sharing between threads.
4 The principal type in this module is `Lazy`, which makes it easy to construct
5 values that are shared safely across multiple threads simultaneously.
10 /// A lazily initialized value that implements `Deref` for `T`.
12 /// A `Lazy` takes an initialization function and permits callers from any
13 /// thread to access the result of that initialization function in a safe
14 /// manner. In effect, this permits one-time initialization of global resources
15 /// in a (possibly) multi-threaded program.
17 /// This type and its functionality are available even when neither the `alloc`
18 /// nor the `std` features are enabled. In exchange, a `Lazy` does **not**
19 /// guarantee that the given `create` function is called at most once. It
20 /// might be called multiple times. Moreover, a call to `Lazy::get` (either
21 /// explicitly or implicitly via `Lazy`'s `Deref` impl) may block until a `T`
24 /// This is very similar to `lazy_static` or `once_cell`, except it doesn't
25 /// guarantee that the initialization function will be run once and it works
26 /// in no-alloc no-std environments. With that said, if you need stronger
27 /// guarantees or a more flexible API, then it is recommended to use either
28 /// `lazy_static` or `once_cell`.
30 /// # Warning: may use a spin lock
32 /// When this crate is compiled _without_ the `alloc` feature, then this type
33 /// may used a spin lock internally. This can have subtle effects that may
34 /// be undesirable. See [Spinlocks Considered Harmful][spinharm] for a more
35 /// thorough treatment of this topic.
37 /// [spinharm]: https://matklad.github.io/2020/01/02/spinlocks-considered-harmful.html
41 /// This type is useful for creating regexes once, and then using them from
42 /// multiple threads simultaneously without worrying about synchronization.
45 /// use regex_automata::{dfa::regex::Regex, util::lazy::Lazy, Match};
47 /// static RE: Lazy<Regex> = Lazy::new(|| Regex::new("foo[0-9]+bar").unwrap());
49 /// let expected = Some(Match::must(0, 3..14));
50 /// assert_eq!(expected, RE.find(b"zzzfoo12345barzzz"));
52 pub struct Lazy
<T
, F
= fn() -> T
>(lazy
::Lazy
<T
, F
>);
54 impl<T
, F
> Lazy
<T
, F
> {
55 /// Create a new `Lazy` value that is initialized via the given function.
57 /// The `T` type is automatically inferred from the return type of the
58 /// `create` function given.
59 pub const fn new(create
: F
) -> Lazy
<T
, F
> {
60 Lazy(lazy
::Lazy
::new(create
))
64 impl<T
, F
: Fn() -> T
> Lazy
<T
, F
> {
65 /// Return a reference to the lazily initialized value.
67 /// This routine may block if another thread is initializing a `T`.
69 /// Note that given a `x` which has type `Lazy`, this must be called via
70 /// `Lazy::get(x)` and not `x.get()`. This routine is defined this way
71 /// because `Lazy` impls `Deref` with a target of `T`.
75 /// This panics if the `create` function inside this lazy value panics.
76 /// If the panic occurred in another thread, then this routine _may_ also
77 /// panic (but is not guaranteed to do so).
78 pub fn get(this
: &Lazy
<T
, F
>) -> &T
{
83 impl<T
, F
: Fn() -> T
> core
::ops
::Deref
for Lazy
<T
, F
> {
86 fn deref(&self) -> &T
{
91 impl<T
: fmt
::Debug
, F
: Fn() -> T
> fmt
::Debug
for Lazy
<T
, F
> {
92 fn fmt(&self, f
: &mut fmt
::Formatter
) -> fmt
::Result
{
97 #[cfg(feature = "alloc")]
102 sync
::atomic
::{AtomicPtr, Ordering}
,
105 use alloc
::boxed
::Box
;
107 /// A non-std lazy initialized value.
109 /// This might run the initialization function more than once, but will
112 /// I wish I could get these semantics into the non-alloc non-std Lazy
113 /// type below, but I'm not sure how to do it. If you can do an alloc,
114 /// then the implementation becomes very simple if you don't care about
115 /// redundant work precisely because a pointer can be atomically swapped.
117 /// Perhaps making this approach work in the non-alloc non-std case
118 /// requires asking the caller for a pointer? It would make the API less
119 /// convenient I think.
120 pub(super) struct Lazy
<T
, F
> {
123 // This indicates to the compiler that this type can drop T. It's not
124 // totally clear how the absence of this marker could lead to trouble,
125 // but putting here doesn't have any downsides so we hedge until somone
126 // can from the Unsafe Working Group can tell us definitively that we
129 // See: https://github.com/BurntSushi/regex-automata/issues/30
130 owned
: PhantomData
<Box
<T
>>,
133 // SAFETY: So long as T and &T (and F and &F) can themselves be safely
134 // shared among threads, so to can a Lazy<T, _>. Namely, the Lazy API only
135 // permits accessing a &T and initialization is free of data races. So if T
136 // is thread safe, then so to is Lazy<T, _>.
138 // We specifically require that T: Send in order for Lazy<T> to be Sync.
139 // Without that requirement, it's possible to send a T from one thread to
140 // another via Lazy's destructor.
142 // It's not clear whether we need F: Send+Sync for Lazy to be Sync. But
143 // we're conservative for now and keep both.
144 unsafe impl<T
: Send
+ Sync
, F
: Send
+ Sync
> Sync
for Lazy
<T
, F
> {}
146 impl<T
, F
> Lazy
<T
, F
> {
147 /// Create a new alloc but non-std lazy value that is racily
148 /// initialized. That is, the 'create' function may be called more than
150 pub(super) const fn new(create
: F
) -> Lazy
<T
, F
> {
152 data
: AtomicPtr
::new(core
::ptr
::null_mut()),
159 impl<T
, F
: Fn() -> T
> Lazy
<T
, F
> {
160 /// Get the underlying lazy value. If it hasn't been initialized
161 /// yet, then always attempt to initialize it (even if some other
162 /// thread is initializing it) and atomically attach it to this lazy
163 /// value before returning it.
164 pub(super) fn get(&self) -> &T
{
165 if let Some(data
) = self.poll() {
168 let data
= (self.create
)();
169 let mut ptr
= Box
::into_raw(Box
::new(data
));
170 // We attempt to stuff our initialized value into our atomic
171 // pointer. Upon success, we don't need to do anything. But if
172 // someone else beat us to the punch, then we need to make sure
173 // our newly created value is dropped.
174 let result
= self.data
.compare_exchange(
175 core
::ptr
::null_mut(),
180 if let Err(old
) = result
{
181 // SAFETY: We created 'ptr' via Box::into_raw above, so turning
182 // it back into a Box via from_raw is safe.
183 drop(unsafe { Box::from_raw(ptr) }
);
186 // SAFETY: We just set the pointer above to a non-null value, even
187 // in the error case, and set it to a fully initialized value
188 // returned by 'create'.
192 /// If this lazy value has been initialized successfully, then return
193 /// that value. Otherwise return None immediately. This never attempts
194 /// to run initialization itself.
195 fn poll(&self) -> Option
<&T
> {
196 let ptr
= self.data
.load(Ordering
::Acquire
);
200 // SAFETY: We just checked that the pointer is not null. Since it's
201 // not null, it must have been fully initialized by 'get' at some
203 Some(unsafe { &*ptr }
)
207 impl<T
: fmt
::Debug
, F
: Fn() -> T
> fmt
::Debug
for Lazy
<T
, F
> {
208 fn fmt(&self, f
: &mut fmt
::Formatter
) -> fmt
::Result
{
209 f
.debug_struct("Lazy").field("data", &self.poll()).finish()
213 impl<T
, F
> Drop
for Lazy
<T
, F
> {
215 let ptr
= *self.data
.get_mut();
217 // SAFETY: We just checked that 'ptr' is not null. And since
218 // we have exclusive access, there are no races to worry about.
219 drop(unsafe { Box::from_raw(ptr) }
);
225 #[cfg(not(feature = "alloc"))]
231 panic
::{RefUnwindSafe, UnwindSafe}
,
232 sync
::atomic
::{AtomicU8, Ordering}
,
235 /// Our 'Lazy' value can be in one of three states:
237 /// * INIT is where it starts, and also ends up back here if the
238 /// 'create' routine panics.
239 /// * BUSY is where it sits while initialization is running in exactly
241 /// * DONE is where it sits after 'create' has completed and 'data' has
242 /// been fully initialized.
243 const LAZY_STATE_INIT
: u8 = 0;
244 const LAZY_STATE_BUSY
: u8 = 1;
245 const LAZY_STATE_DONE
: u8 = 2;
247 /// A non-alloc non-std lazy initialized value.
249 /// This guarantees initialization only happens once, but uses a spinlock
250 /// to block in the case of simultaneous access. Blocking occurs so that
251 /// one thread waits while another thread initializes the value.
253 /// I would much rather have the semantics of the 'alloc' Lazy type above.
254 /// Namely, that we might run the initialization function more than once,
255 /// but we never otherwise block. However, I don't know how to do that in
256 /// a non-alloc non-std context.
257 pub(super) struct Lazy
<T
, F
> {
259 create
: Cell
<Option
<F
>>,
260 data
: Cell
<MaybeUninit
<T
>>,
263 // SAFETY: So long as T and &T (and F and &F) can themselves be safely
264 // shared among threads, so to can a Lazy<T, _>. Namely, the Lazy API only
265 // permits accessing a &T and initialization is free of data races. So if T
266 // is thread safe, then so to is Lazy<T, _>.
267 unsafe impl<T
: Send
+ Sync
, F
: Send
+ Sync
> Sync
for Lazy
<T
, F
> {}
268 // A reference to a Lazy is unwind safe because we specifically take
269 // precautions to poison all accesses to a Lazy if the caller-provided
270 // 'create' function panics.
271 impl<T
: UnwindSafe
, F
: UnwindSafe
+ RefUnwindSafe
> RefUnwindSafe
276 impl<T
, F
> Lazy
<T
, F
> {
277 /// Create a new non-alloc non-std lazy value that is initialized
278 /// exactly once on first use using the given function.
279 pub(super) const fn new(create
: F
) -> Lazy
<T
, F
> {
281 state
: AtomicU8
::new(LAZY_STATE_INIT
),
282 create
: Cell
::new(Some(create
)),
283 data
: Cell
::new(MaybeUninit
::uninit()),
288 impl<T
, F
: FnOnce() -> T
> Lazy
<T
, F
> {
289 /// Get the underlying lazy value. If it isn't been initialized
290 /// yet, then either initialize it or block until some other thread
291 /// initializes it. If the 'create' function given to Lazy::new panics
292 /// (even in another thread), then this panics too.
293 pub(super) fn get(&self) -> &T
{
294 // This is effectively a spinlock. We loop until we enter a DONE
295 // state, and if possible, initialize it ourselves. The only way
296 // we exit the loop is if 'create' panics, we initialize 'data' or
297 // some other thread initializes 'data'.
299 // Yes, I have read spinlocks considered harmful[1]. And that
300 // article is why this spinlock is only active when 'alloc' isn't
301 // enabled. I did this because I don't think there is really
302 // another choice without 'alloc', other than not providing this at
303 // all. But I think that's a big bummer.
305 // [1]: https://matklad.github.io/2020/01/02/spinlocks-considered-harmful.html
306 while self.state
.load(Ordering
::Acquire
) != LAZY_STATE_DONE
{
307 // Check if we're the first ones to get here. If so, we'll be
308 // the ones who initialize.
309 let result
= self.state
.compare_exchange(
315 // This means we saw the INIT state and nobody else can. So we
316 // must take responsibility for initializing. And by virtue of
317 // observing INIT, we have also told anyone else trying to
318 // get here that we are BUSY. If someone else sees BUSY, then
319 // they will spin until we finish initialization.
320 if let Ok(_
) = result
{
321 // Since we are guaranteed to be the only ones here, we
322 // know that 'create' is there... Unless someone else got
323 // here before us and 'create' panicked. In which case,
324 // 'self.create' is now 'None' and we forward the panic
325 // to the caller. (i.e., We implement poisoning.)
327 // SAFETY: Our use of 'self.state' guarantees that we are
328 // the only thread executing this line, and thus there are
330 let create
= unsafe {
331 (*self.create
.as_ptr()).take().expect(
332 "Lazy's create function panicked, \
333 preventing initialization,
334 poisoning current thread",
337 let guard
= Guard { state: &self.state }
;
338 // SAFETY: Our use of 'self.state' guarantees that we are
339 // the only thread executing this line, and thus there are
342 (*self.data
.as_ptr()).as_mut_ptr().write(create());
344 // All is well. 'self.create' ran successfully, so we
346 core
::mem
::forget(guard
);
347 // Everything is initialized, so we can declare success.
348 self.state
.store(LAZY_STATE_DONE
, Ordering
::Release
);
351 core
::hint
::spin_loop();
353 // We only get here if data is fully initialized, and thus poll
354 // will always return something.
358 /// If this lazy value has been initialized successfully, then return
359 /// that value. Otherwise return None immediately. This never blocks.
360 fn poll(&self) -> Option
<&T
> {
361 if self.state
.load(Ordering
::Acquire
) == LAZY_STATE_DONE
{
362 // SAFETY: The DONE state only occurs when data has been fully
364 Some(unsafe { &*(*self.data.as_ptr()).as_ptr() }
)
371 impl<T
: fmt
::Debug
, F
: FnMut() -> T
> fmt
::Debug
for Lazy
<T
, F
> {
372 fn fmt(&self, f
: &mut fmt
::Formatter
) -> fmt
::Result
{
373 f
.debug_struct("Lazy")
374 .field("state", &self.state
.load(Ordering
::Acquire
))
375 .field("create", &"<closure>")
376 .field("data", &self.poll())
381 impl<T
, F
> Drop
for Lazy
<T
, F
> {
383 if *self.state
.get_mut() == LAZY_STATE_DONE
{
384 // SAFETY: state is DONE if and only if data has been fully
385 // initialized. At which point, it is safe to drop.
387 self.data
.get_mut().assume_init_drop();
393 /// A guard that will reset a Lazy's state back to INIT when dropped. The
394 /// idea here is to 'forget' this guard on success. On failure (when a
395 /// panic occurs), the Drop impl runs and causes all in-progress and future
396 /// 'get' calls to panic. Without this guard, all in-progress and future
397 /// 'get' calls would spin forever. Crashing is much better than getting
398 /// stuck in an infinite loop.
403 impl<'a
> Drop
for Guard
<'a
> {
405 // We force ourselves back into an INIT state. This will in turn
406 // cause any future 'get' calls to attempt calling 'self.create'
407 // again which will in turn panic because 'self.create' will now
409 self.state
.store(LAZY_STATE_INIT
, Ordering
::Release
);
418 fn assert_send
<T
: Send
>() {}
419 fn assert_sync
<T
: Sync
>() {}
420 fn assert_unwind
<T
: core
::panic
::UnwindSafe
>() {}
421 fn assert_refunwind
<T
: core
::panic
::RefUnwindSafe
>() {}
425 assert_send
::<Lazy
<u64>>();
426 assert_sync
::<Lazy
<u64>>();
427 assert_unwind
::<Lazy
<u64>>();
428 assert_refunwind
::<Lazy
<u64>>();
431 // This is a regression test because we used to rely on the inferred Sync
432 // impl for the Lazy type defined above (for 'alloc' mode). In the
433 // inferred impl, it only requires that T: Sync for Lazy<T>: Sync. But
434 // if we have that, we can actually make use of the fact that Lazy<T> drops
435 // T to create a value on one thread and drop it on another. This *should*
436 // require T: Send, but our missing bounds before let it sneak by.
438 // Basically, this test should not compile, so we... comment it out. We
439 // don't have a great way of testing compile-fail tests right now.
441 // See: https://github.com/BurntSushi/regex-automata/issues/30
446 fn inner<T: Sync + Default>() {
447 let lazy = Lazy::new(move || T::default());
448 std::thread::scope(|scope| {
450 Lazy::get(&lazy); // We create T in this thread
453 // And drop in this thread.
455 // So we have send a !Send type over threads. (with some more
456 // legwork, its possible to even sneak the value out of drop
457 // through thread local)