Skip to main content

rustix/backend/libc/thread/
syscalls.rs

1//! libc syscalls supporting `rustix::thread`.
2
3#[cfg(any(freebsdlike, linux_kernel, target_os = "fuchsia"))]
4use super::types::RawCpuSet;
5use crate::backend::c;
6use crate::backend::conv::ret;
7use crate::io;
8#[cfg(any(freebsdlike, linux_kernel, target_os = "fuchsia"))]
9use crate::pid::Pid;
10#[cfg(not(any(
11    apple,
12    freebsdlike,
13    target_os = "emscripten",
14    target_os = "espidf",
15    target_os = "haiku",
16    target_os = "horizon",
17    target_os = "openbsd",
18    target_os = "redox",
19    target_os = "vita",
20    target_os = "wasi",
21)))]
22use crate::thread::ClockId;
23#[cfg(linux_kernel)]
24use crate::thread::{Cpuid, MembarrierCommand, MembarrierQuery};
25#[cfg(not(target_os = "redox"))]
26use crate::thread::{NanosleepRelativeResult, Timespec};
27#[cfg(all(target_env = "gnu", fix_y2038))]
28use crate::timespec::LibcTimespec;
29#[cfg(not(fix_y2038))]
30use crate::timespec::{as_libc_timespec_mut_ptr, as_libc_timespec_ptr};
31#[cfg(linux_kernel)]
32use crate::utils::option_as_ptr;
33use core::mem::MaybeUninit;
34#[cfg(linux_kernel)]
35use core::sync::atomic::AtomicU32;
36#[cfg(linux_kernel)]
37use {
38    crate::backend::conv::{borrowed_fd, ret_c_int, ret_u32, ret_usize},
39    crate::fd::BorrowedFd,
40    crate::thread::futex,
41    crate::utils::as_mut_ptr,
42};
43
44#[cfg(all(target_env = "gnu", fix_y2038))]
45weak!(fn __clock_nanosleep_time64(c::clockid_t, c::c_int, *const LibcTimespec, *mut LibcTimespec) -> c::c_int);
46#[cfg(all(target_env = "gnu", fix_y2038))]
47weak!(fn __nanosleep64(*const LibcTimespec, *mut LibcTimespec) -> c::c_int);
48
49#[cfg(not(any(
50    apple,
51    target_os = "dragonfly",
52    target_os = "emscripten",
53    target_os = "espidf",
54    target_os = "freebsd", // FreeBSD 12 has clock_nanosleep, but libc targets FreeBSD 11.
55    target_os = "haiku",
56    target_os = "horizon",
57    target_os = "openbsd",
58    target_os = "redox",
59    target_os = "vita",
60    target_os = "wasi",
61)))]
62#[inline]
63pub(crate) fn clock_nanosleep_relative(id: ClockId, request: &Timespec) -> NanosleepRelativeResult {
64    // Old 32-bit version: libc has `clock_nanosleep` but it is not y2038 safe
65    // by default. But there may be a `__clock_nanosleep_time64` we can use.
66    #[cfg(fix_y2038)]
67    {
68        #[cfg(target_env = "gnu")]
69        if let Some(libc_clock_nanosleep) = __clock_nanosleep_time64.get() {
70            let flags = 0;
71            let mut remain = MaybeUninit::<LibcTimespec>::uninit();
72
73            unsafe {
74                return match libc_clock_nanosleep(
75                    id as c::clockid_t,
76                    flags,
77                    &request.clone().into(),
78                    remain.as_mut_ptr(),
79                ) {
80                    0 => NanosleepRelativeResult::Ok,
81                    err if err == io::Errno::INTR.0 => {
82                        NanosleepRelativeResult::Interrupted(remain.assume_init().into())
83                    }
84                    err => NanosleepRelativeResult::Err(io::Errno(err)),
85                };
86            }
87        }
88
89        clock_nanosleep_relative_old(id, request)
90    }
91
92    // Main version: libc is y2038 safe and has `clock_nanosleep`.
93    #[cfg(not(fix_y2038))]
94    unsafe {
95        let flags = 0;
96        let mut remain = MaybeUninit::<Timespec>::uninit();
97
98        match c::clock_nanosleep(
99            id as c::clockid_t,
100            flags,
101            as_libc_timespec_ptr(request),
102            as_libc_timespec_mut_ptr(&mut remain),
103        ) {
104            0 => NanosleepRelativeResult::Ok,
105            err if err == io::Errno::INTR.0 => {
106                NanosleepRelativeResult::Interrupted(remain.assume_init())
107            }
108            err => NanosleepRelativeResult::Err(io::Errno(err)),
109        }
110    }
111}
112
113#[cfg(all(
114    fix_y2038,
115    not(any(
116        apple,
117        target_os = "emscripten",
118        target_os = "haiku",
119        target_os = "horizon",
120        target_os = "vita"
121    ))
122))]
123fn clock_nanosleep_relative_old(
124    id: crate::clockid::ClockId,
125    request: &Timespec,
126) -> NanosleepRelativeResult {
127    let tv_sec = match request.tv_sec.try_into() {
128        Ok(tv_sec) => tv_sec,
129        Err(_) => return NanosleepRelativeResult::Err(io::Errno::OVERFLOW),
130    };
131    let tv_nsec = match request.tv_nsec.try_into() {
132        Ok(tv_nsec) => tv_nsec,
133        Err(_) => return NanosleepRelativeResult::Err(io::Errno::INVAL),
134    };
135    let old_request = c::timespec { tv_sec, tv_nsec };
136    let mut old_remain = MaybeUninit::<c::timespec>::uninit();
137    let flags = 0;
138
139    unsafe {
140        match c::clock_nanosleep(
141            id as c::clockid_t,
142            flags,
143            &old_request,
144            old_remain.as_mut_ptr(),
145        ) {
146            0 => NanosleepRelativeResult::Ok,
147            err if err == io::Errno::INTR.0 => {
148                let old_remain = old_remain.assume_init();
149                let remain = Timespec {
150                    tv_sec: old_remain.tv_sec.into(),
151                    tv_nsec: old_remain.tv_nsec.into(),
152                };
153                NanosleepRelativeResult::Interrupted(remain)
154            }
155            err => NanosleepRelativeResult::Err(io::Errno(err)),
156        }
157    }
158}
159
160#[cfg(not(any(
161    apple,
162    target_os = "dragonfly",
163    target_os = "emscripten",
164    target_os = "espidf",
165    target_os = "freebsd", // FreeBSD 12 has clock_nanosleep, but libc targets FreeBSD 11.
166    target_os = "haiku",
167    target_os = "horizon",
168    target_os = "openbsd",
169    target_os = "redox",
170    target_os = "vita",
171    target_os = "wasi",
172)))]
173#[inline]
174pub(crate) fn clock_nanosleep_absolute(id: ClockId, request: &Timespec) -> io::Result<()> {
175    // Old 32-bit version: libc has `clock_nanosleep` but it is not y2038 safe
176    // by default. But there may be a `__clock_nanosleep_time64` we can use.
177    #[cfg(fix_y2038)]
178    {
179        #[cfg(target_env = "gnu")]
180        if let Some(libc_clock_nanosleep) = __clock_nanosleep_time64.get() {
181            let flags = c::TIMER_ABSTIME;
182            unsafe {
183                return match libc_clock_nanosleep(
184                    id as c::clockid_t,
185                    flags,
186                    &request.clone().into(),
187                    core::ptr::null_mut(),
188                ) {
189                    0 => Ok(()),
190                    err => Err(io::Errno(err)),
191                };
192            }
193        }
194
195        clock_nanosleep_absolute_old(id, request)
196    }
197
198    // Main version: libc is y2038 safe and has `clock_nanosleep`.
199    #[cfg(not(fix_y2038))]
200    {
201        let flags = c::TIMER_ABSTIME;
202
203        match unsafe {
204            c::clock_nanosleep(
205                id as c::clockid_t,
206                flags as _,
207                as_libc_timespec_ptr(request),
208                core::ptr::null_mut(),
209            )
210        } {
211            0 => Ok(()),
212            err => Err(io::Errno(err)),
213        }
214    }
215}
216
217#[cfg(all(
218    fix_y2038,
219    not(any(
220        apple,
221        target_os = "emscripten",
222        target_os = "haiku",
223        target_os = "horizon",
224        target_os = "vita"
225    ))
226))]
227fn clock_nanosleep_absolute_old(id: crate::clockid::ClockId, request: &Timespec) -> io::Result<()> {
228    let flags = c::TIMER_ABSTIME;
229
230    let old_request = c::timespec {
231        tv_sec: request.tv_sec.try_into().map_err(|_| io::Errno::OVERFLOW)?,
232        tv_nsec: request.tv_nsec.try_into().map_err(|_| io::Errno::INVAL)?,
233    };
234    match unsafe {
235        c::clock_nanosleep(
236            id as c::clockid_t,
237            flags,
238            &old_request,
239            core::ptr::null_mut(),
240        )
241    } {
242        0 => Ok(()),
243        err => Err(io::Errno(err)),
244    }
245}
246
247#[cfg(not(target_os = "redox"))]
248#[inline]
249pub(crate) fn nanosleep(request: &Timespec) -> NanosleepRelativeResult {
250    // Old 32-bit version: libc has `nanosleep` but it is not y2038 safe by
251    // default. But there may be a `__nanosleep64` we can use.
252    #[cfg(fix_y2038)]
253    {
254        #[cfg(target_env = "gnu")]
255        if let Some(libc_nanosleep) = __nanosleep64.get() {
256            let mut remain = MaybeUninit::<LibcTimespec>::uninit();
257            unsafe {
258                return match ret(libc_nanosleep(&request.clone().into(), remain.as_mut_ptr())) {
259                    Ok(()) => NanosleepRelativeResult::Ok,
260                    Err(io::Errno::INTR) => {
261                        NanosleepRelativeResult::Interrupted(remain.assume_init().into())
262                    }
263                    Err(err) => NanosleepRelativeResult::Err(err),
264                };
265            }
266        }
267
268        nanosleep_old(request)
269    }
270
271    // Main version: libc is y2038 safe and has `nanosleep`.
272    #[cfg(not(fix_y2038))]
273    unsafe {
274        let mut remain = MaybeUninit::<Timespec>::uninit();
275
276        match ret(c::nanosleep(
277            as_libc_timespec_ptr(request),
278            as_libc_timespec_mut_ptr(&mut remain),
279        )) {
280            Ok(()) => NanosleepRelativeResult::Ok,
281            Err(io::Errno::INTR) => NanosleepRelativeResult::Interrupted(remain.assume_init()),
282            Err(err) => NanosleepRelativeResult::Err(err),
283        }
284    }
285}
286
287#[cfg(fix_y2038)]
288fn nanosleep_old(request: &Timespec) -> NanosleepRelativeResult {
289    let tv_sec = match request.tv_sec.try_into() {
290        Ok(tv_sec) => tv_sec,
291        Err(_) => return NanosleepRelativeResult::Err(io::Errno::OVERFLOW),
292    };
293    let tv_nsec = match request.tv_nsec.try_into() {
294        Ok(tv_nsec) => tv_nsec,
295        Err(_) => return NanosleepRelativeResult::Err(io::Errno::INVAL),
296    };
297    let old_request = c::timespec { tv_sec, tv_nsec };
298    let mut old_remain = MaybeUninit::<c::timespec>::uninit();
299
300    unsafe {
301        match ret(c::nanosleep(&old_request, old_remain.as_mut_ptr())) {
302            Ok(()) => NanosleepRelativeResult::Ok,
303            Err(io::Errno::INTR) => {
304                let old_remain = old_remain.assume_init();
305                let remain = Timespec {
306                    tv_sec: old_remain.tv_sec.into(),
307                    tv_nsec: old_remain.tv_nsec.into(),
308                };
309                NanosleepRelativeResult::Interrupted(remain)
310            }
311            Err(err) => NanosleepRelativeResult::Err(err),
312        }
313    }
314}
315
316#[cfg(linux_kernel)]
317#[inline]
318#[must_use]
319pub(crate) fn gettid() -> Pid {
320    // `gettid` wasn't supported in glibc until 2.30, and musl until 1.2.2,
321    // so use `syscall`.
322    // <https://sourceware.org/bugzilla/show_bug.cgi?id=6399#c62>
323    weak_or_syscall! {
324        fn gettid() via SYS_gettid -> c::pid_t
325    }
326
327    unsafe {
328        let tid = gettid();
329        Pid::from_raw_unchecked(tid)
330    }
331}
332
333#[cfg(linux_kernel)]
334#[inline]
335pub(crate) fn setns(fd: BorrowedFd<'_>, nstype: c::c_int) -> io::Result<c::c_int> {
336    // `setns` wasn't supported in glibc until 2.14, and musl until 0.9.5,
337    // so use `syscall`.
338    weak_or_syscall! {
339        fn setns(fd: c::c_int, nstype: c::c_int) via SYS_setns -> c::c_int
340    }
341
342    unsafe { ret_c_int(setns(borrowed_fd(fd), nstype)) }
343}
344
345#[cfg(linux_kernel)]
346#[inline]
347pub(crate) unsafe fn unshare(flags: crate::thread::UnshareFlags) -> io::Result<()> {
348    ret(c::unshare(flags.bits() as i32))
349}
350
351#[cfg(linux_kernel)]
352#[inline]
353pub(crate) fn capget(
354    header: &mut linux_raw_sys::general::__user_cap_header_struct,
355    data: &mut [MaybeUninit<linux_raw_sys::general::__user_cap_data_struct>],
356) -> io::Result<()> {
357    syscall! {
358        fn capget(
359            hdrp: *mut linux_raw_sys::general::__user_cap_header_struct,
360            data: *mut linux_raw_sys::general::__user_cap_data_struct
361        ) via SYS_capget -> c::c_int
362    }
363
364    unsafe {
365        ret(capget(
366            as_mut_ptr(header),
367            data.as_mut_ptr()
368                .cast::<linux_raw_sys::general::__user_cap_data_struct>(),
369        ))
370    }
371}
372
373#[cfg(linux_kernel)]
374#[inline]
375pub(crate) fn capset(
376    header: &mut linux_raw_sys::general::__user_cap_header_struct,
377    data: &[linux_raw_sys::general::__user_cap_data_struct],
378) -> io::Result<()> {
379    syscall! {
380        fn capset(
381            hdrp: *mut linux_raw_sys::general::__user_cap_header_struct,
382            data: *const linux_raw_sys::general::__user_cap_data_struct
383        ) via SYS_capset -> c::c_int
384    }
385
386    unsafe { ret(capset(as_mut_ptr(header), data.as_ptr())) }
387}
388
389#[cfg(linux_kernel)]
390#[inline]
391pub(crate) fn setuid_thread(uid: crate::ugid::Uid) -> io::Result<()> {
392    syscall! {
393        fn setuid(uid: c::uid_t) via SYS_setuid -> c::c_int
394    }
395
396    unsafe { ret(setuid(uid.as_raw())) }
397}
398
399#[cfg(linux_kernel)]
400#[inline]
401pub(crate) fn setresuid_thread(
402    ruid: Option<crate::ugid::Uid>,
403    euid: Option<crate::ugid::Uid>,
404    suid: Option<crate::ugid::Uid>,
405) -> io::Result<()> {
406    #[cfg(any(target_arch = "x86", target_arch = "arm", target_arch = "sparc"))]
407    const SYS: c::c_long = c::SYS_setresuid32 as c::c_long;
408    #[cfg(not(any(target_arch = "x86", target_arch = "arm", target_arch = "sparc")))]
409    const SYS: c::c_long = c::SYS_setresuid as c::c_long;
410
411    syscall! {
412        fn setresuid(ruid: c::uid_t, euid: c::uid_t, suid: c::uid_t) via SYS -> c::c_int
413    }
414
415    unsafe {
416        ret(setresuid(
417            ruid.map_or(-1_i32 as u32, |x| x.as_raw()),
418            euid.map_or(-1_i32 as u32, |x| x.as_raw()),
419            suid.map_or(-1_i32 as u32, |x| x.as_raw()),
420        ))
421    }
422}
423
424#[cfg(linux_kernel)]
425#[inline]
426pub(crate) fn setgid_thread(gid: crate::ugid::Gid) -> io::Result<()> {
427    syscall! {
428        fn setgid(gid: c::gid_t) via SYS_setgid -> c::c_int
429    }
430
431    unsafe { ret(setgid(gid.as_raw())) }
432}
433
434#[cfg(linux_kernel)]
435#[inline]
436pub(crate) fn setresgid_thread(
437    rgid: Option<crate::ugid::Gid>,
438    egid: Option<crate::ugid::Gid>,
439    sgid: Option<crate::ugid::Gid>,
440) -> io::Result<()> {
441    #[cfg(any(target_arch = "x86", target_arch = "arm", target_arch = "sparc"))]
442    const SYS: c::c_long = c::SYS_setresgid32 as c::c_long;
443    #[cfg(not(any(target_arch = "x86", target_arch = "arm", target_arch = "sparc")))]
444    const SYS: c::c_long = c::SYS_setresgid as c::c_long;
445
446    syscall! {
447        fn setresgid(rgid: c::gid_t, egid: c::gid_t, sgid: c::gid_t) via SYS -> c::c_int
448    }
449
450    unsafe {
451        ret(setresgid(
452            rgid.map_or(-1_i32 as u32, |x| x.as_raw()),
453            egid.map_or(-1_i32 as u32, |x| x.as_raw()),
454            sgid.map_or(-1_i32 as u32, |x| x.as_raw()),
455        ))
456    }
457}
458
459/// # Safety
460///
461/// The raw pointers must point to valid aligned memory.
462#[cfg(linux_kernel)]
463pub(crate) unsafe fn futex_val2(
464    uaddr: *const AtomicU32,
465    op: super::futex::Operation,
466    flags: futex::Flags,
467    val: u32,
468    val2: u32,
469    uaddr2: *const AtomicU32,
470    val3: u32,
471) -> io::Result<usize> {
472    // Pass `val2` in the least-significant bytes of the `timeout` argument.
473    // [“the kernel casts the timeout value first to unsigned long, then to
474    // uint32_t”], so we perform that exact conversion in reverse to create
475    // the pointer.
476    //
477    // [“the kernel casts the timeout value first to unsigned long, then to uint32_t”]: https://man7.org/linux/man-pages/man2/futex.2.html
478    let timeout = val2 as usize as *const Timespec;
479
480    #[cfg(all(
481        target_pointer_width = "32",
482        not(any(target_arch = "aarch64", target_arch = "x86_64"))
483    ))]
484    {
485        // TODO: Upstream this to the libc crate.
486        #[allow(non_upper_case_globals)]
487        const SYS_futex_time64: i32 = linux_raw_sys::general::__NR_futex_time64 as i32;
488
489        syscall! {
490            fn futex_time64(
491                uaddr: *const AtomicU32,
492                futex_op: c::c_int,
493                val: u32,
494                timeout: *const Timespec,
495                uaddr2: *const AtomicU32,
496                val3: u32
497            ) via SYS_futex_time64 -> c::ssize_t
498        }
499
500        ret_usize(futex_time64(
501            uaddr,
502            op as i32 | flags.bits() as i32,
503            val,
504            timeout,
505            uaddr2,
506            val3,
507        ))
508    }
509
510    #[cfg(any(
511        target_pointer_width = "64",
512        target_arch = "aarch64",
513        target_arch = "x86_64"
514    ))]
515    {
516        syscall! {
517            fn futex(
518                uaddr: *const AtomicU32,
519                futex_op: c::c_int,
520                val: u32,
521                timeout: *const Timespec,
522                uaddr2: *const AtomicU32,
523                val3: u32
524            ) via SYS_futex -> c::c_long
525        }
526
527        ret_usize(futex(
528            uaddr,
529            op as i32 | flags.bits() as i32,
530            val,
531            timeout.cast(),
532            uaddr2,
533            val3,
534        ) as isize)
535    }
536}
537
538/// # Safety
539///
540/// The raw pointers must point to valid aligned memory.
541#[cfg(linux_kernel)]
542pub(crate) unsafe fn futex_timeout(
543    uaddr: *const AtomicU32,
544    op: super::futex::Operation,
545    flags: futex::Flags,
546    val: u32,
547    timeout: Option<&Timespec>,
548    uaddr2: *const AtomicU32,
549    val3: u32,
550) -> io::Result<usize> {
551    #[cfg(all(
552        target_pointer_width = "32",
553        not(any(target_arch = "aarch64", target_arch = "x86_64"))
554    ))]
555    {
556        // TODO: Upstream this to the libc crate.
557        #[allow(non_upper_case_globals)]
558        const SYS_futex_time64: i32 = linux_raw_sys::general::__NR_futex_time64 as i32;
559
560        syscall! {
561            fn futex_time64(
562                uaddr: *const AtomicU32,
563                futex_op: c::c_int,
564                val: u32,
565                timeout: *const Timespec,
566                uaddr2: *const AtomicU32,
567                val3: u32
568            ) via SYS_futex_time64 -> c::ssize_t
569        }
570
571        ret_usize(futex_time64(
572            uaddr,
573            op as i32 | flags.bits() as i32,
574            val,
575            option_as_ptr(timeout),
576            uaddr2,
577            val3,
578        ))
579        .or_else(|err| {
580            // See the comments in `clock_gettime_via_syscall` about emulation.
581            if err == io::Errno::NOSYS {
582                futex_old_timespec(uaddr, op, flags, val, timeout, uaddr2, val3)
583            } else {
584                Err(err)
585            }
586        })
587    }
588
589    #[cfg(any(
590        target_pointer_width = "64",
591        target_arch = "aarch64",
592        target_arch = "x86_64"
593    ))]
594    {
595        syscall! {
596            fn futex(
597                uaddr: *const AtomicU32,
598                futex_op: c::c_int,
599                val: u32,
600                timeout: *const Timespec,
601                uaddr2: *const AtomicU32,
602                val3: u32
603            ) via SYS_futex -> c::c_long
604        }
605
606        ret_usize(futex(
607            uaddr,
608            op as i32 | flags.bits() as i32,
609            val,
610            option_as_ptr(timeout).cast(),
611            uaddr2,
612            val3,
613        ) as isize)
614    }
615}
616
617/// # Safety
618///
619/// The raw pointers must point to valid aligned memory.
620#[cfg(linux_kernel)]
621#[cfg(all(
622    target_pointer_width = "32",
623    not(any(target_arch = "aarch64", target_arch = "x86_64"))
624))]
625unsafe fn futex_old_timespec(
626    uaddr: *const AtomicU32,
627    op: super::futex::Operation,
628    flags: futex::Flags,
629    val: u32,
630    timeout: Option<&Timespec>,
631    uaddr2: *const AtomicU32,
632    val3: u32,
633) -> io::Result<usize> {
634    syscall! {
635        fn futex(
636            uaddr: *const AtomicU32,
637            futex_op: c::c_int,
638            val: u32,
639            timeout: *const linux_raw_sys::general::__kernel_old_timespec,
640            uaddr2: *const AtomicU32,
641            val3: u32
642        ) via SYS_futex -> c::c_long
643    }
644
645    let old_timeout = if let Some(timeout) = timeout {
646        Some(linux_raw_sys::general::__kernel_old_timespec {
647            tv_sec: timeout.tv_sec.try_into().map_err(|_| io::Errno::INVAL)?,
648            tv_nsec: timeout.tv_nsec.try_into().map_err(|_| io::Errno::INVAL)?,
649        })
650    } else {
651        None
652    };
653    ret_usize(futex(
654        uaddr,
655        op as i32 | flags.bits() as i32,
656        val,
657        option_as_ptr(old_timeout.as_ref()),
658        uaddr2,
659        val3,
660    ) as isize)
661}
662
663#[cfg(linux_kernel)]
664pub(crate) fn futex_waitv(
665    waiters: &[futex::Wait],
666    flags: futex::WaitvFlags,
667    timeout: Option<&Timespec>,
668    clockid: ClockId,
669) -> io::Result<usize> {
670    use futex::Wait as FutexWait;
671    use linux_raw_sys::general::__kernel_clockid_t as clockid_t;
672    syscall! {
673        fn futex_waitv(
674            waiters: *const FutexWait,
675            nr_futexes: c::c_uint,
676            flags: c::c_uint,
677            timeout: *const Timespec,
678            clockid: clockid_t
679        ) via SYS_futex_waitv -> c::c_int
680    }
681
682    let nr_futexes: c::c_uint = waiters.len().try_into().map_err(|_| io::Errno::INVAL)?;
683
684    unsafe {
685        ret_c_int(futex_waitv(
686            waiters.as_ptr(),
687            nr_futexes,
688            flags.bits(),
689            option_as_ptr(timeout).cast(),
690            clockid as _,
691        ))
692        .map(|n| n as usize)
693    }
694}
695
696#[cfg(linux_kernel)]
697#[inline]
698pub(crate) fn setgroups_thread(groups: &[crate::ugid::Gid]) -> io::Result<()> {
699    syscall! {
700        fn setgroups(size: c::size_t, list: *const c::gid_t) via SYS_setgroups -> c::c_int
701    }
702    ret(unsafe { setgroups(groups.len(), groups.as_ptr().cast()) })
703}
704
705#[cfg(any(linux_kernel, target_os = "dragonfly"))]
706#[inline]
707pub(crate) fn sched_getcpu() -> usize {
708    let r = unsafe { c::sched_getcpu() };
709    debug_assert!(r >= 0);
710    r as usize
711}
712
713#[cfg(any(freebsdlike, linux_kernel, target_os = "fuchsia"))]
714#[inline]
715pub(crate) fn sched_getaffinity(pid: Option<Pid>, cpuset: &mut RawCpuSet) -> io::Result<()> {
716    unsafe {
717        ret(c::sched_getaffinity(
718            Pid::as_raw(pid) as _,
719            core::mem::size_of::<RawCpuSet>(),
720            cpuset,
721        ))
722    }
723}
724
725#[cfg(any(freebsdlike, linux_kernel, target_os = "fuchsia"))]
726#[inline]
727pub(crate) fn sched_setaffinity(pid: Option<Pid>, cpuset: &RawCpuSet) -> io::Result<()> {
728    unsafe {
729        ret(c::sched_setaffinity(
730            Pid::as_raw(pid) as _,
731            core::mem::size_of::<RawCpuSet>(),
732            cpuset,
733        ))
734    }
735}
736
737#[inline]
738pub(crate) fn sched_yield() {
739    unsafe {
740        let _ = c::sched_yield();
741    }
742}
743
744// The `membarrier` syscall has a third argument, but it's only used when
745// the `flags` argument is `MEMBARRIER_CMD_FLAG_CPU`.
746#[cfg(linux_kernel)]
747syscall! {
748    fn membarrier_all(
749        cmd: c::c_int,
750        flags: c::c_uint
751    ) via SYS_membarrier -> c::c_int
752}
753
754#[cfg(linux_kernel)]
755pub(crate) fn membarrier_query() -> MembarrierQuery {
756    // glibc does not have a wrapper for `membarrier`; [the documentation]
757    // says to use `syscall`.
758    //
759    // [the documentation]: https://man7.org/linux/man-pages/man2/membarrier.2.html#NOTES
760    const MEMBARRIER_CMD_QUERY: u32 = 0;
761    unsafe {
762        match ret_u32(membarrier_all(MEMBARRIER_CMD_QUERY as i32, 0)) {
763            Ok(query) => MembarrierQuery::from_bits_retain(query),
764            Err(_) => MembarrierQuery::empty(),
765        }
766    }
767}
768
769#[cfg(linux_kernel)]
770pub(crate) fn membarrier(cmd: MembarrierCommand) -> io::Result<()> {
771    unsafe { ret(membarrier_all(cmd as i32, 0)) }
772}
773
774#[cfg(linux_kernel)]
775pub(crate) fn membarrier_cpu(cmd: MembarrierCommand, cpu: Cpuid) -> io::Result<()> {
776    const MEMBARRIER_CMD_FLAG_CPU: u32 = 1;
777
778    syscall! {
779        fn membarrier_cpu(
780            cmd: c::c_int,
781            flags: c::c_uint,
782            cpu_id: c::c_int
783        ) via SYS_membarrier -> c::c_int
784    }
785
786    unsafe {
787        ret(membarrier_cpu(
788            cmd as i32,
789            MEMBARRIER_CMD_FLAG_CPU,
790            bitcast!(cpu.as_raw()),
791        ))
792    }
793}