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