Skip to content

Commit 57d6ac7

Browse files
committed
basic theading
1 parent 1118d94 commit 57d6ac7

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

59 files changed

+476
-165
lines changed

src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
#![feature(try_blocks)]
66
#![feature(let_else)]
77
#![feature(io_error_more)]
8+
#![feature(int_log)]
9+
#![feature(variant_count)]
810
#![feature(yeet_expr)]
911
#![feature(is_some_with)]
1012
#![feature(nonzero_ops)]

src/shims/tls.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,7 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
235235
fn schedule_windows_tls_dtors(&mut self) -> InterpResult<'tcx> {
236236
let this = self.eval_context_mut();
237237
let active_thread = this.get_active_thread();
238-
assert_eq!(this.get_total_thread_count(), 1, "concurrency on Windows is not supported");
238+
239239
// Windows has a special magic linker section that is run on certain events.
240240
// Instead of searching for that section and supporting arbitrary hooks in there
241241
// (that would be basically https://github.com/rust-lang/miri/issues/450),
@@ -252,7 +252,7 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
252252
this.get_ptr_fn(this.scalar_to_ptr(thread_callback)?)?.as_instance()?;
253253

254254
// The signature of this function is `unsafe extern "system" fn(h: c::LPVOID, dwReason: c::DWORD, pv: c::LPVOID)`.
255-
let reason = this.eval_path_scalar(&["std", "sys", "windows", "c", "DLL_THREAD_DETACH"])?;
255+
let reason = this.eval_windows("c", "DLL_THREAD_DETACH")?;
256256
this.call_function(
257257
thread_callback,
258258
Abi::System { unwind: false },

src/shims/unix/thread.rs

Lines changed: 5 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -13,47 +13,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
1313
) -> InterpResult<'tcx, i32> {
1414
let this = self.eval_context_mut();
1515

16-
// Create the new thread
17-
let new_thread_id = this.create_thread();
18-
19-
// Write the current thread-id, switch to the next thread later
20-
// to treat this write operation as occuring on the current thread.
21-
let thread_info_place = this.deref_operand(thread)?;
22-
this.write_scalar(
23-
Scalar::from_uint(new_thread_id.to_u32(), thread_info_place.layout.size),
24-
&thread_info_place.into(),
25-
)?;
26-
27-
// Read the function argument that will be sent to the new thread
28-
// before the thread starts executing since reading after the
29-
// context switch will incorrectly report a data-race.
30-
let fn_ptr = this.read_pointer(start_routine)?;
31-
let func_arg = this.read_immediate(arg)?;
32-
33-
// Finally switch to new thread so that we can push the first stackframe.
34-
// After this all accesses will be treated as occuring in the new thread.
35-
let old_thread_id = this.set_active_thread(new_thread_id);
36-
37-
// Perform the function pointer load in the new thread frame.
38-
let instance = this.get_ptr_fn(fn_ptr)?.as_instance()?;
39-
40-
// Note: the returned value is currently ignored (see the FIXME in
41-
// pthread_join below) because the Rust standard library does not use
42-
// it.
43-
let ret_place =
44-
this.allocate(this.layout_of(this.tcx.types.usize)?, MiriMemoryKind::Machine.into())?;
45-
46-
this.call_function(
47-
instance,
16+
this.start_thread(
17+
Some(thread),
18+
start_routine,
4819
Abi::C { unwind: false },
49-
&[*func_arg],
50-
Some(&ret_place.into()),
51-
StackPopCleanup::Root { cleanup: true },
20+
arg,
21+
this.layout_of(this.tcx.types.usize)?,
5222
)?;
5323

54-
// Restore the old active thread frame.
55-
this.set_active_thread(old_thread_id);
56-
5724
Ok(0)
5825
}
5926

src/shims/windows/dlsym.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@ use rustc_target::spec::abi::Abi;
55
use log::trace;
66

77
use crate::helpers::check_arg_count;
8+
use crate::shims::windows::handle::Handle;
89
use crate::*;
910

1011
#[derive(Debug, Copy, Clone)]
1112
pub enum Dlsym {
1213
NtWriteFile,
14+
SetThreadDescription,
1315
}
1416

1517
impl Dlsym {
@@ -20,6 +22,7 @@ impl Dlsym {
2022
"GetSystemTimePreciseAsFileTime" => None,
2123
"SetThreadDescription" => None,
2224
"NtWriteFile" => Some(Dlsym::NtWriteFile),
25+
"SetThreadDescription" => Some(Dlsym::SetThreadDescription),
2326
_ => throw_unsup_format!("unsupported Windows dlsym: {}", name),
2427
})
2528
}
@@ -106,6 +109,22 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
106109
dest,
107110
)?;
108111
}
112+
Dlsym::SetThreadDescription => {
113+
let [handle, name] = check_arg_count(args)?;
114+
115+
let name = this.read_wide_str(this.read_pointer(name)?)?;
116+
117+
let thread =
118+
match Handle::from_scalar(this.read_scalar(handle)?.check_init()?, this)? {
119+
Some(Handle::Thread(thread)) => thread,
120+
Some(Handle::CurrentThread) => this.get_active_thread(),
121+
_ => throw_ub_format!("invalid handle"),
122+
};
123+
124+
this.set_thread_name_wide(thread, name);
125+
126+
this.write_null(dest)?;
127+
}
109128
}
110129

111130
trace!("{:?}", this.dump_place(**dest));

src/shims/windows/foreign_items.rs

Lines changed: 61 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
11
use std::iter;
2+
use std::time::{Duration, Instant};
23

34
use rustc_middle::mir;
45
use rustc_span::Symbol;
56
use rustc_target::abi::Size;
67
use rustc_target::spec::abi::Abi;
78

9+
use crate::thread::Time;
810
use crate::*;
911
use shims::foreign_items::EmulateByNameResult;
12+
use shims::windows::handle::{EvalContextExt as _, Handle};
1013
use shims::windows::sync::EvalContextExt as _;
14+
use shims::windows::thread::EvalContextExt as _;
15+
1116
use smallvec::SmallVec;
1217

1318
impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
@@ -230,6 +235,29 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
230235
let result = this.QueryPerformanceFrequency(lpFrequency)?;
231236
this.write_scalar(Scalar::from_i32(result), dest)?;
232237
}
238+
"Sleep" => {
239+
let [timeout] =
240+
this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
241+
242+
this.check_no_isolation("`Sleep`")?;
243+
244+
let timeout_ms = this.read_scalar(timeout)?.to_u32()?;
245+
246+
let duration = Duration::from_millis(timeout_ms as u64);
247+
let timeout_time = Time::Monotonic(Instant::now().checked_add(duration).unwrap());
248+
249+
let active_thread = this.get_active_thread();
250+
this.block_thread(active_thread);
251+
252+
this.register_timeout_callback(
253+
active_thread,
254+
timeout_time,
255+
Box::new(move |ecx| {
256+
ecx.unblock_thread(active_thread);
257+
Ok(())
258+
}),
259+
);
260+
}
233261

234262
// Synchronization primitives
235263
"AcquireSRWLockExclusive" => {
@@ -325,10 +353,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
325353
// FIXME: we should set last_error, but to what?
326354
this.write_null(dest)?;
327355
}
328-
"SwitchToThread" => {
356+
// this is only callable from std because we know that std ignores the return value
357+
"SwitchToThread" if this.frame_in_std() => {
329358
let [] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
330-
// Note that once Miri supports concurrency, this will need to return a nonzero
331-
// value if this call does result in switching to another thread.
359+
360+
this.yield_active_thread();
361+
362+
// FIXME: this should return a nonzero value if this call does result in switching to another thread.
332363
this.write_null(dest)?;
333364
}
334365
"GetStdHandle" => {
@@ -340,14 +371,37 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
340371
// std-only shim.
341372
this.write_scalar(Scalar::from_machine_isize(which.into(), this), dest)?;
342373
}
374+
"CloseHandle" => {
375+
let [handle] =
376+
this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
343377

344-
// Better error for attempts to create a thread
378+
this.CloseHandle(handle)?;
379+
380+
this.write_scalar(Scalar::from_u32(1), dest)?;
381+
}
382+
383+
// Threading
345384
"CreateThread" => {
346-
let [_, _, _, _, _, _] =
385+
let [security, stacksize, start, arg, flags, thread] =
347386
this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
348387

349-
this.handle_unsupported("can't create threads on Windows")?;
350-
return Ok(EmulateByNameResult::AlreadyJumped);
388+
let thread_id =
389+
this.CreateThread(security, stacksize, start, arg, flags, thread)?;
390+
391+
this.write_scalar(Handle::Thread(thread_id).to_scalar(this), dest)?;
392+
}
393+
"WaitForSingleObject" => {
394+
let [handle, timeout] =
395+
this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
396+
397+
this.WaitForSingleObject(handle, timeout)?;
398+
399+
this.write_scalar(Scalar::from_u32(0), dest)?;
400+
}
401+
"GetCurrentThread" => {
402+
let [] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
403+
404+
this.write_scalar(Handle::CurrentThread.to_scalar(this), dest)?;
351405
}
352406

353407
// Incomplete shims that we "stub out" just to get pre-main initialization code to work.
@@ -385,40 +439,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
385439
// Any non zero value works for the stdlib. This is just used for stack overflows anyway.
386440
this.write_scalar(Scalar::from_u32(1), dest)?;
387441
}
388-
| "InitializeCriticalSection"
389-
| "EnterCriticalSection"
390-
| "LeaveCriticalSection"
391-
| "DeleteCriticalSection"
392-
if this.frame_in_std() =>
393-
{
394-
#[allow(non_snake_case)]
395-
let [_lpCriticalSection] =
396-
this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
397-
assert_eq!(
398-
this.get_total_thread_count(),
399-
1,
400-
"concurrency on Windows is not supported"
401-
);
402-
// Nothing to do, not even a return value.
403-
// (Windows locks are reentrant, and we have only 1 thread,
404-
// so not doing any futher checks here is at least not incorrect.)
405-
}
406-
"TryEnterCriticalSection" if this.frame_in_std() => {
407-
#[allow(non_snake_case)]
408-
let [_lpCriticalSection] =
409-
this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
410-
assert_eq!(
411-
this.get_total_thread_count(),
412-
1,
413-
"concurrency on Windows is not supported"
414-
);
415-
// There is only one thread, so this always succeeds and returns TRUE.
416-
this.write_scalar(Scalar::from_i32(1), dest)?;
417-
}
418-
"GetCurrentThread" if this.frame_in_std() => {
419-
let [] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
420-
this.write_scalar(Scalar::from_machine_isize(1, this), dest)?;
421-
}
422442
"GetCurrentProcessId" if this.frame_in_std() => {
423443
let [] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
424444
let result = this.GetCurrentProcessId()?;

0 commit comments

Comments
 (0)