Skip to content

Commit

Permalink
Auto merge of #1284 - vakaras:add-threads-cr2, r=RalfJung
Browse files Browse the repository at this point in the history
Implement basic support for concurrency (Linux/macos only)

Changes  (most new code is in `src/threads.rs` and `src/shims/foreign_items/posix.rs`):

1. Move the stack from `Machine` to a newly created `Thread` struct.
2. Add a `ThreadSet` struct that manages the threads.
3. Change `canonical_alloc_id` to create a unique allocation id for each thread local and thread (the responsible struct is `ThreadLocalStorage`)
4. Change the code to execute the thread local destructors immediately when a thread terminates.
5. Add the most basic round-robin scheduler.

This pull request depends on [these changes to the compiler](rust-lang/rust#70598).
  • Loading branch information
bors committed Apr 30, 2020
2 parents 7aecd70 + 48da0cf commit 351d46d
Show file tree
Hide file tree
Showing 39 changed files with 1,946 additions and 272 deletions.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,9 @@ in your program, and cannot run all programs:
* Miri runs the program as a platform-independent interpreter, so the program
has no access to most platform-specific APIs or FFI. A few APIs have been
implemented (such as printing to stdout) but most have not: for example, Miri
currently does not support concurrency, or SIMD, or networking.
currently does not support SIMD or networking.
* Miri currently does not check for data-races and most other concurrency
related issues.

[rust]: https://www.rust-lang.org/
[mir]: https://github.com/rust-lang/rfcs/blob/master/text/1211-mir.md
Expand Down
4 changes: 2 additions & 2 deletions src/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ fn report_msg<'tcx, 'mir>(
mut helps: Vec<String>,
error: bool,
) {
let span = if let Some(frame) = ecx.machine.stack.last() {
let span = if let Some(frame) = ecx.active_thread_stack().last() {
frame.current_source_info().unwrap().span
} else {
DUMMY_SP
Expand Down Expand Up @@ -171,7 +171,7 @@ fn report_msg<'tcx, 'mir>(

err.emit();

for (i, frame) in ecx.machine.stack.iter().enumerate() {
for (i, frame) in ecx.active_thread_stack().iter().enumerate() {
trace!("-------------------");
trace!("Frame {}", i);
trace!(" return: {:?}", frame.return_place.map(|p| *p));
Expand Down
20 changes: 15 additions & 5 deletions src/eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,14 +205,24 @@ pub fn eval_main<'tcx>(tcx: TyCtxt<'tcx>, main_id: DefId, config: MiriConfig) ->
// Perform the main execution.
let res: InterpResult<'_, i64> = (|| {
// Main loop.
while ecx.step()? {
loop {
match ecx.schedule()? {
SchedulingAction::ExecuteStep => {
assert!(ecx.step()?, "a terminated thread was scheduled for execution");
}
SchedulingAction::ExecuteDtors => {
// This will either enable the thread again (so we go back
// to `ExecuteStep`), or determine that this thread is done
// for good.
ecx.schedule_next_tls_dtor_for_active_thread()?;
}
SchedulingAction::Stop => {
break;
}
}
ecx.process_diagnostics();
}
// Read the return code pointer *before* we run TLS destructors, to assert
// that it was written to by the time that `start` lang item returned.
let return_code = ecx.read_scalar(ret_place.into())?.not_undef()?.to_machine_isize(&ecx)?;
// Global destructors.
ecx.run_tls_dtors()?;
Ok(return_code)
})();

Expand Down
6 changes: 6 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ extern crate rustc_ast;
#[macro_use] extern crate rustc_middle;
extern crate rustc_data_structures;
extern crate rustc_hir;
extern crate rustc_index;
extern crate rustc_mir;
extern crate rustc_span;
extern crate rustc_target;
Expand All @@ -26,6 +27,7 @@ mod operator;
mod range_map;
mod shims;
mod stacked_borrows;
mod thread;

// Make all those symbols available in the same place as our own.
pub use rustc_mir::interpret::*;
Expand All @@ -40,6 +42,7 @@ pub use crate::shims::intrinsics::EvalContextExt as IntrinsicsEvalContextExt;
pub use crate::shims::os_str::EvalContextExt as OsStrEvalContextExt;
pub use crate::shims::panic::{CatchUnwindData, EvalContextExt as PanicEvalContextExt};
pub use crate::shims::sync::{EvalContextExt as SyncEvalContextExt};
pub use crate::shims::thread::EvalContextExt as ThreadShimsEvalContextExt;
pub use crate::shims::time::EvalContextExt as TimeEvalContextExt;
pub use crate::shims::tls::{EvalContextExt as TlsEvalContextExt, TlsData};
pub use crate::shims::EvalContextExt as ShimsEvalContextExt;
Expand All @@ -60,6 +63,9 @@ pub use crate::range_map::RangeMap;
pub use crate::stacked_borrows::{
EvalContextExt as StackedBorEvalContextExt, Item, Permission, PtrId, Stack, Stacks, Tag,
};
pub use crate::thread::{
EvalContextExt as ThreadsEvalContextExt, SchedulingAction, ThreadId, ThreadManager, ThreadState,
};

/// Insert rustc arguments at the beginning of the argument list that Miri wants to be
/// set per default, for maximal validation power.
Expand Down
24 changes: 15 additions & 9 deletions src/machine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -251,8 +251,8 @@ pub struct Evaluator<'mir, 'tcx> {
/// The "time anchor" for this machine's monotone clock (for `Instant` simulation).
pub(crate) time_anchor: Instant,

/// The call stack.
pub(crate) stack: Vec<Frame<'mir, 'tcx, Tag, FrameData<'tcx>>>,
/// The set of threads.
pub(crate) threads: ThreadManager<'mir, 'tcx>,

/// Precomputed `TyLayout`s for primitive data types that are commonly used inside Miri.
pub(crate) layouts: PrimitiveLayouts<'tcx>,
Expand Down Expand Up @@ -282,7 +282,7 @@ impl<'mir, 'tcx> Evaluator<'mir, 'tcx> {
panic_payload: None,
time_anchor: Instant::now(),
layouts,
stack: Vec::default(),
threads: ThreadManager::default(),
}
}
}
Expand Down Expand Up @@ -416,6 +416,14 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> {
Ok(())
}

fn adjust_global_const(
ecx: &InterpCx<'mir, 'tcx, Self>,
mut val: mir::interpret::ConstValue<'tcx>,
) -> InterpResult<'tcx, mir::interpret::ConstValue<'tcx>> {
ecx.remap_thread_local_alloc_ids(&mut val)?;
Ok(val)
}

fn canonical_alloc_id(mem: &Memory<'mir, 'tcx, Self>, id: AllocId) -> AllocId {
let tcx = mem.tcx;
// Figure out if this is an extern static, and if yes, which one.
Expand Down Expand Up @@ -525,18 +533,16 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> {
Ok(frame.with_extra(extra))
}

#[inline(always)]
fn stack<'a>(
ecx: &'a InterpCx<'mir, 'tcx, Self>,
ecx: &'a InterpCx<'mir, 'tcx, Self>
) -> &'a [Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>] {
&ecx.machine.stack
ecx.active_thread_stack()
}

#[inline(always)]
fn stack_mut<'a>(
ecx: &'a mut InterpCx<'mir, 'tcx, Self>,
ecx: &'a mut InterpCx<'mir, 'tcx, Self>
) -> &'a mut Vec<Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>> {
&mut ecx.machine.stack
ecx.active_thread_stack_mut()
}

#[inline(always)]
Expand Down
44 changes: 36 additions & 8 deletions src/shims/foreign_items/posix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,13 +221,15 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
}
"pthread_getspecific" => {
let key = this.force_bits(this.read_scalar(args[0])?.not_undef()?, args[0].layout.size)?;
let ptr = this.machine.tls.load_tls(key, this)?;
let active_thread = this.get_active_thread()?;
let ptr = this.machine.tls.load_tls(key, active_thread, this)?;
this.write_scalar(ptr, dest)?;
}
"pthread_setspecific" => {
let key = this.force_bits(this.read_scalar(args[0])?.not_undef()?, args[0].layout.size)?;
let active_thread = this.get_active_thread()?;
let new_ptr = this.read_scalar(args[1])?.not_undef()?;
this.machine.tls.store_tls(key, this.test_null(new_ptr)?)?;
this.machine.tls.store_tls(key, active_thread, this.test_null(new_ptr)?)?;

// Return success (`0`).
this.write_null(dest)?;
Expand Down Expand Up @@ -291,9 +293,30 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
this.write_scalar(Scalar::from_i32(result), dest)?;
}

// Better error for attempts to create a thread
// Threading
"pthread_create" => {
throw_unsup_format!("Miri does not support threading");
assert_eq!(args.len(), 4);
let result = this.pthread_create(args[0], args[1], args[2], args[3])?;
this.write_scalar(Scalar::from_i32(result), dest)?;
}
"pthread_join" => {
assert_eq!(args.len(), 2);
let result = this.pthread_join(args[0], args[1])?;
this.write_scalar(Scalar::from_i32(result), dest)?;
}
"pthread_detach" => {
assert_eq!(args.len(), 1);
let result = this.pthread_detach(args[0])?;
this.write_scalar(Scalar::from_i32(result), dest)?;
}
"pthread_self" => {
assert_eq!(args.len(), 0);
this.pthread_self(dest)?;
}
"sched_yield" => {
assert_eq!(args.len(), 0);
let result = this.sched_yield()?;
this.write_scalar(Scalar::from_i32(result), dest)?;
}

// Miscellaneous
Expand All @@ -312,15 +335,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
// We do not support forking, so there is nothing to do here.
this.write_null(dest)?;
}
"sched_yield" => {
this.write_null(dest)?;
}

// Incomplete shims that we "stub out" just to get pre-main initialization code to work.
// These shims are enabled only when the caller is in the standard library.
| "pthread_attr_init"
| "pthread_attr_destroy"
| "pthread_self"
| "pthread_attr_setstacksize"
| "pthread_condattr_init"
| "pthread_condattr_setclock"
Expand All @@ -330,6 +349,15 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
=> {
this.write_null(dest)?;
}
"pthread_attr_getguardsize" if this.frame().instance.to_string().starts_with("std::sys::unix::")
=> {
let guard_size = this.deref_operand(args[1])?;
let guard_size_layout = this.libc_ty_layout("size_t")?;
this.write_scalar(Scalar::from_uint(crate::PAGE_SIZE, guard_size_layout.size), guard_size.into())?;

// Return success (`0`).
this.write_null(dest)?;
}

| "signal"
| "sigaction"
Expand Down
7 changes: 7 additions & 0 deletions src/shims/foreign_items/posix/linux.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
this.write_null(dest)?;
}

// Threading
"prctl" => {
assert_eq!(args.len(), 5);
let result = this.prctl(args[0], args[1], args[2], args[3], args[4])?;
this.write_scalar(Scalar::from_i32(result), dest)?;
}

// Dynamically invoked syscalls
"syscall" => {
let sys_getrandom = this
Expand Down
3 changes: 2 additions & 1 deletion src/shims/foreign_items/posix/macos.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
let dtor = this.read_scalar(args[0])?.not_undef()?;
let dtor = this.memory.get_fn(dtor)?.as_instance()?;
let data = this.read_scalar(args[1])?.not_undef()?;
this.machine.tls.set_global_dtor(dtor, data)?;
let active_thread = this.get_active_thread()?;
this.machine.tls.set_thread_dtor(active_thread, dtor, data)?;
}

// Querying system information
Expand Down
6 changes: 4 additions & 2 deletions src/shims/foreign_items/windows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,13 +144,15 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
}
"TlsGetValue" => {
let key = u128::from(this.read_scalar(args[0])?.to_u32()?);
let ptr = this.machine.tls.load_tls(key, this)?;
let active_thread = this.get_active_thread()?;
let ptr = this.machine.tls.load_tls(key, active_thread, this)?;
this.write_scalar(ptr, dest)?;
}
"TlsSetValue" => {
let key = u128::from(this.read_scalar(args[0])?.to_u32()?);
let active_thread = this.get_active_thread()?;
let new_ptr = this.read_scalar(args[1])?.not_undef()?;
this.machine.tls.store_tls(key, this.test_null(new_ptr)?)?;
this.machine.tls.store_tls(key, active_thread, this.test_null(new_ptr)?)?;

// Return success (`1`).
this.write_scalar(Scalar::from_i32(1), dest)?;
Expand Down
1 change: 1 addition & 0 deletions src/shims/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ pub mod intrinsics;
pub mod os_str;
pub mod panic;
pub mod sync;
pub mod thread;
pub mod time;
pub mod tls;

Expand Down
Loading

0 comments on commit 351d46d

Please sign in to comment.