Skip to content

Commit d45d3a3

Browse files
committed
make lazy_sync_get_data also take a closure to initialize if needed
1 parent 8dd5bcb commit d45d3a3

File tree

3 files changed

+32
-51
lines changed

3 files changed

+32
-51
lines changed

src/concurrency/sync.rs

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -201,12 +201,11 @@ pub fn lazy_sync_init<'tcx, T: 'static + Copy>(
201201
init_offset: Size,
202202
data: T,
203203
) -> InterpResult<'tcx> {
204-
let init_field = primitive.offset(init_offset, ecx.machine.layouts.u32, ecx)?;
205-
206204
let (alloc, offset, _) = ecx.ptr_get_alloc_id(primitive.ptr(), 0)?;
207205
let (alloc_extra, _machine) = ecx.get_alloc_extra_mut(alloc)?;
208206
alloc_extra.sync.insert(offset, Box::new(data));
209207
// Mark this as "initialized".
208+
let init_field = primitive.offset(init_offset, ecx.machine.layouts.u32, ecx)?;
210209
ecx.write_scalar_atomic(
211210
Scalar::from_u32(LAZY_INIT_COOKIE),
212211
&init_field,
@@ -217,18 +216,19 @@ pub fn lazy_sync_init<'tcx, T: 'static + Copy>(
217216

218217
/// Helper for lazily initialized `alloc_extra.sync` data:
219218
/// Checks if the primitive is initialized, and return its associated data if so.
220-
/// Otherwise, return None.
219+
/// Otherwise, calls `new_data` to initialize the primitive.
221220
pub fn lazy_sync_get_data<'tcx, T: 'static + Copy>(
222221
ecx: &mut MiriInterpCx<'tcx>,
223222
primitive: &MPlaceTy<'tcx>,
224223
init_offset: Size,
225224
name: &str,
226-
) -> InterpResult<'tcx, Option<T>> {
227-
let init_field = primitive.offset(init_offset, ecx.machine.layouts.u32, ecx)?;
225+
new_data: impl FnOnce(&mut MiriInterpCx<'tcx>) -> InterpResult<'tcx, T>,
226+
) -> InterpResult<'tcx, T> {
228227
// Check if this is already initialized. Needs to be atomic because we can race with another
229228
// thread initializing. Needs to be an RMW operation to ensure we read the *latest* value.
230229
// So we just try to replace MUTEX_INIT_COOKIE with itself.
231230
let init_cookie = Scalar::from_u32(LAZY_INIT_COOKIE);
231+
let init_field = primitive.offset(init_offset, ecx.machine.layouts.u32, ecx)?;
232232
let (_init, success) = ecx
233233
.atomic_compare_exchange_scalar(
234234
&init_field,
@@ -239,6 +239,7 @@ pub fn lazy_sync_get_data<'tcx, T: 'static + Copy>(
239239
/* can_fail_spuriously */ false,
240240
)?
241241
.to_scalar_pair();
242+
242243
if success.to_bool()? {
243244
// If it is initialized, it must be found in the "sync primitive" table,
244245
// or else it has been moved illegally.
@@ -247,9 +248,11 @@ pub fn lazy_sync_get_data<'tcx, T: 'static + Copy>(
247248
let data = alloc_extra
248249
.get_sync::<T>(offset)
249250
.ok_or_else(|| err_ub_format!("`{name}` can't be moved after first use"))?;
250-
interp_ok(Some(*data))
251+
interp_ok(*data)
251252
} else {
252-
interp_ok(None)
253+
let data = new_data(ecx)?;
254+
lazy_sync_init(ecx, primitive, init_offset, data)?;
255+
interp_ok(data)
253256
}
254257
}
255258

src/shims/unix/sync.rs

Lines changed: 12 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -190,19 +190,11 @@ fn mutex_get_data<'tcx, 'a>(
190190
mutex_ptr: &OpTy<'tcx>,
191191
) -> InterpResult<'tcx, MutexData> {
192192
let mutex = ecx.deref_pointer(mutex_ptr)?;
193-
194-
if let Some(data) =
195-
lazy_sync_get_data::<MutexData>(ecx, &mutex, mutex_init_offset(ecx)?, "pthread_mutex_t")?
196-
{
197-
interp_ok(data)
198-
} else {
199-
// Not yet initialized. This must be a static initializer, figure out the kind
200-
// from that. We don't need to worry about races since we are the interpreter
201-
// and don't let any other tread take a step.
193+
lazy_sync_get_data(ecx, &mutex, mutex_init_offset(ecx)?, "pthread_mutex_t", |ecx| {
202194
let kind = mutex_kind_from_static_initializer(ecx, &mutex)?;
203-
// And then create the mutex like this.
204-
mutex_create(ecx, mutex_ptr, kind)
205-
}
195+
let id = ecx.machine.sync.mutex_create();
196+
interp_ok(MutexData { id, kind })
197+
})
206198
}
207199

208200
/// Returns the kind of a static initializer.
@@ -271,13 +263,7 @@ fn rwlock_get_data<'tcx>(
271263
rwlock_ptr: &OpTy<'tcx>,
272264
) -> InterpResult<'tcx, RwLockData> {
273265
let rwlock = ecx.deref_pointer(rwlock_ptr)?;
274-
let init_offset = rwlock_init_offset(ecx)?;
275-
276-
if let Some(data) =
277-
lazy_sync_get_data::<RwLockData>(ecx, &rwlock, init_offset, "pthread_rwlock_t")?
278-
{
279-
interp_ok(data)
280-
} else {
266+
lazy_sync_get_data(ecx, &rwlock, rwlock_init_offset(ecx)?, "pthread_rwlock_t", |ecx| {
281267
if !bytewise_equal_atomic_relaxed(
282268
ecx,
283269
&rwlock,
@@ -286,10 +272,8 @@ fn rwlock_get_data<'tcx>(
286272
throw_unsup_format!("unsupported static initializer used for `pthread_rwlock_t`");
287273
}
288274
let id = ecx.machine.sync.rwlock_create();
289-
let data = RwLockData { id };
290-
lazy_sync_init(ecx, &rwlock, init_offset, data)?;
291-
interp_ok(data)
292-
}
275+
interp_ok(RwLockData { id })
276+
})
293277
}
294278

295279
// # pthread_condattr_t
@@ -405,21 +389,18 @@ fn cond_get_data<'tcx>(
405389
cond_ptr: &OpTy<'tcx>,
406390
) -> InterpResult<'tcx, CondData> {
407391
let cond = ecx.deref_pointer(cond_ptr)?;
408-
let init_offset = cond_init_offset(ecx)?;
409-
410-
if let Some(data) = lazy_sync_get_data::<CondData>(ecx, &cond, init_offset, "pthread_cond_t")? {
411-
interp_ok(data)
412-
} else {
413-
// This used the static initializer. The clock there is always CLOCK_REALTIME.
392+
lazy_sync_get_data(ecx, &cond, cond_init_offset(ecx)?, "pthread_cond_t", |ecx| {
414393
if !bytewise_equal_atomic_relaxed(
415394
ecx,
416395
&cond,
417396
&ecx.eval_path(&["libc", "PTHREAD_COND_INITIALIZER"]),
418397
)? {
419398
throw_unsup_format!("unsupported static initializer used for `pthread_cond_t`");
420399
}
421-
cond_create(ecx, cond_ptr, ClockId::Realtime)
422-
}
400+
// This used the static initializer. The clock there is always CLOCK_REALTIME.
401+
let id = ecx.machine.sync.condvar_create();
402+
interp_ok(CondData { id, clock: ClockId::Realtime })
403+
})
423404
}
424405

425406
impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}

src/shims/windows/sync.rs

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ 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, lazy_sync_init};
6+
use crate::concurrency::sync::lazy_sync_get_data;
77
use crate::*;
88

99
#[derive(Copy, Clone)]
@@ -16,23 +16,20 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
1616
// Windows sync primitives are pointer sized.
1717
// We only use the first 4 bytes for the id.
1818

19-
fn init_once_get_id(&mut self, init_once_ptr: &OpTy<'tcx>) -> InterpResult<'tcx, InitOnceId> {
19+
fn init_once_get_data(
20+
&mut self,
21+
init_once_ptr: &OpTy<'tcx>,
22+
) -> InterpResult<'tcx, InitOnceData> {
2023
let this = self.eval_context_mut();
2124

2225
let init_once = this.deref_pointer(init_once_ptr)?;
2326
let init_offset = Size::ZERO;
2427

25-
if let Some(data) =
26-
lazy_sync_get_data::<InitOnceData>(this, &init_once, init_offset, "INIT_ONCE")?
27-
{
28-
interp_ok(data.id)
29-
} else {
28+
lazy_sync_get_data(this, &init_once, init_offset, "INIT_ONCE", |this| {
3029
// TODO: check that this is still all-zero.
3130
let id = this.machine.sync.init_once_create();
32-
let data = InitOnceData { id };
33-
lazy_sync_init(this, &init_once, init_offset, data)?;
34-
interp_ok(id)
35-
}
31+
interp_ok(InitOnceData { id })
32+
})
3633
}
3734

3835
/// Returns `true` if we were succssful, `false` if we would block.
@@ -74,7 +71,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
7471
) -> InterpResult<'tcx> {
7572
let this = self.eval_context_mut();
7673

77-
let id = this.init_once_get_id(init_once_op)?;
74+
let id = this.init_once_get_data(init_once_op)?.id;
7875
let flags = this.read_scalar(flags_op)?.to_u32()?;
7976
let pending_place = this.deref_pointer(pending_op)?;
8077
let context = this.read_pointer(context_op)?;
@@ -120,7 +117,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
120117
) -> InterpResult<'tcx, Scalar> {
121118
let this = self.eval_context_mut();
122119

123-
let id = this.init_once_get_id(init_once_op)?;
120+
let id = this.init_once_get_data(init_once_op)?.id;
124121
let flags = this.read_scalar(flags_op)?.to_u32()?;
125122
let context = this.read_pointer(context_op)?;
126123

0 commit comments

Comments
 (0)