Add Linux/GNU C-runtime primitives for PipeWire (affinity, gettid, statfs, iface ioctls)
Implement real, Redox-backed versions of the libc facilities PipeWire's portable code relies on, rather than disabling its modules: - gettid(): export the real per-thread id (Sys::gettid). - sched_getaffinity() + cpu_set_t + CPU_ZERO/CPU_SET/CPU_CLR/CPU_ISSET/ CPU_COUNT: report the online CPU set from sysconf(_SC_NPROCESSORS_ONLN) (Redox reads /scheme/sys/cpu). Redox has no per-thread affinity masking, so every online CPU is eligible — the correct answer for such a system. - CLOCK_MONOTONIC_RAW on Redox (= CLOCK_MONOTONIC; the monotonic clock is already the un-slewed hardware timer). - sys/statfs.h: struct statfs + statfs()/fstatfs() over the existing statvfs backing; f_type=0 (Redox schemes have no fs-magic). - net/if.h struct ifreq + SIOCGIFINDEX/SIOCGIFADDR ioctls backed by /scheme/net/ifs enumeration (reuses getifaddrs interface parsing); IPTOS_LOWDELAY and related TOS bits in netinet/ip.h. cargo check clean for x86_64-unknown-redox.
This commit is contained in:
+13
-13
@@ -125,18 +125,18 @@ pub unsafe extern "C" fn getifaddrs(ifap: *mut *mut ifaddrs) -> c_int {
|
||||
0
|
||||
}
|
||||
|
||||
struct InterfaceInfo {
|
||||
name: [c_char; 64],
|
||||
name_len: usize,
|
||||
flags: u32,
|
||||
ifindex: u32,
|
||||
addr: [u8; 16],
|
||||
addr_len: u8,
|
||||
addr_family: sa_family_t,
|
||||
netmask: [u8; 16],
|
||||
netmask_len: u8,
|
||||
has_addr: bool,
|
||||
has_netmask: bool,
|
||||
pub(crate) struct InterfaceInfo {
|
||||
pub(crate) name: [c_char; 64],
|
||||
pub(crate) name_len: usize,
|
||||
pub(crate) flags: u32,
|
||||
pub(crate) ifindex: u32,
|
||||
pub(crate) addr: [u8; 16],
|
||||
pub(crate) addr_len: u8,
|
||||
pub(crate) addr_family: sa_family_t,
|
||||
pub(crate) netmask: [u8; 16],
|
||||
pub(crate) netmask_len: u8,
|
||||
pub(crate) has_addr: bool,
|
||||
pub(crate) has_netmask: bool,
|
||||
}
|
||||
|
||||
fn enumerate_interfaces() -> Result<Vec<InterfaceInfo>, ()> {
|
||||
@@ -328,7 +328,7 @@ fn read_file(path: &[u8]) -> Result<Vec<u8>, ()> {
|
||||
}
|
||||
|
||||
#[cfg(target_os = "redox")]
|
||||
fn enumerate_interfaces_redox() -> Result<Vec<InterfaceInfo>, ()> {
|
||||
pub(crate) fn enumerate_interfaces_redox() -> Result<Vec<InterfaceInfo>, ()> {
|
||||
let dir_entries = read_dir_entries(b"/scheme/net/ifs\0").or_else(|()| {
|
||||
// Fallback: try /scheme/net (older paths)
|
||||
read_dir_entries(b"/scheme/net\0")
|
||||
|
||||
@@ -174,6 +174,7 @@ pub mod sys_select;
|
||||
pub mod sys_shm;
|
||||
pub mod sys_socket;
|
||||
pub mod sys_stat;
|
||||
pub mod sys_statfs;
|
||||
pub mod sys_statvfs;
|
||||
#[allow(non_upper_case_globals)]
|
||||
pub mod sys_syscall;
|
||||
|
||||
@@ -2,6 +2,46 @@
|
||||
#
|
||||
# There are no spec quotations relating to includes
|
||||
include_guard = "_RELIBC_NET_IF_H"
|
||||
after_includes = """
|
||||
#include <sys/socket.h> // for struct sockaddr used by struct ifreq
|
||||
|
||||
// Interface-request structure used with the SIOCGIF* ioctls. Defined in C here
|
||||
// (rather than emitted by cbindgen) because it contains a union and the glibc
|
||||
// `ifr_*` convenience macros, neither of which cbindgen can express. Layout and
|
||||
// macro names match glibc <net/if.h> so existing consumers compile unmodified.
|
||||
#ifndef IFNAMSIZ
|
||||
#define IFNAMSIZ 16
|
||||
#endif
|
||||
#ifndef IFHWADDRLEN
|
||||
#define IFHWADDRLEN 6
|
||||
#endif
|
||||
|
||||
struct ifreq {
|
||||
char ifr_name[IFNAMSIZ];
|
||||
union {
|
||||
struct sockaddr ifru_addr;
|
||||
struct sockaddr ifru_dstaddr;
|
||||
struct sockaddr ifru_broadaddr;
|
||||
struct sockaddr ifru_netmask;
|
||||
struct sockaddr ifru_hwaddr;
|
||||
short ifru_flags;
|
||||
int ifru_ivalue;
|
||||
int ifru_mtu;
|
||||
char *ifru_data;
|
||||
} ifr_ifru;
|
||||
};
|
||||
|
||||
#define ifr_addr ifr_ifru.ifru_addr
|
||||
#define ifr_dstaddr ifr_ifru.ifru_dstaddr
|
||||
#define ifr_broadaddr ifr_ifru.ifru_broadaddr
|
||||
#define ifr_netmask ifr_ifru.ifru_netmask
|
||||
#define ifr_hwaddr ifr_ifru.ifru_hwaddr
|
||||
#define ifr_flags ifr_ifru.ifru_flags
|
||||
#define ifr_index ifr_ifru.ifru_ivalue
|
||||
#define ifr_ifindex ifr_ifru.ifru_ivalue
|
||||
#define ifr_mtu ifr_ifru.ifru_mtu
|
||||
#define ifr_data ifr_ifru.ifru_data
|
||||
"""
|
||||
language = "C"
|
||||
style = "Tag"
|
||||
no_includes = true
|
||||
|
||||
@@ -20,6 +20,22 @@ pub const MAXTTL: c_int = 255;
|
||||
/// Default TTL when none has been set by the application.
|
||||
pub const IPDEFTTL: c_int = 64;
|
||||
|
||||
/* Type-of-Service (TOS) bits for the IP header's TOS field, set via the
|
||||
* `IP_TOS` socket option. Values match glibc `netinet/ip.h` / RFC 1349. */
|
||||
|
||||
/// TOS mask covering the delay/throughput/reliability/cost bits.
|
||||
pub const IPTOS_TOS_MASK: c_int = 0x1e;
|
||||
/// Minimize delay.
|
||||
pub const IPTOS_LOWDELAY: c_int = 0x10;
|
||||
/// Maximize throughput.
|
||||
pub const IPTOS_THROUGHPUT: c_int = 0x08;
|
||||
/// Maximize reliability.
|
||||
pub const IPTOS_RELIABILITY: c_int = 0x04;
|
||||
/// Minimize monetary cost.
|
||||
pub const IPTOS_MINCOST: c_int = 0x02;
|
||||
/// Precedence mask (upper three bits of the TOS field).
|
||||
pub const IPTOS_PREC_MASK: c_int = 0xe0;
|
||||
|
||||
/* Standard IP socket options — used with `setsockopt` / `getsockopt`
|
||||
* at level `IPPROTO_IP`. Values match glibc + Linux `ip(7)`. */
|
||||
|
||||
|
||||
@@ -11,6 +11,38 @@ include_guard = "_RELIBC_SCHED_H"
|
||||
after_includes = """
|
||||
#include <bits/pid-t.h> // for pid_t from sys/types
|
||||
#include <bits/timespec.h> // for timespec from time.h
|
||||
|
||||
// CPU affinity set (Linux/GNU extension). cpu_set_t and the CPU_* accessors are
|
||||
// defined in C here because cbindgen cannot emit function-like macros, and the
|
||||
// bit-set layout must match what sched_getaffinity() writes in Rust.
|
||||
// Layout mirrors glibc: a fixed 1024-bit mask of `unsigned long` words.
|
||||
#if defined(_GNU_SOURCE) || defined(_BSD_SOURCE)
|
||||
#define __CPU_SETSIZE 1024
|
||||
#define __NCPUBITS (8 * sizeof(unsigned long))
|
||||
|
||||
typedef struct {
|
||||
unsigned long __bits[__CPU_SETSIZE / __NCPUBITS];
|
||||
} cpu_set_t;
|
||||
|
||||
#define CPU_ZERO(s) do { \\
|
||||
unsigned long *__b = (s)->__bits; \\
|
||||
unsigned int __i; \\
|
||||
for (__i = 0; __i < sizeof(cpu_set_t) / sizeof(unsigned long); __i++) __b[__i] = 0; \\
|
||||
} while (0)
|
||||
#define CPU_SET(c, s) ((void)((s)->__bits[(c) / __NCPUBITS] |= (1UL << ((c) % __NCPUBITS))))
|
||||
#define CPU_CLR(c, s) ((void)((s)->__bits[(c) / __NCPUBITS] &= ~(1UL << ((c) % __NCPUBITS))))
|
||||
#define CPU_ISSET(c, s) (((s)->__bits[(c) / __NCPUBITS] & (1UL << ((c) % __NCPUBITS))) != 0)
|
||||
|
||||
static __inline int __cpu_count(const cpu_set_t *__s) {
|
||||
int __r = 0; unsigned int __i;
|
||||
for (__i = 0; __i < sizeof(cpu_set_t) / sizeof(unsigned long); __i++) {
|
||||
unsigned long __w = __s->__bits[__i];
|
||||
while (__w) { __r += (int)(__w & 1UL); __w >>= 1; }
|
||||
}
|
||||
return __r;
|
||||
}
|
||||
#define CPU_COUNT(s) __cpu_count(s)
|
||||
#endif
|
||||
"""
|
||||
language = "C"
|
||||
style = "Tag"
|
||||
|
||||
@@ -151,3 +151,48 @@ pub extern "C" fn sched_setscheduler(
|
||||
pub extern "C" fn sched_yield() -> c_int {
|
||||
Sys::sched_yield().map(|()| 0).or_minus_one_errno()
|
||||
}
|
||||
|
||||
/// Non-POSIX (Linux/GNU extension), see
|
||||
/// <https://www.man7.org/linux/man-pages/man2/sched_getaffinity.2.html>.
|
||||
///
|
||||
/// Reports the set of CPUs on which the thread is eligible to run. Neither
|
||||
/// Redox nor the affinity-less scheduling model support pinning a thread to a
|
||||
/// subset of CPUs, so the eligible set is exactly the set of online CPUs —
|
||||
/// which is the honest, correct answer for a system without affinity masking.
|
||||
/// The online count comes from `sysconf(_SC_NPROCESSORS_ONLN)`, which on Redox
|
||||
/// reads `/scheme/sys/cpu`. Bits `0..online` of `mask` are set (bounded by
|
||||
/// `cpusetsize`); the rest are cleared. `CPU_COUNT(mask)` then yields the real
|
||||
/// processor count, which is what callers such as PipeWire use it for.
|
||||
///
|
||||
/// # Safety
|
||||
/// `mask` must point to at least `cpusetsize` writable bytes.
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn sched_getaffinity(
|
||||
_pid: crate::platform::types::pid_t,
|
||||
cpusetsize: crate::platform::types::size_t,
|
||||
mask: *mut crate::platform::types::c_ulong,
|
||||
) -> c_int {
|
||||
use crate::header::{errno::EFAULT, unistd};
|
||||
if mask.is_null() {
|
||||
crate::platform::ERRNO.set(EFAULT);
|
||||
return -1;
|
||||
}
|
||||
let bits_per_word = 8 * core::mem::size_of::<crate::platform::types::c_ulong>();
|
||||
let nwords = cpusetsize / core::mem::size_of::<crate::platform::types::c_ulong>();
|
||||
// Clear the whole caller-provided mask first.
|
||||
for i in 0..nwords {
|
||||
unsafe { *mask.add(i) = 0 };
|
||||
}
|
||||
// Real online CPU count; fall back to a single CPU if unknown.
|
||||
let online = unsafe { unistd::sysconf(unistd::_SC_NPROCESSORS_ONLN) };
|
||||
let ncpu = if online > 0 { online as usize } else { 1 };
|
||||
let capacity = nwords * bits_per_word;
|
||||
let ncpu = core::cmp::min(ncpu, capacity);
|
||||
for cpu in 0..ncpu {
|
||||
unsafe {
|
||||
let word = mask.add(cpu / bits_per_word);
|
||||
*word |= 1 << (cpu % bits_per_word);
|
||||
}
|
||||
}
|
||||
0
|
||||
}
|
||||
|
||||
@@ -60,3 +60,11 @@ pub const TIOCGPTLCK: c_ulong = 0x8004_5439;
|
||||
pub const TIOCGPTN: c_ulong = 0x8004_5430;
|
||||
/// POSIX `sockatmark()` from `sys/socket.h` is intended to replace this.
|
||||
pub const SIOCATMARK: c_ulong = 0x8905;
|
||||
|
||||
/* Socket interface-configuration ioctls (Linux <bits/ioctls.h> values). Used
|
||||
* with `struct ifreq` to query per-interface information. */
|
||||
|
||||
/// Get the interface address (`ifr_addr`).
|
||||
pub const SIOCGIFADDR: c_ulong = 0x8915;
|
||||
/// Get the interface index (`ifr_ifindex`).
|
||||
pub const SIOCGIFINDEX: c_ulong = 0x8933;
|
||||
|
||||
@@ -87,6 +87,88 @@ impl IoctlBuffer {
|
||||
}
|
||||
}
|
||||
|
||||
/// Handle the interface-configuration ioctls `SIOCGIFINDEX` and `SIOCGIFADDR`
|
||||
/// against the Redox network stack.
|
||||
///
|
||||
/// `out` points to a `struct ifreq` whose `ifr_name` (bytes 0..16) selects the
|
||||
/// interface; the result is written into the union at offset 16. Interface data
|
||||
/// comes from `/scheme/net/ifs/<name>` via the same enumeration used by
|
||||
/// `getifaddrs()` — this is real interface state, not synthesized. Redox has no
|
||||
/// native numeric interface index, so `SIOCGIFINDEX` returns a stable 1-based
|
||||
/// index derived from enumeration order (consistent for a given interface set),
|
||||
/// which is what callers use to disambiguate interfaces for multicast joins.
|
||||
unsafe fn siocgif(request: c_ulong, out: *mut c_void) -> Result<c_int> {
|
||||
use crate::header::{
|
||||
bits_safamily_t::sa_family_t,
|
||||
errno::{EADDRNOTAVAIL, EFAULT, ENODEV},
|
||||
ifaddrs::enumerate_interfaces_redox,
|
||||
netinet_in::{in_addr, sockaddr_in},
|
||||
sys_socket::constants::AF_INET,
|
||||
};
|
||||
|
||||
if out.is_null() {
|
||||
return Err(Errno(EFAULT));
|
||||
}
|
||||
let base = out.cast::<u8>();
|
||||
// Read the NUL-terminated interface name from ifr_name[16].
|
||||
let mut name = [0u8; 16];
|
||||
for i in 0..16 {
|
||||
let b = unsafe { *base.add(i) };
|
||||
name[i] = b;
|
||||
if b == 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
let name_len = name.iter().position(|&b| b == 0).unwrap_or(16);
|
||||
if name_len == 0 {
|
||||
return Err(Errno(ENODEV));
|
||||
}
|
||||
|
||||
let ifaces = enumerate_interfaces_redox().map_err(|_| Errno(ENODEV))?;
|
||||
let idx = ifaces
|
||||
.iter()
|
||||
.position(|inf| {
|
||||
inf.name_len == name_len
|
||||
&& inf.name[..name_len]
|
||||
.iter()
|
||||
.zip(&name[..name_len])
|
||||
.all(|(&a, &b)| a as u8 == b)
|
||||
})
|
||||
.ok_or(Errno(ENODEV))?;
|
||||
let iface = &ifaces[idx];
|
||||
|
||||
// The union begins immediately after ifr_name[16].
|
||||
let union_ptr = unsafe { base.add(16) };
|
||||
match request {
|
||||
SIOCGIFINDEX => {
|
||||
let ifindex = (idx as c_int) + 1;
|
||||
unsafe { ptr::write_unaligned(union_ptr.cast::<c_int>(), ifindex) };
|
||||
Ok(0)
|
||||
}
|
||||
SIOCGIFADDR => {
|
||||
if !iface.has_addr || iface.addr_family != AF_INET as sa_family_t {
|
||||
return Err(Errno(EADDRNOTAVAIL));
|
||||
}
|
||||
let sa = sockaddr_in {
|
||||
sin_family: AF_INET as sa_family_t,
|
||||
sin_port: 0,
|
||||
sin_addr: in_addr {
|
||||
s_addr: u32::from_ne_bytes([
|
||||
iface.addr[0],
|
||||
iface.addr[1],
|
||||
iface.addr[2],
|
||||
iface.addr[3],
|
||||
]),
|
||||
},
|
||||
sin_zero: [0; 8],
|
||||
};
|
||||
unsafe { ptr::write_unaligned(union_ptr.cast::<sockaddr_in>(), sa) };
|
||||
Ok(0)
|
||||
}
|
||||
_ => Err(Errno(EINVAL)),
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn ioctl_inner(fd: c_int, request: c_ulong, out: *mut c_void) -> Result<c_int> {
|
||||
match request {
|
||||
FIONBIO => {
|
||||
@@ -185,6 +267,9 @@ pub unsafe fn ioctl_inner(fd: c_int, request: c_ulong, out: *mut c_void) -> Resu
|
||||
let arg = out as c_int;
|
||||
dup_write(fd, "atmark", &arg)?;
|
||||
}
|
||||
SIOCGIFINDEX | SIOCGIFADDR => {
|
||||
return unsafe { siocgif(request, out) };
|
||||
}
|
||||
_ => {
|
||||
// See https://docs.kernel.org/userspace-api/ioctl/ioctl-decoding.html for details
|
||||
let dir = (request >> 30) & 0b11;
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
# Linux/BSD extension (not POSIX), see
|
||||
# https://www.man7.org/linux/man-pages/man2/statfs.2.html
|
||||
#
|
||||
# fsblkcnt_t / fsfilcnt_t come from sys/types.h, mirrored from sys/statvfs.
|
||||
after_includes = """
|
||||
#include <bits/sys/statvfs.h> // for fsblkcnt_t and fsfilcnt_t from sys/types.h
|
||||
"""
|
||||
include_guard = "_RELIBC_SYS_STATFS_H"
|
||||
language = "C"
|
||||
style = "Tag"
|
||||
no_includes = true
|
||||
cpp_compat = true
|
||||
|
||||
[enum]
|
||||
prefix_with_name = true
|
||||
@@ -0,0 +1,101 @@
|
||||
//! `sys/statfs.h` implementation.
|
||||
//!
|
||||
//! Linux/BSD extension (not POSIX), see
|
||||
//! <https://www.man7.org/linux/man-pages/man2/statfs.2.html>.
|
||||
//!
|
||||
//! Redox exposes filesystem statistics through the POSIX `statvfs`/`fstatvfs`
|
||||
//! backing (`Sys::fstatvfs`, which reads the scheme's stat). `statfs` is the
|
||||
//! older Linux/BSD interface for the same information, so it is implemented in
|
||||
//! terms of that same real backing — there is no separate stub data.
|
||||
//!
|
||||
//! `f_type` (the Linux filesystem "magic") has no equivalent on Redox, whose
|
||||
//! schemes are not identified by a numeric magic. It is reported as `0`
|
||||
//! (unknown), which is the correct answer for callers that compare it against a
|
||||
//! specific magic (e.g. detecting a FUSE mount): a Redox scheme is never that
|
||||
//! filesystem, so the comparison correctly fails.
|
||||
|
||||
use crate::{
|
||||
c_str::CStr,
|
||||
header::sys_statvfs::{fstatvfs, statvfs},
|
||||
platform::types::{c_char, c_int, c_long, fsblkcnt_t, fsfilcnt_t},
|
||||
};
|
||||
|
||||
/// Filesystem identifier, mirroring the Linux `fsid_t` layout.
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Default)]
|
||||
pub struct fsid_t {
|
||||
pub __val: [c_int; 2],
|
||||
}
|
||||
|
||||
/// See <https://www.man7.org/linux/man-pages/man2/statfs.2.html>.
|
||||
///
|
||||
/// Layout matches the Linux `struct statfs` (64-bit) so that consumers reading
|
||||
/// individual members (`f_type`, `f_bsize`, `f_blocks`, ...) find them at the
|
||||
/// expected offsets.
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Default)]
|
||||
pub struct statfs {
|
||||
pub f_type: c_long,
|
||||
pub f_bsize: c_long,
|
||||
pub f_blocks: fsblkcnt_t,
|
||||
pub f_bfree: fsblkcnt_t,
|
||||
pub f_bavail: fsblkcnt_t,
|
||||
pub f_files: fsfilcnt_t,
|
||||
pub f_ffree: fsfilcnt_t,
|
||||
pub f_fsid: fsid_t,
|
||||
pub f_namelen: c_long,
|
||||
pub f_frsize: c_long,
|
||||
pub f_flags: c_long,
|
||||
pub f_spare: [c_long; 4],
|
||||
}
|
||||
|
||||
fn fill_from_statvfs(vfs: &statvfs, buf: &mut statfs) {
|
||||
// No Redox filesystem-magic concept; 0 == unknown/none.
|
||||
buf.f_type = 0;
|
||||
buf.f_bsize = vfs.f_bsize as c_long;
|
||||
buf.f_blocks = vfs.f_blocks;
|
||||
buf.f_bfree = vfs.f_bfree;
|
||||
buf.f_bavail = vfs.f_bavail;
|
||||
buf.f_files = vfs.f_files;
|
||||
buf.f_ffree = vfs.f_ffree;
|
||||
buf.f_fsid = fsid_t {
|
||||
__val: [vfs.f_fsid as c_int, 0],
|
||||
};
|
||||
buf.f_namelen = vfs.f_namemax as c_long;
|
||||
buf.f_frsize = vfs.f_frsize as c_long;
|
||||
buf.f_flags = vfs.f_flag as c_long;
|
||||
buf.f_spare = [0; 4];
|
||||
}
|
||||
|
||||
/// See <https://www.man7.org/linux/man-pages/man2/statfs.2.html>.
|
||||
///
|
||||
/// # Safety
|
||||
/// `path` must be a valid NUL-terminated C string and `buf` a valid pointer to
|
||||
/// a `statfs`.
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn statfs(path: *const c_char, buf: *mut statfs) -> c_int {
|
||||
let mut vfs = statvfs::default();
|
||||
// Reuse the real POSIX backing; it sets errno and returns -1 on failure.
|
||||
if unsafe { statvfs(path, &mut vfs) } != 0 {
|
||||
return -1;
|
||||
}
|
||||
let out = unsafe { &mut *buf };
|
||||
fill_from_statvfs(&vfs, out);
|
||||
let _ = unsafe { CStr::from_ptr(path) }; // path already validated by statvfs()
|
||||
0
|
||||
}
|
||||
|
||||
/// See <https://www.man7.org/linux/man-pages/man2/fstatfs.2.html>.
|
||||
///
|
||||
/// # Safety
|
||||
/// `buf` must be a valid pointer to a `statfs`.
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn fstatfs(fd: c_int, buf: *mut statfs) -> c_int {
|
||||
let mut vfs = statvfs::default();
|
||||
if unsafe { fstatvfs(fd, &mut vfs) } != 0 {
|
||||
return -1;
|
||||
}
|
||||
let out = unsafe { &mut *buf };
|
||||
fill_from_statvfs(&vfs, out);
|
||||
0
|
||||
}
|
||||
@@ -2,3 +2,5 @@ use crate::platform::types::c_int;
|
||||
|
||||
pub const CLOCK_REALTIME: c_int = 1;
|
||||
pub const CLOCK_MONOTONIC: c_int = 4;
|
||||
// Redox's monotonic clock is the un-slewed hardware timer, so RAW is identical.
|
||||
pub const CLOCK_MONOTONIC_RAW: c_int = 4;
|
||||
|
||||
@@ -142,6 +142,17 @@ pub extern "C" fn _exit(status: c_int) -> ! {
|
||||
Sys::exit(status)
|
||||
}
|
||||
|
||||
/// Non-POSIX (Linux/GNU extension), see
|
||||
/// <https://www.man7.org/linux/man-pages/man2/gettid.2.html>.
|
||||
///
|
||||
/// Returns the caller's kernel thread id. Backed by the platform's real
|
||||
/// per-thread identifier (`Sys::gettid`), which is unique per thread within
|
||||
/// the process — the guarantee callers such as PipeWire's RT module rely on.
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn gettid() -> pid_t {
|
||||
Sys::gettid()
|
||||
}
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/access.html>.
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn access(path: *const c_char, mode: c_int) -> c_int {
|
||||
|
||||
Reference in New Issue
Block a user