1
- use crate :: cell:: { Cell , OnceCell } ;
2
- use crate :: fmt;
3
1
use 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
+ }
4
11
5
12
/// A value which is initialized on the first access.
6
13
///
@@ -31,8 +38,7 @@ use crate::ops::Deref;
31
38
/// ```
32
39
#[ unstable( feature = "lazy_cell" , issue = "109736" ) ]
33
40
pub struct LazyCell < T , F = fn ( ) -> T > {
34
- cell : OnceCell < T > ,
35
- init : Cell < Option < F > > ,
41
+ state : UnsafeCell < State < T , F > > ,
36
42
}
37
43
38
44
impl < T , F : FnOnce ( ) -> T > LazyCell < T , F > {
@@ -53,8 +59,8 @@ impl<T, F: FnOnce() -> T> LazyCell<T, F> {
53
59
/// ```
54
60
#[ inline]
55
61
#[ 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 ) ) }
58
64
}
59
65
60
66
/// Forces the evaluation of this lazy value and returns a reference to
@@ -77,10 +83,65 @@ impl<T, F: FnOnce() -> T> LazyCell<T, F> {
77
83
#[ inline]
78
84
#[ unstable( feature = "lazy_cell" , issue = "109736" ) ]
79
85
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
+ }
84
145
}
85
146
}
86
147
@@ -105,6 +166,11 @@ impl<T: Default> Default for LazyCell<T> {
105
166
#[ unstable( feature = "lazy_cell" , issue = "109736" ) ]
106
167
impl < T : fmt:: Debug , F > fmt:: Debug for LazyCell < T , F > {
107
168
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 ( )
109
175
}
110
176
}
0 commit comments