Skip to main content

miri/shims/unix/
thread.rs

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