Skip to content

Commit 224dff4

Browse files
committed
add acquire when init once is already complete
1 parent c162fd3 commit 224dff4

File tree

3 files changed

+59
-20
lines changed

3 files changed

+59
-20
lines changed

src/tools/miri/src/concurrency/init_once.rs

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -141,18 +141,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
141141
// Wake up everyone.
142142
// need to take the queue to avoid having `this` be borrowed multiple times
143143
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-
152144
this.unblock_thread(waiter.thread);
153145

154146
// Call callback, with the woken-up thread as `current`.
155147
this.set_active_thread(waiter.thread);
148+
this.init_once_acquire(id);
156149
waiter.callback.call(this)?;
157150
this.set_active_thread(current_thread);
158151
}
@@ -172,26 +165,17 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
172165
);
173166

174167
// 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.
177168
if let Some(data_race) = &this.machine.data_race {
178169
data_race.validate_lock_release(&mut init_once.data_race, current_thread);
179170
}
180171

181172
// Wake up one waiting thread, so they can go ahead and try to init this.
182173
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-
191174
this.unblock_thread(waiter.thread);
192175

193176
// Call callback, with the woken-up thread as `current`.
194177
this.set_active_thread(waiter.thread);
178+
this.init_once_acquire(id);
195179
waiter.callback.call(this)?;
196180
this.set_active_thread(current_thread);
197181
} else {
@@ -201,4 +185,19 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
201185

202186
Ok(())
203187
}
188+
189+
/// Synchronize with the previous completion or failure of an InitOnce.
190+
/// This is required to prevent data races.
191+
#[inline]
192+
fn init_once_acquire(&mut self, id: InitOnceId) {
193+
let this = self.eval_context_mut();
194+
let current_thread = this.get_active_thread();
195+
196+
if let Some(data_race) = &this.machine.data_race {
197+
data_race.validate_lock_acquire(
198+
&this.machine.threads.sync.init_onces[id].data_race,
199+
current_thread,
200+
);
201+
}
202+
}
204203
}

src/tools/miri/src/shims/windows/sync.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -177,8 +177,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
177177
Box::new(Callback { init_once_id: id, pending_place }),
178178
)
179179
}
180-
InitOnceStatus::Complete =>
181-
this.write_scalar(this.eval_windows("c", "FALSE")?, &pending_place)?,
180+
InitOnceStatus::Complete => {
181+
this.init_once_acquire(id);
182+
this.write_scalar(this.eval_windows("c", "FALSE")?, &pending_place)?;
183+
}
182184
}
183185

184186
// This always succeeds (even if the thread is blocked, we will succeed if we ever unblock).

src/tools/miri/tests/pass/concurrency/windows_init_once.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,8 +131,46 @@ fn retry_on_fail() {
131131
waiter2.join().unwrap();
132132
}
133133

134+
fn no_data_race_after_complete() {
135+
let mut init_once = null_mut();
136+
let mut pending = 0;
137+
138+
unsafe {
139+
assert_eq!(InitOnceBeginInitialize(&mut init_once, 0, &mut pending, null_mut()), TRUE);
140+
assert_eq!(pending, TRUE);
141+
}
142+
143+
let init_once_ptr = SendPtr(&mut init_once);
144+
145+
let mut place = 0;
146+
let place_ptr = SendPtr(&mut place);
147+
148+
let reader = thread::spawn(move || unsafe {
149+
let mut pending = 0;
150+
151+
assert_eq!(InitOnceBeginInitialize(init_once_ptr.0, 0, &mut pending, null_mut()), TRUE);
152+
assert_eq!(pending, FALSE);
153+
// this should not data race
154+
place_ptr.0.read()
155+
});
156+
157+
unsafe {
158+
// this should not data race
159+
place_ptr.0.write(1);
160+
}
161+
162+
unsafe {
163+
assert_eq!(InitOnceComplete(init_once_ptr.0, 0, null_mut()), TRUE);
164+
}
165+
//println!("complete");
166+
167+
// run reader
168+
assert_eq!(reader.join().unwrap(), 1);
169+
}
170+
134171
fn main() {
135172
single_thread();
136173
block_until_complete();
137174
retry_on_fail();
175+
no_data_race_after_complete();
138176
}

0 commit comments

Comments
 (0)