• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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