@@ -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,57 @@ 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 ) -> & T
325+ where
326+ F : FnOnce ( ) -> T ,
327+ G : FnOnce ( Pin < & mut T > ) ,
328+ {
329+ if let Some ( value) = self . get_ref ( ) . get ( ) {
330+ return value;
331+ }
332+
333+ let slot = & self . value ;
334+
335+ // Ignore poisoning from other threads
336+ // If another thread panics, then we'll be able to run our closure
337+ self . once . call_once_force ( |_| {
338+ let value = f ( ) ;
339+ // SAFETY: We use the Once (self.once) to guarantee unique access
340+ // to the UnsafeCell (slot).
341+ let value: & mut T = unsafe { ( & mut * slot. get ( ) ) . write ( value) } ;
342+ // SAFETY: The value has been written to its final place in
343+ // self.value. We do not to move it anymore, which we promise here
344+ // with a Pin<&mut T>.
345+ g ( unsafe { Pin :: new_unchecked ( value) } ) ;
346+ } ) ;
347+
348+ // SAFETY: The inner value has been initialized.
349+ unsafe { self . get_ref ( ) . get_unchecked ( ) }
350+ }
351+
300352 /// Consumes the `SyncOnceCell`, returning the wrapped value. Returns
301353 /// `None` if the cell was empty.
302354 ///
0 commit comments