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