Skip to content

Commit c678bd7

Browse files
committed
Add random failures to compare_exchange_weak
1 parent de0800e commit c678bd7

File tree

3 files changed

+45
-20
lines changed

3 files changed

+45
-20
lines changed

src/data_race.rs

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ use rustc_target::abi::Size;
7575

7676
use crate::{
7777
ImmTy, Immediate, InterpResult, MPlaceTy, MemPlaceMeta, MiriEvalContext, MiriEvalContextExt,
78-
OpTy, Pointer, RangeMap, ScalarMaybeUninit, Tag, ThreadId, VClock, VTimestamp,
78+
OpTy, Pointer, RangeMap, Scalar, ScalarMaybeUninit, Tag, ThreadId, VClock, VTimestamp,
7979
VectorIdx, MemoryKind, MiriMemoryKind
8080
};
8181

@@ -544,31 +544,42 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: MiriEvalContextExt<'mir, 'tcx> {
544544

545545
/// Perform an atomic compare and exchange at a given memory location.
546546
/// On success an atomic RMW operation is performed and on failure
547-
/// only an atomic read occurs.
547+
/// only an atomic read occurs. If `can_fail_spuriously` is true,
548+
/// then we treat it as a "compare_exchange_weak" operation, and
549+
/// some portion of the time fail even when the values are actually
550+
/// identical.
548551
fn atomic_compare_exchange_scalar(
549552
&mut self,
550553
place: MPlaceTy<'tcx, Tag>,
551554
expect_old: ImmTy<'tcx, Tag>,
552555
new: ScalarMaybeUninit<Tag>,
553556
success: AtomicRwOp,
554557
fail: AtomicReadOp,
558+
can_fail_spuriously: bool,
555559
) -> InterpResult<'tcx, Immediate<Tag>> {
560+
use rand::Rng as _;
556561
let this = self.eval_context_mut();
557562

558563
// Failure ordering cannot be stronger than success ordering, therefore first attempt
559564
// to read with the failure ordering and if successful then try again with the success
560565
// read ordering and write in the success case.
561566
// Read as immediate for the sake of `binary_op()`
562567
let old = this.allow_data_races_mut(|this| this.read_immediate(place.into()))?;
563-
564568
// `binary_op` will bail if either of them is not a scalar.
565569
let eq = this.overflowing_binary_op(mir::BinOp::Eq, old, expect_old)?.0;
566-
let res = Immediate::ScalarPair(old.to_scalar_or_uninit(), eq.into());
570+
// If the operation would succeed, but is "weak", fail 50% of the time.
571+
// FIXME: this is quite arbitrary.
572+
let cmpxchg_success = eq.to_bool()?
573+
&& (!can_fail_spuriously || this.memory.extra.rng.borrow_mut().gen_range(0, 2) == 0);
574+
let res = Immediate::ScalarPair(
575+
old.to_scalar_or_uninit(),
576+
Scalar::from_bool(cmpxchg_success).into(),
577+
);
567578

568579
// Update ptr depending on comparison.
569580
// if successful, perform a full rw-atomic validation
570581
// otherwise treat this as an atomic load with the fail ordering.
571-
if eq.to_bool()? {
582+
if cmpxchg_success {
572583
this.allow_data_races_mut(|this| this.write_scalar(new, place.into()))?;
573584
this.validate_atomic_rmw(place, success)?;
574585
} else {

src/shims/intrinsics.rs

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -518,9 +518,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
518518
Ok(())
519519
}
520520

521-
fn atomic_compare_exchange(
521+
fn atomic_compare_exchange_impl(
522522
&mut self, args: &[OpTy<'tcx, Tag>], dest: PlaceTy<'tcx, Tag>,
523-
success: AtomicRwOp, fail: AtomicReadOp
523+
success: AtomicRwOp, fail: AtomicReadOp, can_fail_spuriously: bool
524524
) -> InterpResult<'tcx> {
525525
let this = self.eval_context_mut();
526526

@@ -538,22 +538,26 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
538538

539539

540540
let old = this.atomic_compare_exchange_scalar(
541-
place, expect_old, new, success, fail
541+
place, expect_old, new, success, fail, can_fail_spuriously
542542
)?;
543543

544544
// Return old value.
545545
this.write_immediate(old, dest)?;
546546
Ok(())
547547
}
548548

549-
fn atomic_compare_exchange_weak(
549+
fn atomic_compare_exchange(
550550
&mut self, args: &[OpTy<'tcx, Tag>], dest: PlaceTy<'tcx, Tag>,
551551
success: AtomicRwOp, fail: AtomicReadOp
552552
) -> InterpResult<'tcx> {
553+
self.atomic_compare_exchange_impl(args, dest, success, fail, false)
554+
}
553555

554-
// FIXME: the weak part of this is currently not modelled,
555-
// it is assumed to always succeed unconditionally.
556-
self.atomic_compare_exchange(args, dest, success, fail)
556+
fn atomic_compare_exchange_weak(
557+
&mut self, args: &[OpTy<'tcx, Tag>], dest: PlaceTy<'tcx, Tag>,
558+
success: AtomicRwOp, fail: AtomicReadOp
559+
) -> InterpResult<'tcx> {
560+
self.atomic_compare_exchange_impl(args, dest, success, fail, true)
557561
}
558562

559563
fn float_to_int_unchecked<F>(

tests/run-pass/atomic.rs

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,20 @@ fn atomic_bool() {
2424
assert_eq!(*ATOMIC.get_mut(), false);
2525
}
2626
}
27-
27+
// There isn't a trait to use to make this generic, so just use a macro
28+
macro_rules! compare_exchange_weak_loop {
29+
($atom:expr, $from:expr, $to:expr, $succ_order:expr, $fail_order:expr) => {
30+
loop {
31+
match $atom.compare_exchange_weak($from, $to, $succ_order, $fail_order) {
32+
Ok(n) => {
33+
assert_eq!(n, $from);
34+
break;
35+
}
36+
Err(n) => assert_eq!(n, $from),
37+
}
38+
}
39+
};
40+
}
2841
fn atomic_isize() {
2942
static ATOMIC: AtomicIsize = AtomicIsize::new(0);
3043

@@ -40,11 +53,11 @@ fn atomic_isize() {
4053
ATOMIC.compare_exchange(0, 1, SeqCst, SeqCst).ok();
4154

4255
ATOMIC.store(0, SeqCst);
43-
44-
assert_eq!(ATOMIC.compare_exchange_weak(0, 1, Relaxed, Relaxed), Ok(0));
56+
compare_exchange_weak_loop!(ATOMIC, 0, 1, Relaxed, Relaxed);
4557
assert_eq!(ATOMIC.compare_exchange_weak(0, 2, Acquire, Relaxed), Err(1));
4658
assert_eq!(ATOMIC.compare_exchange_weak(0, 1, Release, Relaxed), Err(1));
47-
assert_eq!(ATOMIC.compare_exchange_weak(1, 0, AcqRel, Relaxed), Ok(1));
59+
compare_exchange_weak_loop!(ATOMIC, 1, 0, AcqRel, Relaxed);
60+
assert_eq!(ATOMIC.load(Relaxed), 0);
4861
ATOMIC.compare_exchange_weak(0, 1, AcqRel, Relaxed).ok();
4962
ATOMIC.compare_exchange_weak(0, 1, SeqCst, Relaxed).ok();
5063
ATOMIC.compare_exchange_weak(0, 1, Acquire, Acquire).ok();
@@ -58,10 +71,7 @@ fn atomic_u64() {
5871

5972
ATOMIC.store(1, SeqCst);
6073
assert_eq!(ATOMIC.compare_exchange(0, 0x100, AcqRel, Acquire), Err(1));
61-
assert_eq!(
62-
ATOMIC.compare_exchange_weak(1, 0x100, AcqRel, Acquire),
63-
Ok(1)
64-
);
74+
compare_exchange_weak_loop!(ATOMIC, 1, 0x100, AcqRel, Acquire);
6575
assert_eq!(ATOMIC.load(Relaxed), 0x100);
6676
}
6777

0 commit comments

Comments
 (0)