miri/shims/windows/
thread.rs

1use rustc_abi::ExternAbi;
2
3use self::shims::windows::handle::{EvalContextExt as _, Handle, PseudoHandle};
4use crate::*;
5
6impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
7
8#[allow(non_snake_case)]
9pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
10    fn CreateThread(
11        &mut self,
12        security_op: &OpTy<'tcx>,
13        stacksize_op: &OpTy<'tcx>,
14        start_op: &OpTy<'tcx>,
15        arg_op: &OpTy<'tcx>,
16        flags_op: &OpTy<'tcx>,
17        thread_op: &OpTy<'tcx>,
18    ) -> InterpResult<'tcx, ThreadId> {
19        let this = self.eval_context_mut();
20
21        let security = this.read_pointer(security_op)?;
22        // stacksize is ignored, but still needs to be a valid usize
23        this.read_target_usize(stacksize_op)?;
24        let start_routine = this.read_pointer(start_op)?;
25        let func_arg = this.read_immediate(arg_op)?;
26        let flags = this.read_scalar(flags_op)?.to_u32()?;
27
28        let thread = if this.ptr_is_null(this.read_pointer(thread_op)?)? {
29            None
30        } else {
31            let thread_info_place = this.deref_pointer_as(thread_op, this.machine.layouts.u32)?;
32            Some(thread_info_place)
33        };
34
35        let stack_size_param_is_a_reservation =
36            this.eval_windows_u32("c", "STACK_SIZE_PARAM_IS_A_RESERVATION");
37
38        // We ignore the stack size, so we also ignore the
39        // `STACK_SIZE_PARAM_IS_A_RESERVATION` flag.
40        if flags != 0 && flags != stack_size_param_is_a_reservation {
41            throw_unsup_format!("unsupported `dwCreationFlags` {} in `CreateThread`", flags)
42        }
43
44        if !this.ptr_is_null(security)? {
45            throw_unsup_format!("non-null `lpThreadAttributes` in `CreateThread`")
46        }
47
48        this.start_regular_thread(
49            thread,
50            start_routine,
51            ExternAbi::System { unwind: false },
52            func_arg,
53            this.layout_of(this.tcx.types.u32)?,
54        )
55    }
56
57    fn WaitForSingleObject(
58        &mut self,
59        handle_op: &OpTy<'tcx>,
60        timeout_op: &OpTy<'tcx>,
61        return_dest: &MPlaceTy<'tcx>,
62    ) -> InterpResult<'tcx> {
63        let this = self.eval_context_mut();
64
65        let handle = this.read_handle(handle_op, "WaitForSingleObject")?;
66        let timeout = this.read_scalar(timeout_op)?.to_u32()?;
67
68        let joined_thread_id = match handle {
69            Handle::Thread(thread) => thread,
70            // Unlike on posix, the outcome of joining the current thread is not documented.
71            // On current Windows, it just deadlocks.
72            Handle::Pseudo(PseudoHandle::CurrentThread) => this.active_thread(),
73            _ => this.invalid_handle("WaitForSingleObject")?,
74        };
75
76        if timeout != this.eval_windows_u32("c", "INFINITE") {
77            throw_unsup_format!("`WaitForSingleObject` with non-infinite timeout");
78        }
79
80        this.join_thread(
81            joined_thread_id,
82            /* success_retval */ this.eval_windows("c", "WAIT_OBJECT_0"),
83            return_dest,
84        )?;
85
86        interp_ok(())
87    }
88}