1 //! libc syscalls supporting `rustix::thread`.
2
3 use super::super::c;
4 use super::super::conv::ret;
5 #[cfg(any(target_os = "android", target_os = "linux"))]
6 use super::super::conv::{borrowed_fd, ret_c_int};
7 use super::super::time::types::LibcTimespec;
8 #[cfg(any(target_os = "android", target_os = "linux"))]
9 use crate::fd::BorrowedFd;
10 use crate::io;
11 #[cfg(any(target_os = "android", target_os = "linux"))]
12 use crate::process::{Pid, RawNonZeroPid};
13 #[cfg(not(target_os = "redox"))]
14 use crate::thread::{NanosleepRelativeResult, Timespec};
15 use core::mem::MaybeUninit;
16 #[cfg(not(any(
17 target_os = "dragonfly",
18 target_os = "emscripten",
19 target_os = "freebsd",
20 target_os = "haiku",
21 target_os = "ios",
22 target_os = "macos",
23 target_os = "openbsd",
24 target_os = "redox",
25 target_os = "wasi",
26 )))]
27 use {crate::thread::ClockId, core::ptr::null_mut};
28
29 #[cfg(all(
30 any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
31 target_env = "gnu",
32 ))]
33 weak!(fn __clock_nanosleep_time64(c::clockid_t, c::c_int, *const LibcTimespec, *mut LibcTimespec) -> c::c_int);
34 #[cfg(all(
35 any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
36 target_env = "gnu",
37 ))]
38 weak!(fn __nanosleep64(*const LibcTimespec, *mut LibcTimespec) -> c::c_int);
39
40 #[cfg(not(any(
41 target_os = "dragonfly",
42 target_os = "emscripten",
43 target_os = "freebsd", // FreeBSD 12 has clock_nanosleep, but libc targets FreeBSD 11.
44 target_os = "haiku",
45 target_os = "ios",
46 target_os = "macos",
47 target_os = "openbsd",
48 target_os = "redox",
49 target_os = "wasi",
50 )))]
51 #[inline]
clock_nanosleep_relative(id: ClockId, request: &Timespec) -> NanosleepRelativeResult52 pub(crate) fn clock_nanosleep_relative(id: ClockId, request: &Timespec) -> NanosleepRelativeResult {
53 let mut remain = MaybeUninit::<LibcTimespec>::uninit();
54 let flags = 0;
55
56 // 32-bit gnu version: libc has `clock_nanosleep` but it is not y2038 safe by
57 // default.
58 #[cfg(all(
59 any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
60 target_env = "gnu",
61 ))]
62 unsafe {
63 if let Some(libc_clock_nanosleep) = __clock_nanosleep_time64.get() {
64 match libc_clock_nanosleep(
65 id as c::clockid_t,
66 flags,
67 &request.clone().into(),
68 remain.as_mut_ptr(),
69 ) {
70 0 => NanosleepRelativeResult::Ok,
71 err if err == io::Errno::INTR.0 => {
72 NanosleepRelativeResult::Interrupted(remain.assume_init().into())
73 }
74 err => NanosleepRelativeResult::Err(io::Errno(err)),
75 }
76 } else {
77 clock_nanosleep_relative_old(id, request)
78 }
79 }
80
81 // Main version: libc is y2038 safe and has `clock_nanosleep`.
82 #[cfg(not(all(
83 any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
84 target_env = "gnu",
85 )))]
86 unsafe {
87 match c::clock_nanosleep(id as c::clockid_t, flags, request, remain.as_mut_ptr()) {
88 0 => NanosleepRelativeResult::Ok,
89 err if err == io::Errno::INTR.0 => {
90 NanosleepRelativeResult::Interrupted(remain.assume_init())
91 }
92 err => NanosleepRelativeResult::Err(io::Errno(err)),
93 }
94 }
95 }
96
97 #[cfg(all(
98 any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
99 target_env = "gnu",
100 ))]
clock_nanosleep_relative_old(id: ClockId, request: &Timespec) -> NanosleepRelativeResult101 unsafe fn clock_nanosleep_relative_old(id: ClockId, request: &Timespec) -> NanosleepRelativeResult {
102 use core::convert::TryInto;
103 let tv_sec = match request.tv_sec.try_into() {
104 Ok(tv_sec) => tv_sec,
105 Err(_) => return NanosleepRelativeResult::Err(io::Errno::OVERFLOW),
106 };
107 let tv_nsec = match request.tv_nsec.try_into() {
108 Ok(tv_nsec) => tv_nsec,
109 Err(_) => return NanosleepRelativeResult::Err(io::Errno::INVAL),
110 };
111 let old_request = c::timespec { tv_sec, tv_nsec };
112 let mut old_remain = MaybeUninit::<c::timespec>::uninit();
113 let flags = 0;
114
115 match c::clock_nanosleep(
116 id as c::clockid_t,
117 flags,
118 &old_request,
119 old_remain.as_mut_ptr(),
120 ) {
121 0 => NanosleepRelativeResult::Ok,
122 err if err == io::Errno::INTR.0 => {
123 let old_remain = old_remain.assume_init();
124 let remain = Timespec {
125 tv_sec: old_remain.tv_sec.into(),
126 tv_nsec: old_remain.tv_nsec.into(),
127 };
128 NanosleepRelativeResult::Interrupted(remain)
129 }
130 err => NanosleepRelativeResult::Err(io::Errno(err)),
131 }
132 }
133
134 #[cfg(not(any(
135 target_os = "dragonfly",
136 target_os = "emscripten",
137 target_os = "freebsd", // FreeBSD 12 has clock_nanosleep, but libc targets FreeBSD 11.
138 target_os = "haiku",
139 target_os = "ios",
140 target_os = "macos",
141 target_os = "openbsd",
142 target_os = "redox",
143 target_os = "wasi",
144 )))]
145 #[inline]
clock_nanosleep_absolute(id: ClockId, request: &Timespec) -> io::Result<()>146 pub(crate) fn clock_nanosleep_absolute(id: ClockId, request: &Timespec) -> io::Result<()> {
147 let flags = c::TIMER_ABSTIME;
148
149 // 32-bit gnu version: libc has `clock_nanosleep` but it is not y2038 safe by
150 // default.
151 #[cfg(all(
152 any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
153 target_env = "gnu",
154 ))]
155 {
156 if let Some(libc_clock_nanosleep) = __clock_nanosleep_time64.get() {
157 match unsafe {
158 libc_clock_nanosleep(
159 id as c::clockid_t,
160 flags,
161 &request.clone().into(),
162 null_mut(),
163 )
164 } {
165 0 => Ok(()),
166 err => Err(io::Errno(err)),
167 }
168 } else {
169 clock_nanosleep_absolute_old(id, request)
170 }
171 }
172
173 // Main version: libc is y2038 safe and has `clock_nanosleep`.
174 #[cfg(not(all(
175 any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
176 target_env = "gnu",
177 )))]
178 match unsafe { c::clock_nanosleep(id as c::clockid_t, flags, request, null_mut()) } {
179 0 => Ok(()),
180 err => Err(io::Errno(err)),
181 }
182 }
183
184 #[cfg(all(
185 any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
186 target_env = "gnu",
187 ))]
clock_nanosleep_absolute_old(id: ClockId, request: &Timespec) -> io::Result<()>188 fn clock_nanosleep_absolute_old(id: ClockId, request: &Timespec) -> io::Result<()> {
189 use core::convert::TryInto;
190
191 let flags = c::TIMER_ABSTIME;
192
193 let old_request = c::timespec {
194 tv_sec: request.tv_sec.try_into().map_err(|_| io::Errno::OVERFLOW)?,
195 tv_nsec: request.tv_nsec.try_into().map_err(|_| io::Errno::INVAL)?,
196 };
197 match unsafe { c::clock_nanosleep(id as c::clockid_t, flags, &old_request, null_mut()) } {
198 0 => Ok(()),
199 err => Err(io::Errno(err)),
200 }
201 }
202
203 #[cfg(not(target_os = "redox"))]
204 #[inline]
nanosleep(request: &Timespec) -> NanosleepRelativeResult205 pub(crate) fn nanosleep(request: &Timespec) -> NanosleepRelativeResult {
206 let mut remain = MaybeUninit::<LibcTimespec>::uninit();
207
208 // 32-bit gnu version: libc has `nanosleep` but it is not y2038 safe by
209 // default.
210 #[cfg(all(
211 any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
212 target_env = "gnu",
213 ))]
214 unsafe {
215 if let Some(libc_nanosleep) = __nanosleep64.get() {
216 match ret(libc_nanosleep(&request.clone().into(), remain.as_mut_ptr())) {
217 Ok(()) => NanosleepRelativeResult::Ok,
218 Err(io::Errno::INTR) => {
219 NanosleepRelativeResult::Interrupted(remain.assume_init().into())
220 }
221 Err(err) => NanosleepRelativeResult::Err(err),
222 }
223 } else {
224 nanosleep_old(request)
225 }
226 }
227
228 // Main version: libc is y2038 safe and has `nanosleep`.
229 #[cfg(not(all(
230 any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
231 target_env = "gnu",
232 )))]
233 unsafe {
234 match ret(c::nanosleep(request, remain.as_mut_ptr())) {
235 Ok(()) => NanosleepRelativeResult::Ok,
236 Err(io::Errno::INTR) => NanosleepRelativeResult::Interrupted(remain.assume_init()),
237 Err(err) => NanosleepRelativeResult::Err(err),
238 }
239 }
240 }
241
242 #[cfg(all(
243 any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
244 target_env = "gnu",
245 ))]
nanosleep_old(request: &Timespec) -> NanosleepRelativeResult246 unsafe fn nanosleep_old(request: &Timespec) -> NanosleepRelativeResult {
247 use core::convert::TryInto;
248 let tv_sec = match request.tv_sec.try_into() {
249 Ok(tv_sec) => tv_sec,
250 Err(_) => return NanosleepRelativeResult::Err(io::Errno::OVERFLOW),
251 };
252 let tv_nsec = match request.tv_nsec.try_into() {
253 Ok(tv_nsec) => tv_nsec,
254 Err(_) => return NanosleepRelativeResult::Err(io::Errno::INVAL),
255 };
256 let old_request = c::timespec { tv_sec, tv_nsec };
257 let mut old_remain = MaybeUninit::<c::timespec>::uninit();
258
259 match ret(c::nanosleep(&old_request, old_remain.as_mut_ptr())) {
260 Ok(()) => NanosleepRelativeResult::Ok,
261 Err(io::Errno::INTR) => {
262 let old_remain = old_remain.assume_init();
263 let remain = Timespec {
264 tv_sec: old_remain.tv_sec.into(),
265 tv_nsec: old_remain.tv_nsec.into(),
266 };
267 NanosleepRelativeResult::Interrupted(remain)
268 }
269 Err(err) => NanosleepRelativeResult::Err(err),
270 }
271 }
272
273 #[cfg(any(target_os = "android", target_os = "linux"))]
274 #[inline]
275 #[must_use]
gettid() -> Pid276 pub(crate) fn gettid() -> Pid {
277 // `gettid` wasn't supported in glibc until 2.30, and musl until 1.2.2,
278 // so use `syscall`.
279 // <https://sourceware.org/bugzilla/show_bug.cgi?id=6399#c62>
280 weak_or_syscall! {
281 fn gettid() via SYS_gettid -> c::pid_t
282 }
283
284 unsafe {
285 let tid = gettid();
286 debug_assert_ne!(tid, 0);
287 Pid::from_raw_nonzero(RawNonZeroPid::new_unchecked(tid))
288 }
289 }
290
291 #[cfg(any(target_os = "android", target_os = "linux"))]
292 #[inline]
setns(fd: BorrowedFd, nstype: c::c_int) -> io::Result<c::c_int>293 pub(crate) fn setns(fd: BorrowedFd, nstype: c::c_int) -> io::Result<c::c_int> {
294 unsafe { ret_c_int(c::setns(borrowed_fd(fd), nstype)) }
295 }
296
297 #[cfg(any(target_os = "android", target_os = "linux"))]
298 #[inline]
unshare(flags: crate::thread::UnshareFlags) -> io::Result<()>299 pub(crate) fn unshare(flags: crate::thread::UnshareFlags) -> io::Result<()> {
300 unsafe { ret(c::unshare(flags.bits() as i32)) }
301 }
302