Skip to content

Commit dfc12b2

Browse files
committed
basic theading
1 parent 1118d94 commit dfc12b2

Some content is hidden

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

43 files changed

+424
-107
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: 58 additions & 4 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::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" => {
@@ -340,14 +368,40 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
340368
// std-only shim.
341369
this.write_scalar(Scalar::from_machine_isize(which.into(), this), dest)?;
342370
}
371+
"CloseHandle" => {
372+
let [handle] =
373+
this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
374+
375+
match Handle::from_scalar(this.read_scalar(handle)?.check_init()?, this)? {
376+
Some(Handle::Thread(thread)) => this.detach_thread(thread)?,
377+
_ => throw_ub_format!("invalid handle"),
378+
};
343379

344-
// Better error for attempts to create a thread
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.

src/shims/windows/handle.rs

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
use rustc_target::abi::HasDataLayout;
2+
use std::mem::variant_count;
3+
4+
use crate::*;
5+
6+
/// A Windows `HANDLE` that represents a resource instead of being null or a pseudohandle.
7+
///
8+
/// This is a seperate type from [`Handle`] to simplify the packing and unpacking code.
9+
#[derive(Clone, Copy)]
10+
enum RealHandle {
11+
Thread(ThreadId),
12+
}
13+
14+
impl RealHandle {
15+
fn discriminant(self) -> u64 {
16+
match self {
17+
// can't use zero here because all zero handle is invalid
18+
Self::Thread(_) => 1,
19+
}
20+
}
21+
22+
fn data(self) -> u64 {
23+
match self {
24+
Self::Thread(thread) => thread.to_u32() as u64,
25+
}
26+
}
27+
28+
fn packed_disc_size() -> u64 {
29+
(variant_count::<Self>().log2() + 1) as u64
30+
}
31+
32+
fn to_packed(self, bits: u64) -> u64 {
33+
// top bit and lower 2 bits need to be clear
34+
let usable_bits = (bits - 3).min(32);
35+
36+
let disc_size = Self::packed_disc_size();
37+
let data_size = usable_bits - disc_size;
38+
39+
let discriminant = self.discriminant();
40+
let data = self.data();
41+
42+
assert!(discriminant < 2u64.pow(disc_size as u32));
43+
assert!(data < 2u64.pow(data_size as u32));
44+
45+
(discriminant << data_size | data) << 2
46+
}
47+
48+
fn new(discriminant: u32, data: u32) -> Option<Self> {
49+
match discriminant {
50+
1 => Some(Self::Thread(data.into())),
51+
_ => None,
52+
}
53+
}
54+
55+
fn from_packed(handle: u64, bits: u64) -> Option<Self> {
56+
let usable_bits = (bits - 3).min(32);
57+
58+
let disc_size = Self::packed_disc_size();
59+
let data_size = usable_bits - disc_size;
60+
let data_mask = 2u64.pow(data_size as u32) - 1;
61+
62+
let discriminant = handle >> data_size >> 2;
63+
let data = handle >> 2 & data_mask;
64+
65+
Self::new(discriminant.try_into().unwrap(), data.try_into().unwrap())
66+
}
67+
}
68+
69+
/// Miri representation of a Windows `HANDLE`
70+
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
71+
pub enum Handle {
72+
Null, // = 0
73+
// pseudo-handles
74+
CurrentThread, // = -2
75+
// real handles
76+
Thread(ThreadId),
77+
}
78+
79+
impl Handle {
80+
fn to_packed(self, bits: u64) -> i64 {
81+
match self {
82+
Self::Null => 0,
83+
Self::CurrentThread => -2,
84+
Self::Thread(thread) => RealHandle::Thread(thread).to_packed(bits) as i64,
85+
}
86+
}
87+
88+
pub fn to_scalar(self, cx: &impl HasDataLayout) -> Scalar<Tag> {
89+
let bits = cx.data_layout().pointer_size.bits();
90+
91+
let handle = self.to_packed(bits);
92+
93+
Scalar::from_machine_isize(handle, cx)
94+
}
95+
96+
fn from_packed(handle: i64, bits: u64) -> Option<Self> {
97+
if handle == 0 {
98+
Some(Self::Null)
99+
} else if handle == -2 {
100+
Some(Self::CurrentThread)
101+
} else {
102+
match RealHandle::from_packed(handle as u64, bits)? {
103+
RealHandle::Thread(id) => Some(Self::Thread(id)),
104+
}
105+
}
106+
}
107+
108+
pub fn from_scalar<'tcx>(
109+
handle: Scalar<Tag>,
110+
cx: &impl HasDataLayout,
111+
) -> InterpResult<'tcx, Option<Self>> {
112+
let bits = cx.data_layout().pointer_size.bits();
113+
114+
let handle = handle.to_machine_isize(cx)?;
115+
116+
Ok(Self::from_packed(handle, bits))
117+
}
118+
}

src/shims/windows/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
pub mod dlsym;
22
pub mod foreign_items;
33

4+
mod handle;
45
mod sync;
6+
mod thread;

0 commit comments

Comments
 (0)