miri/shims/unix/
thread.rs

1use rustc_abi::ExternAbi;
2
3use crate::*;
4
5#[derive(Debug, Clone, PartialEq, Eq)]
6pub enum ThreadNameResult {
7    Ok,
8    NameTooLong,
9    ThreadNotFound,
10}
11
12impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
13pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
14    fn pthread_create(
15        &mut self,
16        thread: &OpTy<'tcx>,
17        _attr: &OpTy<'tcx>,
18        start_routine: &OpTy<'tcx>,
19        arg: &OpTy<'tcx>,
20    ) -> InterpResult<'tcx, ()> {
21        let this = self.eval_context_mut();
22
23        let thread_info_place = this.deref_pointer_as(thread, this.libc_ty_layout("pthread_t"))?;
24
25        let start_routine = this.read_pointer(start_routine)?;
26
27        let func_arg = this.read_immediate(arg)?;
28
29        this.start_regular_thread(
30            Some(thread_info_place),
31            start_routine,
32            ExternAbi::C { unwind: false },
33            func_arg,
34            this.machine.layouts.mut_raw_ptr,
35        )?;
36
37        interp_ok(())
38    }
39
40    fn pthread_join(
41        &mut self,
42        thread: &OpTy<'tcx>,
43        retval: &OpTy<'tcx>,
44    ) -> InterpResult<'tcx, Scalar> {
45        let this = self.eval_context_mut();
46
47        if !this.ptr_is_null(this.read_pointer(retval)?)? {
48            // FIXME: implement reading the thread function's return place.
49            throw_unsup_format!("Miri supports pthread_join only with retval==NULL");
50        }
51
52        let thread = this.read_scalar(thread)?.to_int(this.libc_ty_layout("pthread_t").size)?;
53        let Ok(thread) = this.thread_id_try_from(thread) else {
54            return interp_ok(this.eval_libc("ESRCH"));
55        };
56
57        this.join_thread_exclusive(thread)?;
58
59        interp_ok(Scalar::from_u32(0))
60    }
61
62    fn pthread_detach(&mut self, thread: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
63        let this = self.eval_context_mut();
64
65        let thread = this.read_scalar(thread)?.to_int(this.libc_ty_layout("pthread_t").size)?;
66        let Ok(thread) = this.thread_id_try_from(thread) else {
67            return interp_ok(this.eval_libc("ESRCH"));
68        };
69        this.detach_thread(thread, /*allow_terminated_joined*/ false)?;
70
71        interp_ok(Scalar::from_u32(0))
72    }
73
74    fn pthread_self(&mut self) -> InterpResult<'tcx, Scalar> {
75        let this = self.eval_context_mut();
76
77        let thread_id = this.active_thread();
78        interp_ok(Scalar::from_uint(thread_id.to_u32(), this.libc_ty_layout("pthread_t").size))
79    }
80
81    /// Set the name of the specified thread. If the name including the null terminator
82    /// is longer or equals to `name_max_len`, then if `truncate` is set the truncated name
83    /// is used as the thread name, otherwise [`ThreadNameResult::NameTooLong`] is returned.
84    /// If the specified thread wasn't found, [`ThreadNameResult::ThreadNotFound`] is returned.
85    fn pthread_setname_np(
86        &mut self,
87        thread: Scalar,
88        name: Scalar,
89        name_max_len: usize,
90        truncate: bool,
91    ) -> InterpResult<'tcx, ThreadNameResult> {
92        let this = self.eval_context_mut();
93
94        let thread = thread.to_int(this.libc_ty_layout("pthread_t").size)?;
95        let Ok(thread) = this.thread_id_try_from(thread) else {
96            return interp_ok(ThreadNameResult::ThreadNotFound);
97        };
98        let name = name.to_pointer(this)?;
99        let mut name = this.read_c_str(name)?.to_owned();
100
101        // Comparing with `>=` to account for null terminator.
102        if name.len() >= name_max_len {
103            if truncate {
104                name.truncate(name_max_len.saturating_sub(1));
105            } else {
106                return interp_ok(ThreadNameResult::NameTooLong);
107            }
108        }
109
110        this.set_thread_name(thread, name);
111
112        interp_ok(ThreadNameResult::Ok)
113    }
114
115    /// Get the name of the specified thread. If the thread name doesn't fit
116    /// the buffer, then if `truncate` is set the truncated name is written out,
117    /// otherwise [`ThreadNameResult::NameTooLong`] is returned. If the specified
118    /// thread wasn't found, [`ThreadNameResult::ThreadNotFound`] is returned.
119    fn pthread_getname_np(
120        &mut self,
121        thread: Scalar,
122        name_out: Scalar,
123        len: Scalar,
124        truncate: bool,
125    ) -> InterpResult<'tcx, ThreadNameResult> {
126        let this = self.eval_context_mut();
127
128        let thread = thread.to_int(this.libc_ty_layout("pthread_t").size)?;
129        let Ok(thread) = this.thread_id_try_from(thread) else {
130            return interp_ok(ThreadNameResult::ThreadNotFound);
131        };
132        let name_out = name_out.to_pointer(this)?;
133        let len = len.to_target_usize(this)?;
134
135        // FIXME: we should use the program name if the thread name is not set
136        let name = this.get_thread_name(thread).unwrap_or(b"<unnamed>").to_owned();
137        let name = match truncate {
138            true => &name[..name.len().min(len.try_into().unwrap_or(usize::MAX).saturating_sub(1))],
139            false => &name,
140        };
141
142        let (success, _written) = this.write_c_str(name, name_out, len)?;
143        let res = if success { ThreadNameResult::Ok } else { ThreadNameResult::NameTooLong };
144
145        interp_ok(res)
146    }
147
148    fn sched_yield(&mut self) -> InterpResult<'tcx, ()> {
149        let this = self.eval_context_mut();
150
151        this.yield_active_thread();
152
153        interp_ok(())
154    }
155}