1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
use rustc_middle::ty::layout::LayoutOf;
use rustc_target::spec::abi::Abi;

use crate::*;
use shims::windows::handle::{EvalContextExt as _, Handle, PseudoHandle};

impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}

#[allow(non_snake_case)]
pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
    fn CreateThread(
        &mut self,
        security_op: &OpTy<'tcx>,
        stacksize_op: &OpTy<'tcx>,
        start_op: &OpTy<'tcx>,
        arg_op: &OpTy<'tcx>,
        flags_op: &OpTy<'tcx>,
        thread_op: &OpTy<'tcx>,
    ) -> InterpResult<'tcx, ThreadId> {
        let this = self.eval_context_mut();

        let security = this.read_pointer(security_op)?;
        // stacksize is ignored, but still needs to be a valid usize
        this.read_target_usize(stacksize_op)?;
        let start_routine = this.read_pointer(start_op)?;
        let func_arg = this.read_immediate(arg_op)?;
        let flags = this.read_scalar(flags_op)?.to_u32()?;

        let thread = if this.ptr_is_null(this.read_pointer(thread_op)?)? {
            None
        } else {
            let thread_info_place = this.deref_pointer(thread_op)?;
            Some(thread_info_place)
        };

        let stack_size_param_is_a_reservation =
            this.eval_windows_u32("c", "STACK_SIZE_PARAM_IS_A_RESERVATION");

        // We ignore the stack size, so we also ignore the
        // `STACK_SIZE_PARAM_IS_A_RESERVATION` flag.
        if flags != 0 && flags != stack_size_param_is_a_reservation {
            throw_unsup_format!("unsupported `dwCreationFlags` {} in `CreateThread`", flags)
        }

        if !this.ptr_is_null(security)? {
            throw_unsup_format!("non-null `lpThreadAttributes` in `CreateThread`")
        }

        this.start_regular_thread(
            thread,
            start_routine,
            Abi::System { unwind: false },
            func_arg,
            this.layout_of(this.tcx.types.u32)?,
        )
    }

    fn WaitForSingleObject(
        &mut self,
        handle_op: &OpTy<'tcx>,
        timeout_op: &OpTy<'tcx>,
    ) -> InterpResult<'tcx, u32> {
        let this = self.eval_context_mut();

        let handle = this.read_scalar(handle_op)?;
        let timeout = this.read_scalar(timeout_op)?.to_u32()?;

        let thread = match Handle::from_scalar(handle, this)? {
            Some(Handle::Thread(thread)) => thread,
            // Unlike on posix, the outcome of joining the current thread is not documented.
            // On current Windows, it just deadlocks.
            Some(Handle::Pseudo(PseudoHandle::CurrentThread)) => this.active_thread(),
            _ => this.invalid_handle("WaitForSingleObject")?,
        };

        if timeout != this.eval_windows_u32("c", "INFINITE") {
            throw_unsup_format!("`WaitForSingleObject` with non-infinite timeout");
        }

        this.join_thread(thread)?;

        Ok(0)
    }
}