@@ -3,7 +3,7 @@ use std::num::NonZeroU32;
33
44use rustc_index:: vec:: Idx ;
55
6- use super :: sync:: EvalContextExtPriv ;
6+ use super :: sync:: EvalContextExtPriv as _ ;
77use super :: thread:: MachineCallback ;
88use super :: vector_clock:: VClock ;
99use crate :: * ;
@@ -52,6 +52,43 @@ impl<'mir, 'tcx> VisitTags for InitOnce<'mir, 'tcx> {
5252 }
5353}
5454
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+
5592impl < ' mir , ' tcx : ' mir > EvalContextExt < ' mir , ' tcx > for crate :: MiriInterpCx < ' mir , ' tcx > { }
5693pub trait EvalContextExt < ' mir , ' tcx : ' mir > : crate :: MiriInterpCxExt < ' mir , ' tcx > {
5794 fn init_once_get_or_create_id (
@@ -141,20 +178,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
141178 // Wake up everyone.
142179 // need to take the queue to avoid having `this` be borrowed multiple times
143180 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) ?;
158182 }
159183
160184 Ok ( ( ) )
@@ -172,33 +196,33 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
172196 ) ;
173197
174198 // 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.
177199 if let Some ( data_race) = & this. machine . data_race {
178200 data_race. validate_lock_release ( & mut init_once. data_race , current_thread) ;
179201 }
180202
181203 // Wake up one waiting thread, so they can go ahead and try to init this.
182204 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) ?;
197206 } else {
198207 // Nobody there to take this, so go back to 'uninit'
199208 init_once. status = InitOnceStatus :: Uninitialized ;
200209 }
201210
202211 Ok ( ( ) )
203212 }
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+ }
204228}
0 commit comments