Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit b6fc2fc

Browse files
committed
basic theading
1 parent af033ea commit b6fc2fc

File tree

65 files changed

+448
-207
lines changed

Some content is hidden

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

65 files changed

+448
-207
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: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -229,25 +229,25 @@ impl<'tcx> TlsData<'tcx> {
229229

230230
impl<'mir, 'tcx: 'mir> EvalContextPrivExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
231231
trait EvalContextPrivExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
232-
/// Schedule TLS destructors for the main thread on Windows. The
233-
/// implementation assumes that we do not support concurrency on Windows
234-
/// yet.
232+
/// Schedule TLS destructors for Windows.
233+
/// On windows, TLS destructors are managed by std.
235234
fn schedule_windows_tls_dtors(&mut self) -> InterpResult<'tcx> {
236235
let this = self.eval_context_mut();
237236
let active_thread = this.get_active_thread();
238-
assert_eq!(this.get_total_thread_count(), 1, "concurrency on Windows is not supported");
237+
239238
// Windows has a special magic linker section that is run on certain events.
240239
// Instead of searching for that section and supporting arbitrary hooks in there
241240
// (that would be basically https://github.com/rust-lang/miri/issues/450),
242241
// we specifically look up the static in libstd that we know is placed
243242
// in that section.
244-
let thread_callback = this
245-
.eval_path_scalar(&["std", "sys", "windows", "thread_local_key", "p_thread_callback"])?
246-
.to_pointer(this)?;
243+
let thread_callback =
244+
this.eval_windows("thread_local_key", "p_thread_callback")?.to_pointer(this)?;
247245
let thread_callback = this.get_ptr_fn(thread_callback)?.as_instance()?;
248246

247+
// Technically, the reason should be `DLL_PROCESS_DETACH` when the main thread exits but std ignores it.
248+
let reason = this.eval_windows("c", "DLL_THREAD_DETACH")?;
249+
249250
// The signature of this function is `unsafe extern "system" fn(h: c::LPVOID, dwReason: c::DWORD, pv: c::LPVOID)`.
250-
let reason = this.eval_path_scalar(&["std", "sys", "windows", "c", "DLL_THREAD_DETACH"])?;
251251
this.call_function(
252252
thread_callback,
253253
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 & 1 deletion
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 {
@@ -18,8 +20,8 @@ impl Dlsym {
1820
pub fn from_str<'tcx>(name: &str) -> InterpResult<'tcx, Option<Dlsym>> {
1921
Ok(match name {
2022
"GetSystemTimePreciseAsFileTime" => None,
21-
"SetThreadDescription" => None,
2223
"NtWriteFile" => Some(Dlsym::NtWriteFile),
24+
"SetThreadDescription" => Some(Dlsym::SetThreadDescription),
2325
_ => throw_unsup_format!("unsupported Windows dlsym: {}", name),
2426
})
2527
}
@@ -107,6 +109,22 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
107109
dest,
108110
)?;
109111
}
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+
}
110128
}
111129

112130
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,12 +1,17 @@
11
use std::iter;
2+
use std::time::{Duration, Instant};
23

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

8+
use crate::thread::Time;
79
use crate::*;
810
use shims::foreign_items::EmulateByNameResult;
11+
use shims::windows::handle::{EvalContextExt as _, Handle};
912
use shims::windows::sync::EvalContextExt as _;
13+
use shims::windows::thread::EvalContextExt as _;
14+
1015
use smallvec::SmallVec;
1116

1217
impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
@@ -219,6 +224,29 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
219224
let result = this.QueryPerformanceFrequency(lpFrequency)?;
220225
this.write_scalar(Scalar::from_i32(result), dest)?;
221226
}
227+
"Sleep" => {
228+
let [timeout] =
229+
this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
230+
231+
this.check_no_isolation("`Sleep`")?;
232+
233+
let timeout_ms = this.read_scalar(timeout)?.to_u32()?;
234+
235+
let duration = Duration::from_millis(timeout_ms as u64);
236+
let timeout_time = Time::Monotonic(Instant::now().checked_add(duration).unwrap());
237+
238+
let active_thread = this.get_active_thread();
239+
this.block_thread(active_thread);
240+
241+
this.register_timeout_callback(
242+
active_thread,
243+
timeout_time,
244+
Box::new(move |ecx| {
245+
ecx.unblock_thread(active_thread);
246+
Ok(())
247+
}),
248+
);
249+
}
222250

223251
// Synchronization primitives
224252
"AcquireSRWLockExclusive" => {
@@ -314,10 +342,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
314342
// FIXME: we should set last_error, but to what?
315343
this.write_null(dest)?;
316344
}
317-
"SwitchToThread" => {
345+
// this is only callable from std because we know that std ignores the return value
346+
"SwitchToThread" if this.frame_in_std() => {
318347
let [] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
319-
// Note that once Miri supports concurrency, this will need to return a nonzero
320-
// value if this call does result in switching to another thread.
348+
349+
this.yield_active_thread();
350+
351+
// FIXME: this should return a nonzero value if this call does result in switching to another thread.
321352
this.write_null(dest)?;
322353
}
323354
"GetStdHandle" => {
@@ -329,14 +360,37 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
329360
// std-only shim.
330361
this.write_scalar(Scalar::from_machine_isize(which.into(), this), dest)?;
331362
}
363+
"CloseHandle" => {
364+
let [handle] =
365+
this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
332366

333-
// Better error for attempts to create a thread
367+
this.CloseHandle(handle)?;
368+
369+
this.write_scalar(Scalar::from_u32(1), dest)?;
370+
}
371+
372+
// Threading
334373
"CreateThread" => {
335-
let [_, _, _, _, _, _] =
374+
let [security, stacksize, start, arg, flags, thread] =
336375
this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
337376

338-
this.handle_unsupported("can't create threads on Windows")?;
339-
return Ok(EmulateByNameResult::AlreadyJumped);
377+
let thread_id =
378+
this.CreateThread(security, stacksize, start, arg, flags, thread)?;
379+
380+
this.write_scalar(Handle::Thread(thread_id).to_scalar(this), dest)?;
381+
}
382+
"WaitForSingleObject" => {
383+
let [handle, timeout] =
384+
this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
385+
386+
this.WaitForSingleObject(handle, timeout)?;
387+
388+
this.write_scalar(Scalar::from_u32(0), dest)?;
389+
}
390+
"GetCurrentThread" => {
391+
let [] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
392+
393+
this.write_scalar(Handle::CurrentThread.to_scalar(this), dest)?;
340394
}
341395

342396
// Incomplete shims that we "stub out" just to get pre-main initialization code to work.
@@ -374,40 +428,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
374428
// Any non zero value works for the stdlib. This is just used for stack overflows anyway.
375429
this.write_scalar(Scalar::from_u32(1), dest)?;
376430
}
377-
| "InitializeCriticalSection"
378-
| "EnterCriticalSection"
379-
| "LeaveCriticalSection"
380-
| "DeleteCriticalSection"
381-
if this.frame_in_std() =>
382-
{
383-
#[allow(non_snake_case)]
384-
let [_lpCriticalSection] =
385-
this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
386-
assert_eq!(
387-
this.get_total_thread_count(),
388-
1,
389-
"concurrency on Windows is not supported"
390-
);
391-
// Nothing to do, not even a return value.
392-
// (Windows locks are reentrant, and we have only 1 thread,
393-
// so not doing any futher checks here is at least not incorrect.)
394-
}
395-
"TryEnterCriticalSection" if this.frame_in_std() => {
396-
#[allow(non_snake_case)]
397-
let [_lpCriticalSection] =
398-
this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
399-
assert_eq!(
400-
this.get_total_thread_count(),
401-
1,
402-
"concurrency on Windows is not supported"
403-
);
404-
// There is only one thread, so this always succeeds and returns TRUE.
405-
this.write_scalar(Scalar::from_i32(1), dest)?;
406-
}
407-
"GetCurrentThread" if this.frame_in_std() => {
408-
let [] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
409-
this.write_scalar(Scalar::from_machine_isize(1, this), dest)?;
410-
}
411431
"GetCurrentProcessId" if this.frame_in_std() => {
412432
let [] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
413433
let result = this.GetCurrentProcessId()?;

0 commit comments

Comments
 (0)