@@ -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,60 @@ 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 ) -> 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
+
300
355
/// Consumes the `SyncOnceCell`, returning the wrapped value. Returns
301
356
/// `None` if the cell was empty.
302
357
///
0 commit comments