1- use crate :: cell:: { Cell , OnceCell } ;
2- use crate :: fmt;
31use crate :: ops:: Deref ;
2+ use crate :: { fmt, mem} ;
3+
4+ use super :: UnsafeCell ;
5+
6+ enum State < T , F > {
7+ Uninit ( F ) ,
8+ Init ( T ) ,
9+ Poisoned ,
10+ }
411
512/// A value which is initialized on the first access.
613///
@@ -31,8 +38,7 @@ use crate::ops::Deref;
3138/// ```
3239#[ unstable( feature = "lazy_cell" , issue = "109736" ) ]
3340pub struct LazyCell < T , F = fn ( ) -> T > {
34- cell : OnceCell < T > ,
35- init : Cell < Option < F > > ,
41+ state : UnsafeCell < State < T , F > > ,
3642}
3743
3844impl < T , F : FnOnce ( ) -> T > LazyCell < T , F > {
@@ -53,8 +59,8 @@ impl<T, F: FnOnce() -> T> LazyCell<T, F> {
5359 /// ```
5460 #[ inline]
5561 #[ unstable( feature = "lazy_cell" , issue = "109736" ) ]
56- pub const fn new ( init : F ) -> LazyCell < T , F > {
57- LazyCell { cell : OnceCell :: new ( ) , init : Cell :: new ( Some ( init ) ) }
62+ pub const fn new ( f : F ) -> LazyCell < T , F > {
63+ LazyCell { state : UnsafeCell :: new ( State :: Uninit ( f ) ) }
5864 }
5965
6066 /// Forces the evaluation of this lazy value and returns a reference to
@@ -77,10 +83,65 @@ impl<T, F: FnOnce() -> T> LazyCell<T, F> {
7783 #[ inline]
7884 #[ unstable( feature = "lazy_cell" , issue = "109736" ) ]
7985 pub fn force ( this : & LazyCell < T , F > ) -> & T {
80- this. cell . get_or_init ( || match this. init . take ( ) {
81- Some ( f) => f ( ) ,
82- None => panic ! ( "`Lazy` instance has previously been poisoned" ) ,
83- } )
86+ // SAFETY:
87+ // This invalidates any mutable references to the data. The resulting
88+ // reference lives either until the end of the borrow of `this` (in the
89+ // initialized case) or is invalidated in `really_init` (in the
90+ // uninitialized case; `really_init` will create and return a fresh reference).
91+ let state = unsafe { & * this. state . get ( ) } ;
92+ match state {
93+ State :: Init ( data) => data,
94+ // SAFETY: The state is uninitialized.
95+ State :: Uninit ( _) => unsafe { LazyCell :: really_init ( this) } ,
96+ State :: Poisoned => panic ! ( "LazyCell has previously been poisoned" ) ,
97+ }
98+ }
99+
100+ /// # Safety
101+ /// May only be called when the state is `Uninit`.
102+ #[ cold]
103+ unsafe fn really_init ( this : & LazyCell < T , F > ) -> & T {
104+ // SAFETY:
105+ // This function is only called when the state is uninitialized,
106+ // so no references to `state` can exist except for the reference
107+ // in `force`, which is invalidated here and not accessed again.
108+ let state = unsafe { & mut * this. state . get ( ) } ;
109+ // Temporarily mark the state as poisoned. This prevents reentrant
110+ // accesses and correctly poisons the cell if the closure panicked.
111+ let State :: Uninit ( f) = mem:: replace ( state, State :: Poisoned ) else { unreachable ! ( ) } ;
112+
113+ let data = f ( ) ;
114+
115+ // SAFETY:
116+ // If the closure accessed the cell through something like a reentrant
117+ // mutex, but caught the panic resulting from the state being poisoned,
118+ // the mutable borrow for `state` will be invalidated, so we need to
119+ // go through the `UnsafeCell` pointer here. The state can only be
120+ // poisoned at this point, so using `write` to skip the destructor
121+ // of `State` should help the optimizer.
122+ unsafe { this. state . get ( ) . write ( State :: Init ( data) ) } ;
123+
124+ // SAFETY:
125+ // The previous references were invalidated by the `write` call above,
126+ // so do a new shared borrow of the state instead.
127+ let state = unsafe { & * this. state . get ( ) } ;
128+ let State :: Init ( data) = state else { unreachable ! ( ) } ;
129+ data
130+ }
131+ }
132+
133+ impl < T , F > LazyCell < T , F > {
134+ #[ inline]
135+ fn get ( & self ) -> Option < & T > {
136+ // SAFETY:
137+ // This is sound for the same reason as in `force`: once the state is
138+ // initialized, it will not be mutably accessed again, so this reference
139+ // will stay valid for the duration of the borrow to `self`.
140+ let state = unsafe { & * self . state . get ( ) } ;
141+ match state {
142+ State :: Init ( data) => Some ( data) ,
143+ _ => None ,
144+ }
84145 }
85146}
86147
@@ -105,6 +166,11 @@ impl<T: Default> Default for LazyCell<T> {
105166#[ unstable( feature = "lazy_cell" , issue = "109736" ) ]
106167impl < T : fmt:: Debug , F > fmt:: Debug for LazyCell < T , F > {
107168 fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
108- f. debug_struct ( "Lazy" ) . field ( "cell" , & self . cell ) . field ( "init" , & ".." ) . finish ( )
169+ let mut d = f. debug_tuple ( "LazyCell" ) ;
170+ match self . get ( ) {
171+ Some ( data) => d. field ( data) ,
172+ None => d. field ( & format_args ! ( "<uninit>" ) ) ,
173+ } ;
174+ d. finish ( )
109175 }
110176}
0 commit comments