1//! libc syscalls supporting `rustix::thread`. 2 3use super::super::c; 4use super::super::conv::ret; 5#[cfg(any(target_os = "android", target_os = "linux"))] 6use super::super::conv::{borrowed_fd, ret_c_int}; 7use super::super::time::types::LibcTimespec; 8#[cfg(any(target_os = "android", target_os = "linux"))] 9use crate::fd::BorrowedFd; 10use crate::io; 11#[cfg(any(target_os = "android", target_os = "linux"))] 12use crate::process::{Pid, RawNonZeroPid}; 13#[cfg(not(target_os = "redox"))] 14use crate::thread::{NanosleepRelativeResult, Timespec}; 15use 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)))] 27use {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))] 33weak!(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))] 38weak!(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] 52pub(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))] 101unsafe 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] 146pub(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))] 188fn 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] 205pub(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))] 246unsafe 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] 276pub(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] 293pub(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] 299pub(crate) fn unshare(flags: crate::thread::UnshareFlags) -> io::Result<()> { 300 unsafe { ret(c::unshare(flags.bits() as i32)) } 301} 302