]> git.lizzy.rs Git - rust.git/blob - src/shims/unix/thread.rs
Amend experimental thread support warnings
[rust.git] / src / shims / unix / thread.rs
1 use crate::*;
2 use rustc_middle::ty::layout::LayoutOf;
3 use rustc_target::spec::abi::Abi;
4
5 impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
6 pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
7     fn pthread_create(
8         &mut self,
9         thread: &OpTy<'tcx, Tag>,
10         _attr: &OpTy<'tcx, Tag>,
11         start_routine: &OpTy<'tcx, Tag>,
12         arg: &OpTy<'tcx, Tag>,
13     ) -> InterpResult<'tcx, i32> {
14         let this = self.eval_context_mut();
15
16         this.tcx.sess.warn(
17             "thread support is experimental: weak memory effects are not fully compatible with the Rust atomics memory model.",
18         );
19
20         // Create the new thread
21         let new_thread_id = this.create_thread();
22
23         // Write the current thread-id, switch to the next thread later
24         // to treat this write operation as occuring on the current thread.
25         let thread_info_place = this.deref_operand(thread)?;
26         this.write_scalar(
27             Scalar::from_uint(new_thread_id.to_u32(), thread_info_place.layout.size),
28             &thread_info_place.into(),
29         )?;
30
31         // Read the function argument that will be sent to the new thread
32         // before the thread starts executing since reading after the
33         // context switch will incorrectly report a data-race.
34         let fn_ptr = this.read_pointer(start_routine)?;
35         let func_arg = this.read_immediate(arg)?;
36
37         // Finally switch to new thread so that we can push the first stackframe.
38         // After this all accesses will be treated as occuring in the new thread.
39         let old_thread_id = this.set_active_thread(new_thread_id);
40
41         // Perform the function pointer load in the new thread frame.
42         let instance = this.get_ptr_fn(fn_ptr)?.as_instance()?;
43
44         // Note: the returned value is currently ignored (see the FIXME in
45         // pthread_join below) because the Rust standard library does not use
46         // it.
47         let ret_place =
48             this.allocate(this.layout_of(this.tcx.types.usize)?, MiriMemoryKind::Machine.into())?;
49
50         this.call_function(
51             instance,
52             Abi::C { unwind: false },
53             &[*func_arg],
54             &ret_place.into(),
55             StackPopCleanup::Root { cleanup: true },
56         )?;
57
58         // Restore the old active thread frame.
59         this.set_active_thread(old_thread_id);
60
61         Ok(0)
62     }
63
64     fn pthread_join(
65         &mut self,
66         thread: &OpTy<'tcx, Tag>,
67         retval: &OpTy<'tcx, Tag>,
68     ) -> InterpResult<'tcx, i32> {
69         let this = self.eval_context_mut();
70
71         if !this.ptr_is_null(this.read_pointer(retval)?)? {
72             // FIXME: implement reading the thread function's return place.
73             throw_unsup_format!("Miri supports pthread_join only with retval==NULL");
74         }
75
76         let thread_id = this.read_scalar(thread)?.to_machine_usize(this)?;
77         this.join_thread(thread_id.try_into().expect("thread ID should fit in u32"))?;
78
79         Ok(0)
80     }
81
82     fn pthread_detach(&mut self, thread: &OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> {
83         let this = self.eval_context_mut();
84
85         let thread_id = this.read_scalar(thread)?.to_machine_usize(this)?;
86         this.detach_thread(thread_id.try_into().expect("thread ID should fit in u32"))?;
87
88         Ok(0)
89     }
90
91     fn pthread_self(&mut self, dest: &PlaceTy<'tcx, Tag>) -> InterpResult<'tcx> {
92         let this = self.eval_context_mut();
93
94         let thread_id = this.get_active_thread();
95         this.write_scalar(Scalar::from_uint(thread_id.to_u32(), dest.layout.size), dest)
96     }
97
98     fn prctl(&mut self, args: &[OpTy<'tcx, Tag>]) -> InterpResult<'tcx, i32> {
99         let this = self.eval_context_mut();
100         this.assert_target_os("linux", "prctl");
101
102         if args.is_empty() {
103             throw_ub_format!(
104                 "incorrect number of arguments for `prctl`: got {}, expected at least 1",
105                 args.len()
106             );
107         }
108
109         let option = this.read_scalar(&args[0])?.to_i32()?;
110         if option == this.eval_libc_i32("PR_SET_NAME")? {
111             if args.len() < 2 {
112                 throw_ub_format!(
113                     "incorrect number of arguments for `prctl` with `PR_SET_NAME`: got {}, expected at least 2",
114                     args.len()
115                 );
116             }
117
118             let address = this.read_pointer(&args[1])?;
119             let mut name = this.read_c_str(address)?.to_owned();
120             // The name should be no more than 16 bytes, including the null
121             // byte. Since `read_c_str` returns the string without the null
122             // byte, we need to truncate to 15.
123             name.truncate(15);
124             this.set_active_thread_name(name);
125         } else if option == this.eval_libc_i32("PR_GET_NAME")? {
126             if args.len() < 2 {
127                 throw_ub_format!(
128                     "incorrect number of arguments for `prctl` with `PR_SET_NAME`: got {}, expected at least 2",
129                     args.len()
130                 );
131             }
132
133             let address = this.read_pointer(&args[1])?;
134             let mut name = this.get_active_thread_name().to_vec();
135             name.push(0u8);
136             assert!(name.len() <= 16);
137             this.write_bytes_ptr(address, name)?;
138         } else {
139             throw_unsup_format!("unsupported prctl option {}", option);
140         }
141
142         Ok(0)
143     }
144
145     fn pthread_setname_np(&mut self, name: Pointer<Option<Tag>>) -> InterpResult<'tcx> {
146         let this = self.eval_context_mut();
147         this.assert_target_os("macos", "pthread_setname_np");
148
149         let name = this.read_c_str(name)?.to_owned();
150         this.set_active_thread_name(name);
151
152         Ok(())
153     }
154
155     fn sched_yield(&mut self) -> InterpResult<'tcx, i32> {
156         let this = self.eval_context_mut();
157
158         this.yield_active_thread();
159
160         Ok(0)
161     }
162 }