@@ -3,7 +3,7 @@ use std::num::NonZeroU32;
3
3
4
4
use rustc_index:: vec:: Idx ;
5
5
6
- use super :: sync:: EvalContextExtPriv ;
6
+ use super :: sync:: EvalContextExtPriv as _ ;
7
7
use super :: thread:: MachineCallback ;
8
8
use super :: vector_clock:: VClock ;
9
9
use crate :: * ;
@@ -52,6 +52,43 @@ impl<'mir, 'tcx> VisitTags for InitOnce<'mir, 'tcx> {
52
52
}
53
53
}
54
54
55
+ impl < ' mir , ' tcx : ' mir > EvalContextExtPriv < ' mir , ' tcx > for crate :: MiriInterpCx < ' mir , ' tcx > { }
56
+ trait EvalContextExtPriv < ' mir , ' tcx : ' mir > : crate :: MiriInterpCxExt < ' mir , ' tcx > {
57
+ /// Synchronize with the previous initialization attempt of an InitOnce.
58
+ #[ inline]
59
+ fn init_once_observe_attempt ( & mut self , id : InitOnceId ) {
60
+ let this = self . eval_context_mut ( ) ;
61
+ let current_thread = this. get_active_thread ( ) ;
62
+
63
+ if let Some ( data_race) = & this. machine . data_race {
64
+ data_race. validate_lock_acquire (
65
+ & this. machine . threads . sync . init_onces [ id] . data_race ,
66
+ current_thread,
67
+ ) ;
68
+ }
69
+ }
70
+
71
+ #[ inline]
72
+ fn init_once_wake_waiter (
73
+ & mut self ,
74
+ id : InitOnceId ,
75
+ waiter : InitOnceWaiter < ' mir , ' tcx > ,
76
+ ) -> InterpResult < ' tcx > {
77
+ let this = self . eval_context_mut ( ) ;
78
+ let current_thread = this. get_active_thread ( ) ;
79
+
80
+ this. unblock_thread ( waiter. thread ) ;
81
+
82
+ // Call callback, with the woken-up thread as `current`.
83
+ this. set_active_thread ( waiter. thread ) ;
84
+ this. init_once_observe_attempt ( id) ;
85
+ waiter. callback . call ( this) ?;
86
+ this. set_active_thread ( current_thread) ;
87
+
88
+ Ok ( ( ) )
89
+ }
90
+ }
91
+
55
92
impl < ' mir , ' tcx : ' mir > EvalContextExt < ' mir , ' tcx > for crate :: MiriInterpCx < ' mir , ' tcx > { }
56
93
pub trait EvalContextExt < ' mir , ' tcx : ' mir > : crate :: MiriInterpCxExt < ' mir , ' tcx > {
57
94
fn init_once_get_or_create_id (
@@ -141,20 +178,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
141
178
// Wake up everyone.
142
179
// need to take the queue to avoid having `this` be borrowed multiple times
143
180
for waiter in std:: mem:: take ( & mut init_once. waiters ) {
144
- // End of the wait happens-before woken-up thread.
145
- if let Some ( data_race) = & this. machine . data_race {
146
- data_race. validate_lock_acquire (
147
- & this. machine . threads . sync . init_onces [ id] . data_race ,
148
- waiter. thread ,
149
- ) ;
150
- }
151
-
152
- this. unblock_thread ( waiter. thread ) ;
153
-
154
- // Call callback, with the woken-up thread as `current`.
155
- this. set_active_thread ( waiter. thread ) ;
156
- waiter. callback . call ( this) ?;
157
- this. set_active_thread ( current_thread) ;
181
+ this. init_once_wake_waiter ( id, waiter) ?;
158
182
}
159
183
160
184
Ok ( ( ) )
@@ -172,33 +196,33 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
172
196
) ;
173
197
174
198
// Each complete happens-before the end of the wait
175
- // FIXME: should this really induce synchronization? If we think of it as a lock, then yes,
176
- // but the docs don't talk about such details.
177
199
if let Some ( data_race) = & this. machine . data_race {
178
200
data_race. validate_lock_release ( & mut init_once. data_race , current_thread) ;
179
201
}
180
202
181
203
// Wake up one waiting thread, so they can go ahead and try to init this.
182
204
if let Some ( waiter) = init_once. waiters . pop_front ( ) {
183
- // End of the wait happens-before woken-up thread.
184
- if let Some ( data_race) = & this. machine . data_race {
185
- data_race. validate_lock_acquire (
186
- & this. machine . threads . sync . init_onces [ id] . data_race ,
187
- waiter. thread ,
188
- ) ;
189
- }
190
-
191
- this. unblock_thread ( waiter. thread ) ;
192
-
193
- // Call callback, with the woken-up thread as `current`.
194
- this. set_active_thread ( waiter. thread ) ;
195
- waiter. callback . call ( this) ?;
196
- this. set_active_thread ( current_thread) ;
205
+ this. init_once_wake_waiter ( id, waiter) ?;
197
206
} else {
198
207
// Nobody there to take this, so go back to 'uninit'
199
208
init_once. status = InitOnceStatus :: Uninitialized ;
200
209
}
201
210
202
211
Ok ( ( ) )
203
212
}
213
+
214
+ /// Synchronize with the previous completion of an InitOnce.
215
+ /// Must only be called after checking that it is complete.
216
+ #[ inline]
217
+ fn init_once_observe_completed ( & mut self , id : InitOnceId ) {
218
+ let this = self . eval_context_mut ( ) ;
219
+
220
+ assert_eq ! (
221
+ this. init_once_status( id) ,
222
+ InitOnceStatus :: Complete ,
223
+ "observing the completion of incomplete init once"
224
+ ) ;
225
+
226
+ this. init_once_observe_attempt ( id) ;
227
+ }
204
228
}
0 commit comments