Skip to content

Commit 4e14ad6

Browse files
committed
move lazy_sync helper methods to be with InterpCx
1 parent af98424 commit 4e14ad6

File tree

4 files changed

+103
-79
lines changed

4 files changed

+103
-79
lines changed

src/tools/miri/src/concurrency/sync.rs

+92-63
Original file line numberDiff line numberDiff line change
@@ -193,75 +193,104 @@ impl<'tcx> AllocExtra<'tcx> {
193193
/// If `init` is set to this, we consider the primitive initialized.
194194
pub const LAZY_INIT_COOKIE: u32 = 0xcafe_affe;
195195

196-
/// Helper for lazily initialized `alloc_extra.sync` data:
197-
/// this forces an immediate init.
198-
pub fn lazy_sync_init<'tcx, T: 'static + Copy>(
199-
ecx: &mut MiriInterpCx<'tcx>,
200-
primitive: &MPlaceTy<'tcx>,
201-
init_offset: Size,
202-
data: T,
203-
) -> InterpResult<'tcx> {
204-
let (alloc, offset, _) = ecx.ptr_get_alloc_id(primitive.ptr(), 0)?;
205-
let (alloc_extra, _machine) = ecx.get_alloc_extra_mut(alloc)?;
206-
alloc_extra.sync.insert(offset, Box::new(data));
207-
// Mark this as "initialized".
208-
let init_field = primitive.offset(init_offset, ecx.machine.layouts.u32, ecx)?;
209-
ecx.write_scalar_atomic(
210-
Scalar::from_u32(LAZY_INIT_COOKIE),
211-
&init_field,
212-
AtomicWriteOrd::Relaxed,
213-
)?;
214-
interp_ok(())
215-
}
216-
217-
/// Helper for lazily initialized `alloc_extra.sync` data:
218-
/// Checks if the primitive is initialized, and return its associated data if so.
219-
/// Otherwise, calls `new_data` to initialize the primitive.
220-
pub fn lazy_sync_get_data<'tcx, T: 'static + Copy>(
221-
ecx: &mut MiriInterpCx<'tcx>,
222-
primitive: &MPlaceTy<'tcx>,
223-
init_offset: Size,
224-
name: &str,
225-
new_data: impl FnOnce(&mut MiriInterpCx<'tcx>) -> InterpResult<'tcx, T>,
226-
) -> InterpResult<'tcx, T> {
227-
// Check if this is already initialized. Needs to be atomic because we can race with another
228-
// thread initializing. Needs to be an RMW operation to ensure we read the *latest* value.
229-
// So we just try to replace MUTEX_INIT_COOKIE with itself.
230-
let init_cookie = Scalar::from_u32(LAZY_INIT_COOKIE);
231-
let init_field = primitive.offset(init_offset, ecx.machine.layouts.u32, ecx)?;
232-
let (_init, success) = ecx
233-
.atomic_compare_exchange_scalar(
234-
&init_field,
235-
&ImmTy::from_scalar(init_cookie, ecx.machine.layouts.u32),
236-
init_cookie,
237-
AtomicRwOrd::Relaxed,
238-
AtomicReadOrd::Relaxed,
239-
/* can_fail_spuriously */ false,
240-
)?
241-
.to_scalar_pair();
242-
243-
if success.to_bool()? {
244-
// If it is initialized, it must be found in the "sync primitive" table,
245-
// or else it has been moved illegally.
246-
let (alloc, offset, _) = ecx.ptr_get_alloc_id(primitive.ptr(), 0)?;
247-
let alloc_extra = ecx.get_alloc_extra(alloc)?;
248-
let data = alloc_extra
249-
.get_sync::<T>(offset)
250-
.ok_or_else(|| err_ub_format!("`{name}` can't be moved after first use"))?;
251-
interp_ok(*data)
252-
} else {
253-
let data = new_data(ecx)?;
254-
lazy_sync_init(ecx, primitive, init_offset, data)?;
255-
interp_ok(data)
256-
}
257-
}
258-
259196
// Public interface to synchronization primitives. Please note that in most
260197
// cases, the function calls are infallible and it is the client's (shim
261198
// implementation's) responsibility to detect and deal with erroneous
262199
// situations.
263200
impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
264201
pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
202+
/// Helper for lazily initialized `alloc_extra.sync` data:
203+
/// this forces an immediate init.
204+
fn lazy_sync_init<T: 'static + Copy>(
205+
&mut self,
206+
primitive: &MPlaceTy<'tcx>,
207+
init_offset: Size,
208+
data: T,
209+
) -> InterpResult<'tcx> {
210+
let this = self.eval_context_mut();
211+
212+
let (alloc, offset, _) = this.ptr_get_alloc_id(primitive.ptr(), 0)?;
213+
let (alloc_extra, _machine) = this.get_alloc_extra_mut(alloc)?;
214+
alloc_extra.sync.insert(offset, Box::new(data));
215+
// Mark this as "initialized".
216+
let init_field = primitive.offset(init_offset, this.machine.layouts.u32, this)?;
217+
this.write_scalar_atomic(
218+
Scalar::from_u32(LAZY_INIT_COOKIE),
219+
&init_field,
220+
AtomicWriteOrd::Relaxed,
221+
)?;
222+
interp_ok(())
223+
}
224+
225+
/// Helper for lazily initialized `alloc_extra.sync` data:
226+
/// Checks if the primitive is initialized, and return its associated data if so.
227+
/// Otherwise, calls `new_data` to initialize the primitive.
228+
fn lazy_sync_get_data<T: 'static + Copy>(
229+
&mut self,
230+
primitive: &MPlaceTy<'tcx>,
231+
init_offset: Size,
232+
name: &str,
233+
new_data: impl FnOnce(&mut MiriInterpCx<'tcx>) -> InterpResult<'tcx, T>,
234+
) -> InterpResult<'tcx, T> {
235+
let this = self.eval_context_mut();
236+
237+
// Check if this is already initialized. Needs to be atomic because we can race with another
238+
// thread initializing. Needs to be an RMW operation to ensure we read the *latest* value.
239+
// So we just try to replace MUTEX_INIT_COOKIE with itself.
240+
let init_cookie = Scalar::from_u32(LAZY_INIT_COOKIE);
241+
let init_field = primitive.offset(init_offset, this.machine.layouts.u32, this)?;
242+
let (_init, success) = this
243+
.atomic_compare_exchange_scalar(
244+
&init_field,
245+
&ImmTy::from_scalar(init_cookie, this.machine.layouts.u32),
246+
init_cookie,
247+
AtomicRwOrd::Relaxed,
248+
AtomicReadOrd::Relaxed,
249+
/* can_fail_spuriously */ false,
250+
)?
251+
.to_scalar_pair();
252+
253+
if success.to_bool()? {
254+
// If it is initialized, it must be found in the "sync primitive" table,
255+
// or else it has been moved illegally.
256+
let (alloc, offset, _) = this.ptr_get_alloc_id(primitive.ptr(), 0)?;
257+
let alloc_extra = this.get_alloc_extra(alloc)?;
258+
let data = alloc_extra
259+
.get_sync::<T>(offset)
260+
.ok_or_else(|| err_ub_format!("`{name}` can't be moved after first use"))?;
261+
interp_ok(*data)
262+
} else {
263+
let data = new_data(this)?;
264+
this.lazy_sync_init(primitive, init_offset, data)?;
265+
interp_ok(data)
266+
}
267+
}
268+
269+
/// Get the synchronization primitive associated with the given pointer,
270+
/// or initialize a new one.
271+
fn get_sync_or_init<'a, T: 'static>(
272+
&'a mut self,
273+
ptr: Pointer,
274+
new: impl FnOnce(&'a mut MiriMachine<'tcx>) -> InterpResult<'tcx, T>,
275+
) -> InterpResult<'tcx, &'a T>
276+
where
277+
'tcx: 'a,
278+
{
279+
let this = self.eval_context_mut();
280+
// Ensure there is memory behind this pointer, so that this allocation
281+
// is truly the only place where the data could be stored.
282+
this.check_ptr_access(ptr, Size::from_bytes(1), CheckInAllocMsg::InboundsTest)?;
283+
284+
let (alloc, offset, _) = this.ptr_get_alloc_id(ptr, 0)?;
285+
let (alloc_extra, machine) = this.get_alloc_extra_mut(alloc)?;
286+
// Due to borrow checker reasons, we have to do the lookup twice.
287+
if alloc_extra.get_sync::<T>(offset).is_none() {
288+
let new = new(machine)?;
289+
alloc_extra.sync.insert(offset, Box::new(new));
290+
}
291+
interp_ok(alloc_extra.get_sync::<T>(offset).unwrap())
292+
}
293+
265294
#[inline]
266295
/// Get the id of the thread that currently owns this lock.
267296
fn mutex_get_owner(&mut self, id: MutexId) -> ThreadId {

src/tools/miri/src/shims/unix/macos/sync.rs

+4-8
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,11 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
2323
let lock = this.deref_pointer(lock_ptr)?;
2424
// We store the mutex ID in the `sync` metadata. This means that when the lock is moved,
2525
// that's just implicitly creating a new lock at the new location.
26-
let (alloc, offset, _) = this.ptr_get_alloc_id(lock.ptr(), 0)?;
27-
let (alloc_extra, machine) = this.get_alloc_extra_mut(alloc)?;
28-
if let Some(data) = alloc_extra.get_sync::<MacOsUnfairLock>(offset) {
29-
interp_ok(data.id)
30-
} else {
26+
let data = this.get_sync_or_init(lock.ptr(), |machine| {
3127
let id = machine.sync.mutex_create();
32-
alloc_extra.sync.insert(offset, Box::new(MacOsUnfairLock { id }));
33-
interp_ok(id)
34-
}
28+
interp_ok(MacOsUnfairLock { id })
29+
})?;
30+
interp_ok(data.id)
3531
}
3632
}
3733

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

+6-6
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use std::sync::atomic::{AtomicBool, Ordering};
22

33
use rustc_target::abi::Size;
44

5-
use crate::concurrency::sync::{LAZY_INIT_COOKIE, lazy_sync_get_data, lazy_sync_init};
5+
use crate::concurrency::sync::LAZY_INIT_COOKIE;
66
use crate::*;
77

88
/// Do a bytewise comparison of the two places, using relaxed atomic reads. This is used to check if
@@ -176,7 +176,7 @@ fn mutex_create<'tcx>(
176176
let mutex = ecx.deref_pointer(mutex_ptr)?;
177177
let id = ecx.machine.sync.mutex_create();
178178
let data = PthreadMutex { id, kind };
179-
lazy_sync_init(ecx, &mutex, mutex_init_offset(ecx)?, data)?;
179+
ecx.lazy_sync_init(&mutex, mutex_init_offset(ecx)?, data)?;
180180
interp_ok(data)
181181
}
182182

@@ -189,7 +189,7 @@ fn mutex_get_data<'tcx, 'a>(
189189
mutex_ptr: &OpTy<'tcx>,
190190
) -> InterpResult<'tcx, PthreadMutex> {
191191
let mutex = ecx.deref_pointer(mutex_ptr)?;
192-
lazy_sync_get_data(ecx, &mutex, mutex_init_offset(ecx)?, "pthread_mutex_t", |ecx| {
192+
ecx.lazy_sync_get_data(&mutex, mutex_init_offset(ecx)?, "pthread_mutex_t", |ecx| {
193193
let kind = mutex_kind_from_static_initializer(ecx, &mutex)?;
194194
let id = ecx.machine.sync.mutex_create();
195195
interp_ok(PthreadMutex { id, kind })
@@ -261,7 +261,7 @@ fn rwlock_get_data<'tcx>(
261261
rwlock_ptr: &OpTy<'tcx>,
262262
) -> InterpResult<'tcx, PthreadRwLock> {
263263
let rwlock = ecx.deref_pointer(rwlock_ptr)?;
264-
lazy_sync_get_data(ecx, &rwlock, rwlock_init_offset(ecx)?, "pthread_rwlock_t", |ecx| {
264+
ecx.lazy_sync_get_data(&rwlock, rwlock_init_offset(ecx)?, "pthread_rwlock_t", |ecx| {
265265
if !bytewise_equal_atomic_relaxed(
266266
ecx,
267267
&rwlock,
@@ -377,7 +377,7 @@ fn cond_create<'tcx>(
377377
let cond = ecx.deref_pointer(cond_ptr)?;
378378
let id = ecx.machine.sync.condvar_create();
379379
let data = PthreadCondvar { id, clock };
380-
lazy_sync_init(ecx, &cond, cond_init_offset(ecx)?, data)?;
380+
ecx.lazy_sync_init(&cond, cond_init_offset(ecx)?, data)?;
381381
interp_ok(data)
382382
}
383383

@@ -386,7 +386,7 @@ fn cond_get_data<'tcx>(
386386
cond_ptr: &OpTy<'tcx>,
387387
) -> InterpResult<'tcx, PthreadCondvar> {
388388
let cond = ecx.deref_pointer(cond_ptr)?;
389-
lazy_sync_get_data(ecx, &cond, cond_init_offset(ecx)?, "pthread_cond_t", |ecx| {
389+
ecx.lazy_sync_get_data(&cond, cond_init_offset(ecx)?, "pthread_cond_t", |ecx| {
390390
if !bytewise_equal_atomic_relaxed(
391391
ecx,
392392
&cond,

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

+1-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ use std::time::Duration;
33
use rustc_target::abi::Size;
44

55
use crate::concurrency::init_once::InitOnceStatus;
6-
use crate::concurrency::sync::lazy_sync_get_data;
76
use crate::*;
87

98
#[derive(Copy, Clone)]
@@ -25,7 +24,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
2524
let init_once = this.deref_pointer(init_once_ptr)?;
2625
let init_offset = Size::ZERO;
2726

28-
lazy_sync_get_data(this, &init_once, init_offset, "INIT_ONCE", |this| {
27+
this.lazy_sync_get_data(&init_once, init_offset, "INIT_ONCE", |this| {
2928
// TODO: check that this is still all-zero.
3029
let id = this.machine.sync.init_once_create();
3130
interp_ok(WindowsInitOnce { id })

0 commit comments

Comments
 (0)