1
- use crate :: cell:: Cell ;
1
+ use crate :: cell:: UnsafeCell ;
2
2
use crate :: fmt;
3
+ use crate :: mem:: ManuallyDrop ;
3
4
use crate :: ops:: Deref ;
4
5
use crate :: panic:: { RefUnwindSafe , UnwindSafe } ;
5
- use crate :: sync:: OnceLock ;
6
+ use crate :: sync:: Once ;
7
+
8
+ use super :: once:: ExclusiveState ;
9
+
10
+ // We use the state of a Once as discriminant value. Upon creation, the state is
11
+ // "incomplete" and `f` contains the initialization closure. In the first call to
12
+ // `call_once`, `f` is taken and run. If it succeeds, `value` is set and the state
13
+ // is changed to "complete". If it panics, the Once is poisoned, so none of the
14
+ // two fields is initialized.
15
+ union Data < T , F > {
16
+ value : ManuallyDrop < T > ,
17
+ f : ManuallyDrop < F > ,
18
+ }
6
19
7
20
/// A value which is initialized on the first access.
8
21
///
@@ -43,16 +56,17 @@ use crate::sync::OnceLock;
43
56
/// ```
44
57
#[ unstable( feature = "once_cell" , issue = "74465" ) ]
45
58
pub struct LazyLock < T , F = fn ( ) -> T > {
46
- cell : OnceLock < T > ,
47
- init : Cell < Option < F > > ,
59
+ once : Once ,
60
+ data : UnsafeCell < Data < T , F > > ,
48
61
}
62
+
49
63
impl < T , F : FnOnce ( ) -> T > LazyLock < T , F > {
50
64
/// Creates a new lazy value with the given initializing
51
65
/// function.
52
66
#[ inline]
53
67
#[ unstable( feature = "once_cell" , issue = "74465" ) ]
54
68
pub const fn new ( f : F ) -> LazyLock < T , F > {
55
- LazyLock { cell : OnceLock :: new ( ) , init : Cell :: new ( Some ( f) ) }
69
+ LazyLock { once : Once :: new ( ) , data : UnsafeCell :: new ( Data { f : ManuallyDrop :: new ( f) } ) }
56
70
}
57
71
58
72
/// Forces the evaluation of this lazy value and
@@ -74,10 +88,50 @@ impl<T, F: FnOnce() -> T> LazyLock<T, F> {
74
88
#[ inline]
75
89
#[ unstable( feature = "once_cell" , issue = "74465" ) ]
76
90
pub fn force ( this : & LazyLock < T , F > ) -> & T {
77
- this. cell . get_or_init ( || match this. init . take ( ) {
78
- Some ( f) => f ( ) ,
79
- None => panic ! ( "Lazy instance has previously been poisoned" ) ,
80
- } )
91
+ this. once . call_once ( || {
92
+ // SAFETY: `call_once` only runs this closure once, ever.
93
+ let data = unsafe { & mut * this. data . get ( ) } ;
94
+ let f = unsafe { ManuallyDrop :: take ( & mut data. f ) } ;
95
+ let value = f ( ) ;
96
+ data. value = ManuallyDrop :: new ( value) ;
97
+ } ) ;
98
+
99
+ // SAFETY:
100
+ // There are four possible scenarios:
101
+ // * the closure was called and initialized `value`.
102
+ // * the closure was called and panicked, so this point is never reached.
103
+ // * the closure was not called, but a previous call initialized `value`.
104
+ // * the closure was not called because the Once is poisoned, so this point
105
+ // is never reached.
106
+ // So `value` has definitely been initialized and will not be modified again.
107
+ unsafe { & * ( * this. data . get ( ) ) . value }
108
+ }
109
+ }
110
+
111
+ impl < T , F > LazyLock < T , F > {
112
+ /// Get the inner value if it has already been initialized.
113
+ fn get ( & self ) -> Option < & T > {
114
+ if self . once . is_completed ( ) {
115
+ // SAFETY:
116
+ // The closure has been run successfully, so `value` has been initialized
117
+ // and will not be modified again.
118
+ Some ( unsafe { & * ( * self . data . get ( ) ) . value } )
119
+ } else {
120
+ None
121
+ }
122
+ }
123
+ }
124
+
125
+ #[ unstable( feature = "once_cell" , issue = "74465" ) ]
126
+ impl < T , F > Drop for LazyLock < T , F > {
127
+ fn drop ( & mut self ) {
128
+ match self . once . state ( ) {
129
+ ExclusiveState :: Incomplete => unsafe { ManuallyDrop :: drop ( & mut self . data . get_mut ( ) . f ) } ,
130
+ ExclusiveState :: Complete => unsafe {
131
+ ManuallyDrop :: drop ( & mut self . data . get_mut ( ) . value )
132
+ } ,
133
+ ExclusiveState :: Poisoned => { }
134
+ }
81
135
}
82
136
}
83
137
@@ -103,23 +157,23 @@ impl<T: Default> Default for LazyLock<T> {
103
157
#[ unstable( feature = "once_cell" , issue = "74465" ) ]
104
158
impl < T : fmt:: Debug , F > fmt:: Debug for LazyLock < T , F > {
105
159
fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
106
- f. debug_struct ( "Lazy" ) . field ( "cell" , & self . cell ) . finish_non_exhaustive ( )
160
+ match self . get ( ) {
161
+ Some ( v) => f. debug_tuple ( "LazyLock" ) . field ( v) . finish ( ) ,
162
+ None => f. write_str ( "LazyLock(Uninit)" ) ,
163
+ }
107
164
}
108
165
}
109
166
110
167
// We never create a `&F` from a `&LazyLock<T, F>` so it is fine
111
168
// to not impl `Sync` for `F`
112
- // we do create a `&mut Option<F>` in `force`, but this is
113
- // properly synchronized, so it only happens once
114
- // so it also does not contribute to this impl.
115
169
#[ unstable( feature = "once_cell" , issue = "74465" ) ]
116
- unsafe impl < T , F : Send > Sync for LazyLock < T , F > where OnceLock < T > : Sync { }
170
+ unsafe impl < T : Sync + Send , F : Send > Sync for LazyLock < T , F > { }
117
171
// auto-derived `Send` impl is OK.
118
172
119
173
#[ unstable( feature = "once_cell" , issue = "74465" ) ]
120
- impl < T , F : UnwindSafe > RefUnwindSafe for LazyLock < T , F > where OnceLock < T > : RefUnwindSafe { }
174
+ impl < T : RefUnwindSafe + UnwindSafe , F : UnwindSafe > RefUnwindSafe for LazyLock < T , F > { }
121
175
#[ unstable( feature = "once_cell" , issue = "74465" ) ]
122
- impl < T , F : UnwindSafe > UnwindSafe for LazyLock < T , F > where OnceLock < T > : UnwindSafe { }
176
+ impl < T : UnwindSafe , F : UnwindSafe > UnwindSafe for LazyLock < T , F > { }
123
177
124
178
#[ cfg( test) ]
125
179
mod tests;
0 commit comments