Skip to content

Commit 63b4257

Browse files
committed
print proper error when using unsupported synchronization primitive with GenMC
1 parent aceac27 commit 63b4257

File tree

9 files changed

+52
-76
lines changed

9 files changed

+52
-76
lines changed

src/alloc_addresses/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -498,6 +498,8 @@ impl<'tcx> MiriMachine<'tcx> {
498498
// Also remember this address for future reuse.
499499
let thread = self.threads.active_thread();
500500
global_state.reuse.add_addr(rng, addr, size, align, kind, thread, || {
501+
// We already excluded GenMC above. We cannot use `self.release_clock` as
502+
// `self.alloc_addresses` is borrowed.
501503
if let Some(data_race) = self.data_race.as_vclocks_ref() {
502504
data_race.release_clock(&self.threads, |clock| clock.clone())
503505
} else {

src/concurrency/data_race.rs

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -958,16 +958,20 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> {
958958
/// with this program point.
959959
///
960960
/// The closure will only be invoked if data race handling is on.
961-
fn release_clock<R>(&self, callback: impl FnOnce(&VClock) -> R) -> Option<R> {
961+
fn release_clock<R>(
962+
&self,
963+
callback: impl FnOnce(&VClock) -> R,
964+
) -> InterpResult<'tcx, Option<R>> {
962965
let this = self.eval_context_ref();
963-
// FIXME: make this a proper error instead of ICEing the interpreter.
964-
assert!(
965-
this.machine.data_race.as_genmc_ref().is_none(),
966-
"this operation performs synchronization that is not supported in GenMC mode"
967-
);
968-
Some(
969-
this.machine.data_race.as_vclocks_ref()?.release_clock(&this.machine.threads, callback),
970-
)
966+
interp_ok(match &this.machine.data_race {
967+
GlobalDataRaceHandler::None => None,
968+
GlobalDataRaceHandler::Genmc(_genmc_ctx) =>
969+
throw_unsup_format!(
970+
"this operation performs synchronization that is not supported in GenMC mode"
971+
),
972+
GlobalDataRaceHandler::Vclocks(data_race) =>
973+
Some(data_race.release_clock(&this.machine.threads, callback)),
974+
})
971975
}
972976

973977
/// Acquire the given clock into the current thread, establishing synchronization with

src/concurrency/init_once.rs

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -96,10 +96,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
9696
init_once.status = InitOnceStatus::Complete;
9797

9898
// Each complete happens-before the end of the wait
99-
if let Some(data_race) = this.machine.data_race.as_vclocks_ref() {
100-
data_race
101-
.release_clock(&this.machine.threads, |clock| init_once.clock.clone_from(clock));
102-
}
99+
this.release_clock(|clock| init_once.clock.clone_from(clock))?;
103100

104101
// Wake up everyone.
105102
// need to take the queue to avoid having `this` be borrowed multiple times
@@ -125,10 +122,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
125122
init_once.status = InitOnceStatus::Uninitialized;
126123

127124
// Each complete happens-before the end of the wait
128-
if let Some(data_race) = this.machine.data_race.as_vclocks_ref() {
129-
data_race
130-
.release_clock(&this.machine.threads, |clock| init_once.clock.clone_from(clock));
131-
}
125+
this.release_clock(|clock| init_once.clock.clone_from(clock))?;
132126

133127
// Wake up one waiting thread, so they can go ahead and try to init this.
134128
if let Some(waiter) = init_once.waiters.pop_front() {

src/concurrency/sync.rs

Lines changed: 22 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ pub(super) trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
204204
this.mutex_enqueue_and_block(mutex_ref, Some((retval, dest)));
205205
} else {
206206
// We can have it right now!
207-
this.mutex_lock(&mutex_ref);
207+
this.mutex_lock(&mutex_ref)?;
208208
// Don't forget to write the return value.
209209
this.write_scalar(retval, &dest)?;
210210
}
@@ -338,7 +338,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
338338
}
339339

340340
/// Lock by setting the mutex owner and increasing the lock count.
341-
fn mutex_lock(&mut self, mutex_ref: &MutexRef) {
341+
fn mutex_lock(&mut self, mutex_ref: &MutexRef) -> InterpResult<'tcx> {
342342
let this = self.eval_context_mut();
343343
let thread = this.active_thread();
344344
let mut mutex = mutex_ref.0.borrow_mut();
@@ -352,9 +352,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
352352
mutex.owner = Some(thread);
353353
}
354354
mutex.lock_count = mutex.lock_count.strict_add(1);
355-
if let Some(data_race) = this.machine.data_race.as_vclocks_ref() {
356-
data_race.acquire_clock(&mutex.clock, &this.machine.threads);
357-
}
355+
this.acquire_clock(&mutex.clock)?;
356+
interp_ok(())
358357
}
359358

360359
/// Try unlocking by decreasing the lock count and returning the old lock
@@ -377,11 +376,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
377376
// The mutex is completely unlocked. Try transferring ownership
378377
// to another thread.
379378

380-
if let Some(data_race) = this.machine.data_race.as_vclocks_ref() {
381-
data_race.release_clock(&this.machine.threads, |clock| {
382-
mutex.clock.clone_from(clock)
383-
});
384-
}
379+
this.release_clock(|clock| mutex.clock.clone_from(clock))?;
385380
let thread_id = mutex.queue.pop_front();
386381
// We need to drop our mutex borrow before unblock_thread
387382
// because it will be borrowed again in the unblock callback.
@@ -425,7 +420,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
425420
assert_eq!(unblock, UnblockKind::Ready);
426421

427422
assert!(mutex_ref.owner().is_none());
428-
this.mutex_lock(&mutex_ref);
423+
this.mutex_lock(&mutex_ref)?;
429424

430425
if let Some((retval, dest)) = retval_dest {
431426
this.write_scalar(retval, &dest)?;
@@ -439,17 +434,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
439434

440435
/// Read-lock the lock by adding the `reader` the list of threads that own
441436
/// this lock.
442-
fn rwlock_reader_lock(&mut self, rwlock_ref: &RwLockRef) {
437+
fn rwlock_reader_lock(&mut self, rwlock_ref: &RwLockRef) -> InterpResult<'tcx> {
443438
let this = self.eval_context_mut();
444439
let thread = this.active_thread();
445440
trace!("rwlock_reader_lock: now also held (one more time) by {:?}", thread);
446441
let mut rwlock = rwlock_ref.0.borrow_mut();
447442
assert!(!rwlock.is_write_locked(), "the lock is write locked");
448443
let count = rwlock.readers.entry(thread).or_insert(0);
449444
*count = count.strict_add(1);
450-
if let Some(data_race) = this.machine.data_race.as_vclocks_ref() {
451-
data_race.acquire_clock(&rwlock.clock_unlocked, &this.machine.threads);
452-
}
445+
this.acquire_clock(&rwlock.clock_unlocked)?;
446+
interp_ok(())
453447
}
454448

455449
/// Try read-unlock the lock for the current threads and potentially give the lock to a new owner.
@@ -472,12 +466,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
472466
}
473467
Entry::Vacant(_) => return interp_ok(false), // we did not even own this lock
474468
}
475-
if let Some(data_race) = this.machine.data_race.as_vclocks_ref() {
476-
// Add this to the shared-release clock of all concurrent readers.
477-
data_race.release_clock(&this.machine.threads, |clock| {
478-
rwlock.clock_current_readers.join(clock)
479-
});
480-
}
469+
// Add this to the shared-release clock of all concurrent readers.
470+
this.release_clock(|clock| rwlock.clock_current_readers.join(clock))?;
481471

482472
// The thread was a reader. If the lock is not held any more, give it to a writer.
483473
if rwlock.is_locked().not() {
@@ -521,7 +511,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
521511
}
522512
|this, unblock: UnblockKind| {
523513
assert_eq!(unblock, UnblockKind::Ready);
524-
this.rwlock_reader_lock(&rwlock_ref);
514+
this.rwlock_reader_lock(&rwlock_ref)?;
525515
this.write_scalar(retval, &dest)?;
526516
interp_ok(())
527517
}
@@ -531,17 +521,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
531521

532522
/// Lock by setting the writer that owns the lock.
533523
#[inline]
534-
fn rwlock_writer_lock(&mut self, rwlock_ref: &RwLockRef) {
524+
fn rwlock_writer_lock(&mut self, rwlock_ref: &RwLockRef) -> InterpResult<'tcx> {
535525
let this = self.eval_context_mut();
536526
let thread = this.active_thread();
537527
trace!("rwlock_writer_lock: now held by {:?}", thread);
538528

539529
let mut rwlock = rwlock_ref.0.borrow_mut();
540530
assert!(!rwlock.is_locked(), "the rwlock is already locked");
541531
rwlock.writer = Some(thread);
542-
if let Some(data_race) = this.machine.data_race.as_vclocks_ref() {
543-
data_race.acquire_clock(&rwlock.clock_unlocked, &this.machine.threads);
544-
}
532+
this.acquire_clock(&rwlock.clock_unlocked)?;
533+
interp_ok(())
545534
}
546535

547536
/// Try to unlock an rwlock held by the current thread.
@@ -559,11 +548,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
559548
rwlock.writer = None;
560549
trace!("rwlock_writer_unlock: unlocked by {:?}", thread);
561550
// Record release clock for next lock holder.
562-
if let Some(data_race) = this.machine.data_race.as_vclocks_ref() {
563-
data_race.release_clock(&this.machine.threads, |clock| {
564-
rwlock.clock_unlocked.clone_from(clock)
565-
});
566-
}
551+
this.release_clock(|clock| rwlock.clock_unlocked.clone_from(clock))?;
567552

568553
// The thread was a writer.
569554
//
@@ -613,7 +598,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
613598
}
614599
|this, unblock: UnblockKind| {
615600
assert_eq!(unblock, UnblockKind::Ready);
616-
this.rwlock_writer_lock(&rwlock_ref);
601+
this.rwlock_writer_lock(&rwlock_ref)?;
617602
this.write_scalar(retval, &dest)?;
618603
interp_ok(())
619604
}
@@ -663,12 +648,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
663648
match unblock {
664649
UnblockKind::Ready => {
665650
// The condvar was signaled. Make sure we get the clock for that.
666-
if let Some(data_race) = this.machine.data_race.as_vclocks_ref() {
667-
data_race.acquire_clock(
651+
this.acquire_clock(
668652
&condvar_ref.0.borrow().clock,
669-
&this.machine.threads,
670-
);
671-
}
653+
)?;
672654
// Try to acquire the mutex.
673655
// The timeout only applies to the first wait (until the signal), not for mutex acquisition.
674656
this.condvar_reacquire_mutex(mutex_ref, retval_succ, dest)
@@ -695,9 +677,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
695677
let mut condvar = condvar_ref.0.borrow_mut();
696678

697679
// Each condvar signal happens-before the end of the condvar wake
698-
if let Some(data_race) = this.machine.data_race.as_vclocks_ref() {
699-
data_race.release_clock(&this.machine.threads, |clock| condvar.clock.clone_from(clock));
700-
}
680+
this.release_clock(|clock| condvar.clock.clone_from(clock))?;
701681
let Some(waiter) = condvar.waiters.pop_front() else {
702682
return interp_ok(false);
703683
};
@@ -736,9 +716,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
736716
UnblockKind::Ready => {
737717
let futex = futex_ref.0.borrow();
738718
// Acquire the clock of the futex.
739-
if let Some(data_race) = this.machine.data_race.as_vclocks_ref() {
740-
data_race.acquire_clock(&futex.clock, &this.machine.threads);
741-
}
719+
this.acquire_clock(&futex.clock)?;
742720
},
743721
UnblockKind::TimedOut => {
744722
// Remove the waiter from the futex.
@@ -766,9 +744,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
766744
let mut futex = futex_ref.0.borrow_mut();
767745

768746
// Each futex-wake happens-before the end of the futex wait
769-
if let Some(data_race) = this.machine.data_race.as_vclocks_ref() {
770-
data_race.release_clock(&this.machine.threads, |clock| futex.clock.clone_from(clock));
771-
}
747+
this.release_clock(|clock| futex.clock.clone_from(clock))?;
772748

773749
// Remove `count` of the threads in the queue that match any of the bits in the bitset.
774750
// We collect all of them before unblocking because the unblock callback may access the

src/shims/unix/linux_like/epoll.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -608,7 +608,7 @@ fn check_and_update_one_event_interest<'tcx>(
608608
// If we are tracking data races, remember the current clock so we can sync with it later.
609609
ecx.release_clock(|clock| {
610610
event_instance.clock.clone_from(clock);
611-
});
611+
})?;
612612
// Triggers the notification by inserting it to the ready list.
613613
ready_list.insert(epoll_key, event_instance);
614614
interp_ok(true)

src/shims/unix/linux_like/eventfd.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ fn eventfd_write<'tcx>(
199199
// Future `read` calls will synchronize with this write, so update the FD clock.
200200
ecx.release_clock(|clock| {
201201
eventfd.clock.borrow_mut().join(clock);
202-
});
202+
})?;
203203

204204
// Store new counter value.
205205
eventfd.counter.set(new_count);

src/shims/unix/macos/sync.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
296296

297297
this.mutex_enqueue_and_block(mutex_ref, None);
298298
} else {
299-
this.mutex_lock(&mutex_ref);
299+
this.mutex_lock(&mutex_ref)?;
300300
}
301301

302302
interp_ok(())
@@ -321,7 +321,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
321321
// reentrancy.
322322
this.write_scalar(Scalar::from_bool(false), dest)?;
323323
} else {
324-
this.mutex_lock(&mutex_ref);
324+
this.mutex_lock(&mutex_ref)?;
325325
this.write_scalar(Scalar::from_bool(true), dest)?;
326326
}
327327

src/shims/unix/sync.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -498,14 +498,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
498498
MutexKind::Normal => throw_machine_stop!(TerminationInfo::Deadlock),
499499
MutexKind::ErrorCheck => this.eval_libc_i32("EDEADLK"),
500500
MutexKind::Recursive => {
501-
this.mutex_lock(&mutex.mutex_ref);
501+
this.mutex_lock(&mutex.mutex_ref)?;
502502
0
503503
}
504504
}
505505
}
506506
} else {
507507
// The mutex is unlocked. Let's lock it.
508-
this.mutex_lock(&mutex.mutex_ref);
508+
this.mutex_lock(&mutex.mutex_ref)?;
509509
0
510510
};
511511
this.write_scalar(Scalar::from_i32(ret), dest)?;
@@ -525,14 +525,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
525525
MutexKind::Default | MutexKind::Normal | MutexKind::ErrorCheck =>
526526
this.eval_libc_i32("EBUSY"),
527527
MutexKind::Recursive => {
528-
this.mutex_lock(&mutex.mutex_ref);
528+
this.mutex_lock(&mutex.mutex_ref)?;
529529
0
530530
}
531531
}
532532
}
533533
} else {
534534
// The mutex is unlocked. Let's lock it.
535-
this.mutex_lock(&mutex.mutex_ref);
535+
this.mutex_lock(&mutex.mutex_ref)?;
536536
0
537537
}))
538538
}
@@ -600,7 +600,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
600600
dest.clone(),
601601
);
602602
} else {
603-
this.rwlock_reader_lock(&rwlock.rwlock_ref);
603+
this.rwlock_reader_lock(&rwlock.rwlock_ref)?;
604604
this.write_null(dest)?;
605605
}
606606

@@ -615,7 +615,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
615615
if rwlock.rwlock_ref.is_write_locked() {
616616
interp_ok(Scalar::from_i32(this.eval_libc_i32("EBUSY")))
617617
} else {
618-
this.rwlock_reader_lock(&rwlock.rwlock_ref);
618+
this.rwlock_reader_lock(&rwlock.rwlock_ref)?;
619619
interp_ok(Scalar::from_i32(0))
620620
}
621621
}
@@ -648,7 +648,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
648648
dest.clone(),
649649
);
650650
} else {
651-
this.rwlock_writer_lock(&rwlock.rwlock_ref);
651+
this.rwlock_writer_lock(&rwlock.rwlock_ref)?;
652652
this.write_null(dest)?;
653653
}
654654

@@ -663,7 +663,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
663663
if rwlock.rwlock_ref.is_locked() {
664664
interp_ok(Scalar::from_i32(this.eval_libc_i32("EBUSY")))
665665
} else {
666-
this.rwlock_writer_lock(&rwlock.rwlock_ref);
666+
this.rwlock_writer_lock(&rwlock.rwlock_ref)?;
667667
interp_ok(Scalar::from_i32(0))
668668
}
669669
}

src/shims/unix/unnamed_socket.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ fn anonsocket_write<'tcx>(
259259
// Remember this clock so `read` can synchronize with us.
260260
ecx.release_clock(|clock| {
261261
writebuf.clock.join(clock);
262-
});
262+
})?;
263263
// Do full write / partial write based on the space available.
264264
let write_size = len.min(available_space);
265265
let actual_write_size = ecx.write_to_host(&mut writebuf.buf, write_size, ptr)?.unwrap();

0 commit comments

Comments
 (0)