@@ -10,6 +10,7 @@ use crate::{
10
10
mem:: MaybeUninit ,
11
11
ops:: { Deref , Drop } ,
12
12
panic:: { RefUnwindSafe , UnwindSafe } ,
13
+ pin:: Pin ,
13
14
sync:: Once ,
14
15
} ;
15
16
@@ -297,6 +298,57 @@ impl<T> SyncOnceCell<T> {
297
298
Ok ( unsafe { self . get_unchecked ( ) } )
298
299
}
299
300
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
+
300
352
/// Consumes the `SyncOnceCell`, returning the wrapped value. Returns
301
353
/// `None` if the cell was empty.
302
354
///
0 commit comments