-
Notifications
You must be signed in to change notification settings - Fork 386
Improve yield handling and add yield-based livelock detection. #1651
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
08b7077
2d46edb
92699ea
24e2691
d89c057
f7dbd02
afe84f2
3b5bb4d
d4fc2bf
9997fb1
6c8484f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -76,7 +76,7 @@ use rustc_target::abi::Size; | |
use crate::{ | ||
ImmTy, Immediate, InterpResult, MPlaceTy, MemPlaceMeta, MiriEvalContext, MiriEvalContextExt, | ||
OpTy, Pointer, RangeMap, ScalarMaybeUninit, Tag, ThreadId, VClock, VTimestamp, | ||
VectorIdx, MemoryKind, MiriMemoryKind | ||
VectorIdx, MemoryKind, MiriMemoryKind, ThreadsEvalContextExt | ||
}; | ||
|
||
pub type AllocExtra = VClockAlloc; | ||
|
@@ -445,13 +445,13 @@ impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for MiriEvalContext<'mir, 'tcx | |
pub trait EvalContextExt<'mir, 'tcx: 'mir>: MiriEvalContextExt<'mir, 'tcx> { | ||
/// Atomic variant of read_scalar_at_offset. | ||
fn read_scalar_at_offset_atomic( | ||
&self, | ||
&mut self, | ||
op: OpTy<'tcx, Tag>, | ||
offset: u64, | ||
layout: TyAndLayout<'tcx>, | ||
atomic: AtomicReadOp, | ||
) -> InterpResult<'tcx, ScalarMaybeUninit<Tag>> { | ||
let this = self.eval_context_ref(); | ||
let this = self.eval_context_mut(); | ||
let op_place = this.deref_operand(op)?; | ||
let offset = Size::from_bytes(offset); | ||
|
||
|
@@ -482,7 +482,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: MiriEvalContextExt<'mir, 'tcx> { | |
|
||
/// Perform an atomic read operation at the memory location. | ||
fn read_scalar_atomic( | ||
&self, | ||
&mut self, | ||
place: MPlaceTy<'tcx, Tag>, | ||
atomic: AtomicReadOp, | ||
) -> InterpResult<'tcx, ScalarMaybeUninit<Tag>> { | ||
|
@@ -582,15 +582,16 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: MiriEvalContextExt<'mir, 'tcx> { | |
/// Update the data-race detector for an atomic read occurring at the | ||
/// associated memory-place and on the current thread. | ||
fn validate_atomic_load( | ||
&self, | ||
&mut self, | ||
place: MPlaceTy<'tcx, Tag>, | ||
atomic: AtomicReadOp, | ||
) -> InterpResult<'tcx> { | ||
let this = self.eval_context_ref(); | ||
let this = self.eval_context_mut(); | ||
this.validate_atomic_op( | ||
place, | ||
atomic, | ||
"Atomic Load", | ||
false, | ||
move |memory, clocks, index, atomic| { | ||
if atomic == AtomicReadOp::Relaxed { | ||
memory.load_relaxed(&mut *clocks, index) | ||
|
@@ -608,11 +609,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: MiriEvalContextExt<'mir, 'tcx> { | |
place: MPlaceTy<'tcx, Tag>, | ||
atomic: AtomicWriteOp, | ||
) -> InterpResult<'tcx> { | ||
let this = self.eval_context_ref(); | ||
let this = self.eval_context_mut(); | ||
this.validate_atomic_op( | ||
place, | ||
atomic, | ||
"Atomic Store", | ||
true, | ||
move |memory, clocks, index, atomic| { | ||
if atomic == AtomicWriteOp::Relaxed { | ||
memory.store_relaxed(clocks, index) | ||
|
@@ -633,8 +635,15 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: MiriEvalContextExt<'mir, 'tcx> { | |
use AtomicRwOp::*; | ||
let acquire = matches!(atomic, Acquire | AcqRel | SeqCst); | ||
let release = matches!(atomic, Release | AcqRel | SeqCst); | ||
let this = self.eval_context_ref(); | ||
this.validate_atomic_op(place, atomic, "Atomic RMW", move |memory, clocks, index, _| { | ||
let this = self.eval_context_mut(); | ||
this.validate_atomic_op( | ||
place, | ||
atomic, | ||
"Atomic RMW", | ||
// For yields the atomic write overrides all effects of the atomic read | ||
// so it is treated as an atomic write. | ||
true, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Using |
||
move |memory, clocks, index, _| { | ||
if acquire { | ||
memory.load_acquire(clocks, index)?; | ||
} else { | ||
|
@@ -961,18 +970,30 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: MiriEvalContextExt<'mir, 'tcx> { | |
/// FIXME: is this valid, or should get_raw_mut be used for | ||
/// atomic-stores/atomic-rmw? | ||
fn validate_atomic_op<A: Debug + Copy>( | ||
&self, | ||
&mut self, | ||
place: MPlaceTy<'tcx, Tag>, | ||
atomic: A, | ||
description: &str, | ||
write: bool, | ||
mut op: impl FnMut( | ||
&mut MemoryCellClocks, | ||
&mut ThreadClockSet, | ||
VectorIdx, | ||
A, | ||
) -> Result<(), DataRace>, | ||
) -> InterpResult<'tcx> { | ||
let this = self.eval_context_ref(); | ||
let this = self.eval_context_mut(); | ||
|
||
// Update yield metadata for yield based forward progress and live-lock detection. | ||
let place_ptr = place.ptr.assert_ptr(); | ||
let size = place.layout.size; | ||
if write { | ||
this.thread_yield_atomic_wake(place_ptr.alloc_id, place_ptr.offset,size); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Instead of passing |
||
} else { | ||
let alloc_size = this.memory.get_raw(place_ptr.alloc_id)?.size; | ||
this.thread_yield_atomic_watch(place_ptr.alloc_id, alloc_size, place_ptr.offset,size); | ||
} | ||
|
||
if let Some(data_race) = &this.memory.extra.data_race { | ||
if data_race.multi_threaded.get() { | ||
// Load and log the atomic operation. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -222,6 +222,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx | |
"miri_resolve_frame" => { | ||
this.handle_miri_resolve_frame(args, dest)?; | ||
} | ||
// Performs a thread yield. | ||
// Exists since a thread yield operation may not be available on a given platform. | ||
"miri_yield_thread" => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As noted before, |
||
let &[] = check_arg_count(args)?; | ||
this.yield_active_thread(); | ||
} | ||
|
||
|
||
// Standard C allocation | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
New options should come with a documentation in the README. That's also a good opportunity to try and give a short but precise description of what this does. ;)