Skip to content

Commit 351d46d

Browse files
committed
Auto merge of #1284 - vakaras:add-threads-cr2, r=RalfJung
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).
2 parents 7aecd70 + 48da0cf commit 351d46d

39 files changed

+1946
-272
lines changed

README.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,9 @@ in your program, and cannot run all programs:
4747
* Miri runs the program as a platform-independent interpreter, so the program
4848
has no access to most platform-specific APIs or FFI. A few APIs have been
4949
implemented (such as printing to stdout) but most have not: for example, Miri
50-
currently does not support concurrency, or SIMD, or networking.
50+
currently does not support SIMD or networking.
51+
* Miri currently does not check for data-races and most other concurrency
52+
related issues.
5153

5254
[rust]: https://www.rust-lang.org/
5355
[mir]: https://github.com/rust-lang/rfcs/blob/master/text/1211-mir.md

src/diagnostics.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ fn report_msg<'tcx, 'mir>(
139139
mut helps: Vec<String>,
140140
error: bool,
141141
) {
142-
let span = if let Some(frame) = ecx.machine.stack.last() {
142+
let span = if let Some(frame) = ecx.active_thread_stack().last() {
143143
frame.current_source_info().unwrap().span
144144
} else {
145145
DUMMY_SP
@@ -171,7 +171,7 @@ fn report_msg<'tcx, 'mir>(
171171

172172
err.emit();
173173

174-
for (i, frame) in ecx.machine.stack.iter().enumerate() {
174+
for (i, frame) in ecx.active_thread_stack().iter().enumerate() {
175175
trace!("-------------------");
176176
trace!("Frame {}", i);
177177
trace!(" return: {:?}", frame.return_place.map(|p| *p));

src/eval.rs

+15-5
Original file line numberDiff line numberDiff line change
@@ -205,14 +205,24 @@ pub fn eval_main<'tcx>(tcx: TyCtxt<'tcx>, main_id: DefId, config: MiriConfig) ->
205205
// Perform the main execution.
206206
let res: InterpResult<'_, i64> = (|| {
207207
// Main loop.
208-
while ecx.step()? {
208+
loop {
209+
match ecx.schedule()? {
210+
SchedulingAction::ExecuteStep => {
211+
assert!(ecx.step()?, "a terminated thread was scheduled for execution");
212+
}
213+
SchedulingAction::ExecuteDtors => {
214+
// This will either enable the thread again (so we go back
215+
// to `ExecuteStep`), or determine that this thread is done
216+
// for good.
217+
ecx.schedule_next_tls_dtor_for_active_thread()?;
218+
}
219+
SchedulingAction::Stop => {
220+
break;
221+
}
222+
}
209223
ecx.process_diagnostics();
210224
}
211-
// Read the return code pointer *before* we run TLS destructors, to assert
212-
// that it was written to by the time that `start` lang item returned.
213225
let return_code = ecx.read_scalar(ret_place.into())?.not_undef()?.to_machine_isize(&ecx)?;
214-
// Global destructors.
215-
ecx.run_tls_dtors()?;
216226
Ok(return_code)
217227
})();
218228

src/lib.rs

+6
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ extern crate rustc_ast;
1212
#[macro_use] extern crate rustc_middle;
1313
extern crate rustc_data_structures;
1414
extern crate rustc_hir;
15+
extern crate rustc_index;
1516
extern crate rustc_mir;
1617
extern crate rustc_span;
1718
extern crate rustc_target;
@@ -26,6 +27,7 @@ mod operator;
2627
mod range_map;
2728
mod shims;
2829
mod stacked_borrows;
30+
mod thread;
2931

3032
// Make all those symbols available in the same place as our own.
3133
pub use rustc_mir::interpret::*;
@@ -40,6 +42,7 @@ pub use crate::shims::intrinsics::EvalContextExt as IntrinsicsEvalContextExt;
4042
pub use crate::shims::os_str::EvalContextExt as OsStrEvalContextExt;
4143
pub use crate::shims::panic::{CatchUnwindData, EvalContextExt as PanicEvalContextExt};
4244
pub use crate::shims::sync::{EvalContextExt as SyncEvalContextExt};
45+
pub use crate::shims::thread::EvalContextExt as ThreadShimsEvalContextExt;
4346
pub use crate::shims::time::EvalContextExt as TimeEvalContextExt;
4447
pub use crate::shims::tls::{EvalContextExt as TlsEvalContextExt, TlsData};
4548
pub use crate::shims::EvalContextExt as ShimsEvalContextExt;
@@ -60,6 +63,9 @@ pub use crate::range_map::RangeMap;
6063
pub use crate::stacked_borrows::{
6164
EvalContextExt as StackedBorEvalContextExt, Item, Permission, PtrId, Stack, Stacks, Tag,
6265
};
66+
pub use crate::thread::{
67+
EvalContextExt as ThreadsEvalContextExt, SchedulingAction, ThreadId, ThreadManager, ThreadState,
68+
};
6369

6470
/// Insert rustc arguments at the beginning of the argument list that Miri wants to be
6571
/// set per default, for maximal validation power.

src/machine.rs

+15-9
Original file line numberDiff line numberDiff line change
@@ -251,8 +251,8 @@ pub struct Evaluator<'mir, 'tcx> {
251251
/// The "time anchor" for this machine's monotone clock (for `Instant` simulation).
252252
pub(crate) time_anchor: Instant,
253253

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

257257
/// Precomputed `TyLayout`s for primitive data types that are commonly used inside Miri.
258258
pub(crate) layouts: PrimitiveLayouts<'tcx>,
@@ -282,7 +282,7 @@ impl<'mir, 'tcx> Evaluator<'mir, 'tcx> {
282282
panic_payload: None,
283283
time_anchor: Instant::now(),
284284
layouts,
285-
stack: Vec::default(),
285+
threads: ThreadManager::default(),
286286
}
287287
}
288288
}
@@ -416,6 +416,14 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> {
416416
Ok(())
417417
}
418418

419+
fn adjust_global_const(
420+
ecx: &InterpCx<'mir, 'tcx, Self>,
421+
mut val: mir::interpret::ConstValue<'tcx>,
422+
) -> InterpResult<'tcx, mir::interpret::ConstValue<'tcx>> {
423+
ecx.remap_thread_local_alloc_ids(&mut val)?;
424+
Ok(val)
425+
}
426+
419427
fn canonical_alloc_id(mem: &Memory<'mir, 'tcx, Self>, id: AllocId) -> AllocId {
420428
let tcx = mem.tcx;
421429
// Figure out if this is an extern static, and if yes, which one.
@@ -525,18 +533,16 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> {
525533
Ok(frame.with_extra(extra))
526534
}
527535

528-
#[inline(always)]
529536
fn stack<'a>(
530-
ecx: &'a InterpCx<'mir, 'tcx, Self>,
537+
ecx: &'a InterpCx<'mir, 'tcx, Self>
531538
) -> &'a [Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>] {
532-
&ecx.machine.stack
539+
ecx.active_thread_stack()
533540
}
534541

535-
#[inline(always)]
536542
fn stack_mut<'a>(
537-
ecx: &'a mut InterpCx<'mir, 'tcx, Self>,
543+
ecx: &'a mut InterpCx<'mir, 'tcx, Self>
538544
) -> &'a mut Vec<Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>> {
539-
&mut ecx.machine.stack
545+
ecx.active_thread_stack_mut()
540546
}
541547

542548
#[inline(always)]

src/shims/foreign_items/posix.rs

+36-8
Original file line numberDiff line numberDiff line change
@@ -221,13 +221,15 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
221221
}
222222
"pthread_getspecific" => {
223223
let key = this.force_bits(this.read_scalar(args[0])?.not_undef()?, args[0].layout.size)?;
224-
let ptr = this.machine.tls.load_tls(key, this)?;
224+
let active_thread = this.get_active_thread()?;
225+
let ptr = this.machine.tls.load_tls(key, active_thread, this)?;
225226
this.write_scalar(ptr, dest)?;
226227
}
227228
"pthread_setspecific" => {
228229
let key = this.force_bits(this.read_scalar(args[0])?.not_undef()?, args[0].layout.size)?;
230+
let active_thread = this.get_active_thread()?;
229231
let new_ptr = this.read_scalar(args[1])?.not_undef()?;
230-
this.machine.tls.store_tls(key, this.test_null(new_ptr)?)?;
232+
this.machine.tls.store_tls(key, active_thread, this.test_null(new_ptr)?)?;
231233

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

294-
// Better error for attempts to create a thread
296+
// Threading
295297
"pthread_create" => {
296-
throw_unsup_format!("Miri does not support threading");
298+
assert_eq!(args.len(), 4);
299+
let result = this.pthread_create(args[0], args[1], args[2], args[3])?;
300+
this.write_scalar(Scalar::from_i32(result), dest)?;
301+
}
302+
"pthread_join" => {
303+
assert_eq!(args.len(), 2);
304+
let result = this.pthread_join(args[0], args[1])?;
305+
this.write_scalar(Scalar::from_i32(result), dest)?;
306+
}
307+
"pthread_detach" => {
308+
assert_eq!(args.len(), 1);
309+
let result = this.pthread_detach(args[0])?;
310+
this.write_scalar(Scalar::from_i32(result), dest)?;
311+
}
312+
"pthread_self" => {
313+
assert_eq!(args.len(), 0);
314+
this.pthread_self(dest)?;
315+
}
316+
"sched_yield" => {
317+
assert_eq!(args.len(), 0);
318+
let result = this.sched_yield()?;
319+
this.write_scalar(Scalar::from_i32(result), dest)?;
297320
}
298321

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

319339
// Incomplete shims that we "stub out" just to get pre-main initialization code to work.
320340
// These shims are enabled only when the caller is in the standard library.
321341
| "pthread_attr_init"
322342
| "pthread_attr_destroy"
323-
| "pthread_self"
324343
| "pthread_attr_setstacksize"
325344
| "pthread_condattr_init"
326345
| "pthread_condattr_setclock"
@@ -330,6 +349,15 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
330349
=> {
331350
this.write_null(dest)?;
332351
}
352+
"pthread_attr_getguardsize" if this.frame().instance.to_string().starts_with("std::sys::unix::")
353+
=> {
354+
let guard_size = this.deref_operand(args[1])?;
355+
let guard_size_layout = this.libc_ty_layout("size_t")?;
356+
this.write_scalar(Scalar::from_uint(crate::PAGE_SIZE, guard_size_layout.size), guard_size.into())?;
357+
358+
// Return success (`0`).
359+
this.write_null(dest)?;
360+
}
333361

334362
| "signal"
335363
| "sigaction"

src/shims/foreign_items/posix/linux.rs

+7
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
7575
this.write_null(dest)?;
7676
}
7777

78+
// Threading
79+
"prctl" => {
80+
assert_eq!(args.len(), 5);
81+
let result = this.prctl(args[0], args[1], args[2], args[3], args[4])?;
82+
this.write_scalar(Scalar::from_i32(result), dest)?;
83+
}
84+
7885
// Dynamically invoked syscalls
7986
"syscall" => {
8087
let sys_getrandom = this

src/shims/foreign_items/posix/macos.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
8282
let dtor = this.read_scalar(args[0])?.not_undef()?;
8383
let dtor = this.memory.get_fn(dtor)?.as_instance()?;
8484
let data = this.read_scalar(args[1])?.not_undef()?;
85-
this.machine.tls.set_global_dtor(dtor, data)?;
85+
let active_thread = this.get_active_thread()?;
86+
this.machine.tls.set_thread_dtor(active_thread, dtor, data)?;
8687
}
8788

8889
// Querying system information

src/shims/foreign_items/windows.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -144,13 +144,15 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
144144
}
145145
"TlsGetValue" => {
146146
let key = u128::from(this.read_scalar(args[0])?.to_u32()?);
147-
let ptr = this.machine.tls.load_tls(key, this)?;
147+
let active_thread = this.get_active_thread()?;
148+
let ptr = this.machine.tls.load_tls(key, active_thread, this)?;
148149
this.write_scalar(ptr, dest)?;
149150
}
150151
"TlsSetValue" => {
151152
let key = u128::from(this.read_scalar(args[0])?.to_u32()?);
153+
let active_thread = this.get_active_thread()?;
152154
let new_ptr = this.read_scalar(args[1])?.not_undef()?;
153-
this.machine.tls.store_tls(key, this.test_null(new_ptr)?)?;
155+
this.machine.tls.store_tls(key, active_thread, this.test_null(new_ptr)?)?;
154156

155157
// Return success (`1`).
156158
this.write_scalar(Scalar::from_i32(1), dest)?;

src/shims/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ pub mod intrinsics;
66
pub mod os_str;
77
pub mod panic;
88
pub mod sync;
9+
pub mod thread;
910
pub mod time;
1011
pub mod tls;
1112

0 commit comments

Comments
 (0)