Skip to content

Commit 8fe9096

Browse files
committed
Add (internal-only) SyncOnceCell::get_or_init_pin.
1 parent 9dc7f13 commit 8fe9096

File tree

1 file changed

+55
-0
lines changed

1 file changed

+55
-0
lines changed

library/std/src/lazy.rs

+55
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use crate::{
1010
mem::MaybeUninit,
1111
ops::{Deref, Drop},
1212
panic::{RefUnwindSafe, UnwindSafe},
13+
pin::Pin,
1314
sync::Once,
1415
};
1516

@@ -297,6 +298,60 @@ impl<T> SyncOnceCell<T> {
297298
Ok(unsafe { self.get_unchecked() })
298299
}
299300

301+
/// Internal-only API that gets the contents of the cell, initializing it
302+
/// in two steps with `f` and `g` if the cell was empty.
303+
///
304+
/// `f` is called to construct the value, which is then moved into the cell
305+
/// and given as a (pinned) mutable reference to `g` to finish
306+
/// initialization.
307+
///
308+
/// This allows `g` to inspect an manipulate the value after it has been
309+
/// moved into its final place in the cell, but before the cell is
310+
/// considered initialized.
311+
///
312+
/// # Panics
313+
///
314+
/// If `f` or `g` panics, the panic is propagated to the caller, and the
315+
/// cell remains uninitialized.
316+
///
317+
/// With the current implementation, if `g` panics, the value from `f` will
318+
/// not be dropped. This should probably be fixed if this is ever used for
319+
/// a type where this matters.
320+
///
321+
/// It is an error to reentrantly initialize the cell from `f`. The exact
322+
/// outcome is unspecified. Current implementation deadlocks, but this may
323+
/// be changed to a panic in the future.
324+
pub(crate) fn get_or_init_pin<F, G>(self: Pin<&Self>, f: F, g: G) -> Pin<&T>
325+
where
326+
F: FnOnce() -> T,
327+
G: FnOnce(Pin<&mut T>),
328+
{
329+
if let Some(value) = self.get_ref().get() {
330+
// SAFETY: The inner value was already initialized, and will not be
331+
// moved anymore.
332+
return unsafe { Pin::new_unchecked(value) };
333+
}
334+
335+
let slot = &self.value;
336+
337+
// Ignore poisoning from other threads
338+
// If another thread panics, then we'll be able to run our closure
339+
self.once.call_once_force(|_| {
340+
let value = f();
341+
// SAFETY: We use the Once (self.once) to guarantee unique access
342+
// to the UnsafeCell (slot).
343+
let value: &mut T = unsafe { (&mut *slot.get()).write(value) };
344+
// SAFETY: The value has been written to its final place in
345+
// self.value. We do not to move it anymore, which we promise here
346+
// with a Pin<&mut T>.
347+
g(unsafe { Pin::new_unchecked(value) });
348+
});
349+
350+
// SAFETY: The inner value has been initialized, and will not be moved
351+
// anymore.
352+
unsafe { Pin::new_unchecked(self.get_ref().get_unchecked()) }
353+
}
354+
300355
/// Consumes the `SyncOnceCell`, returning the wrapped value. Returns
301356
/// `None` if the cell was empty.
302357
///

0 commit comments

Comments
 (0)