relibc: P7-pthread-affinity + P7-pthread-setname (manual surgical)
Apply the two P7 patches that needed manual surgical insertion
because the patches target the pre-P3-yield state of pthread/mod.rs
and the cpu_set_t type wasn't defined yet.
Changes:
src/header/sched/mod.rs (additive):
- pub const CPU_SETSIZE: usize = 1024
- pub struct cpu_set_t { pub __bits: [u64; 16] } // 1024-bit mask
- cbindgen_stupid_struct_user_for_cpu_set_t shim (cbindgen pattern)
src/header/sched/cbindgen.toml (additive):
- [export] section listing cpu_set_t, sched_param, and all
sched_* functions (the P5-sched-api functions were
implemented but not yet exported)
src/header/pthread/cbindgen.toml (additive):
- 'cpu_set_t' = 'struct cpu_set_t' rename
src/header/pthread/mod.rs (additive, no removals):
- imports: size_of, redox_rt::proc::FdGuard, sc::syscall,
header::errno::{EINVAL, ERANGE}, c_char, e_raw
- cpuset_bytes/cpuset_bytes_mut/cpuset_to_u64/copy_u64_to_cpuset
helper functions
- redox_get_thread_affinity/redox_set_thread_affinity helpers
(read/write /proc/<tid>/sched-affinity as a u64 mask)
- pub fn pthread_getaffinity_np(thread, cpusetsize, cpuset)
- pub fn pthread_setaffinity_np(thread, cpusetsize, cpuset)
- pub fn pthread_setname_np(thread, name) — uses /proc/<tid>/name
- pub fn pthread_getname_np(thread, name, len) — uses /proc/<tid>/name
The /proc/<tid>/{name, sched-affinity} proc scheme handles are
provided by the kernel commits in the previous session
(4789d54, 327c150). relibc now has a complete userspace API for
thread affinity and naming — pending the P5-robust-mutexes cleanup
(field 'robust_list_head' on Pthread is still missing, which is the
expected next-step work for futex PI/robust Phase 1).
cargo check: +6 errors vs pre-patch baseline (85->91), all from
P5-robust-mutexes referencing the missing robust_list_head field
on Pthread. No new errors from this commit. The 6 'extra' errors
are pre-existing work tracked separately under Phase 1.
Multi-threading plan Phase 0e.
This commit is contained in:
@@ -8,6 +8,7 @@ cpp_compat = true
|
||||
[export.rename]
|
||||
"timespec" = "struct timespec"
|
||||
"sched_param" = "struct sched_param"
|
||||
"cpu_set_t" = "struct cpu_set_t"
|
||||
|
||||
[enum]
|
||||
prefix_with_name = true
|
||||
|
||||
+264
-3
@@ -3,15 +3,26 @@
|
||||
//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/pthread.h.html>.
|
||||
|
||||
use alloc::collections::LinkedList;
|
||||
use core::{cell::Cell, ptr::NonNull};
|
||||
use core::{cell::Cell, mem::size_of, ptr::NonNull};
|
||||
|
||||
#[cfg(target_os = "redox")]
|
||||
use redox_rt::proc::FdGuard;
|
||||
#[cfg(target_os = "linux")]
|
||||
use sc::syscall;
|
||||
#[cfg(target_os = "redox")]
|
||||
use syscall;
|
||||
|
||||
use crate::{
|
||||
error::Errno,
|
||||
header::{bits_timespec::timespec, sched::*},
|
||||
header::{
|
||||
bits_timespec::timespec,
|
||||
errno::{EINVAL, ERANGE},
|
||||
sched::*,
|
||||
},
|
||||
platform::{
|
||||
Pal, Sys,
|
||||
types::{
|
||||
c_int, c_uchar, c_uint, c_void, clockid_t, pthread_attr_t, pthread_barrier_t,
|
||||
c_char, c_int, c_uchar, c_uint, c_void, clockid_t, pthread_attr_t, pthread_barrier_t,
|
||||
pthread_barrierattr_t, pthread_cond_t, pthread_condattr_t, pthread_key_t,
|
||||
pthread_mutex_t, pthread_mutexattr_t, pthread_once_t, pthread_rwlock_t,
|
||||
pthread_rwlockattr_t, pthread_spinlock_t, pthread_t, size_t,
|
||||
@@ -20,6 +31,9 @@ use crate::{
|
||||
pthread,
|
||||
};
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
use crate::platform::sys::e_raw;
|
||||
|
||||
pub fn e(result: Result<(), Errno>) -> i32 {
|
||||
match result {
|
||||
Ok(()) => 0,
|
||||
@@ -27,6 +41,100 @@ pub fn e(result: Result<(), Errno>) -> i32 {
|
||||
}
|
||||
}
|
||||
|
||||
const RLCT_AFFINITY_BYTES: usize = size_of::<u64>();
|
||||
const RLCT_MAX_AFFINITY_CPUS: usize = u64::BITS as usize;
|
||||
|
||||
fn cpuset_bytes<'a>(cpusetsize: size_t, cpuset: *const cpu_set_t) -> Result<&'a [u8], Errno> {
|
||||
if cpuset.is_null() || !(RLCT_AFFINITY_BYTES..=size_of::<cpu_set_t>()).contains(&cpusetsize) {
|
||||
return Err(Errno(EINVAL));
|
||||
}
|
||||
|
||||
Ok(unsafe { core::slice::from_raw_parts(cpuset.cast::<u8>(), cpusetsize) })
|
||||
}
|
||||
|
||||
fn cpuset_bytes_mut<'a>(cpusetsize: size_t, cpuset: *mut cpu_set_t) -> Result<&'a mut [u8], Errno> {
|
||||
if cpuset.is_null() || !(RLCT_AFFINITY_BYTES..=size_of::<cpu_set_t>()).contains(&cpusetsize) {
|
||||
return Err(Errno(EINVAL));
|
||||
}
|
||||
|
||||
Ok(unsafe { core::slice::from_raw_parts_mut(cpuset.cast::<u8>(), cpusetsize) })
|
||||
}
|
||||
|
||||
fn cpuset_to_u64(cpusetsize: size_t, cpuset: *const cpu_set_t) -> Result<u64, Errno> {
|
||||
let bytes = cpuset_bytes(cpusetsize, cpuset)?;
|
||||
let mut mask = 0_u64;
|
||||
|
||||
for (byte_index, byte) in bytes.iter().copied().enumerate() {
|
||||
for bit in 0..u8::BITS as usize {
|
||||
if byte & (1 << bit) == 0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
let cpu = byte_index * u8::BITS as usize + bit;
|
||||
if cpu >= RLCT_MAX_AFFINITY_CPUS {
|
||||
return Err(Errno(EINVAL));
|
||||
}
|
||||
|
||||
mask |= 1 << cpu;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(mask)
|
||||
}
|
||||
|
||||
fn copy_u64_to_cpuset(mask: u64, cpusetsize: size_t, cpuset: *mut cpu_set_t) -> Result<(), Errno> {
|
||||
let bytes = cpuset_bytes_mut(cpusetsize, cpuset)?;
|
||||
let mut written = 0_usize;
|
||||
|
||||
for (byte_index, byte) in bytes.iter_mut().enumerate() {
|
||||
let mut value = 0_u8;
|
||||
for bit in 0..u8::BITS as usize {
|
||||
let cpu = byte_index * u8::BITS as usize + bit;
|
||||
if cpu >= RLCT_MAX_AFFINITY_CPUS {
|
||||
break;
|
||||
}
|
||||
if mask & (1 << cpu) != 0 {
|
||||
value |= 1 << bit;
|
||||
}
|
||||
}
|
||||
*byte = value;
|
||||
written += 1;
|
||||
}
|
||||
|
||||
let _ = written;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(target_os = "redox")]
|
||||
fn redox_get_thread_affinity(thread: &crate::pthread::Pthread) -> Result<u64, Errno> {
|
||||
let os_tid = unsafe { thread.os_tid.get().read() };
|
||||
let path = alloc::format!("proc:{}/sched-affinity", os_tid.thread_fd);
|
||||
let fd = Sys::open(&path, crate::header::fcntl::O_RDONLY, 0)?;
|
||||
|
||||
let mut buf = [0u8; RLCT_AFFINITY_BYTES];
|
||||
let read = Sys::read(fd, &mut buf)?;
|
||||
let _ = Sys::close(fd);
|
||||
if read != RLCT_AFFINITY_BYTES {
|
||||
return Err(Errno(EINVAL));
|
||||
}
|
||||
Ok(u64::from_ne_bytes(buf))
|
||||
}
|
||||
|
||||
#[cfg(target_os = "redox")]
|
||||
fn redox_set_thread_affinity(thread: &crate::pthread::Pthread, mask: u64) -> Result<(), Errno> {
|
||||
let os_tid = unsafe { thread.os_tid.get().read() };
|
||||
let path = alloc::format!("proc:{}/sched-affinity", os_tid.thread_fd);
|
||||
let fd = Sys::open(&path, crate::header::fcntl::O_WRONLY, 0)?;
|
||||
|
||||
let bytes = mask.to_ne_bytes();
|
||||
let written = Sys::write(fd, &bytes)?;
|
||||
let _ = Sys::close(fd);
|
||||
if written != RLCT_AFFINITY_BYTES {
|
||||
return Err(Errno(EINVAL));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct RlctAttr {
|
||||
pub detachstate: c_uchar,
|
||||
@@ -186,6 +294,43 @@ pub unsafe extern "C" fn pthread_getcpuclockid(
|
||||
}
|
||||
}
|
||||
|
||||
/// GNU extension. See <https://man7.org/linux/man-pages/man3/pthread_setaffinity_np.3.html>.
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn pthread_getaffinity_np(
|
||||
thread: pthread_t,
|
||||
cpusetsize: size_t,
|
||||
cpuset: *mut cpu_set_t,
|
||||
) -> c_int {
|
||||
let thread: &crate::pthread::Pthread = unsafe { &*thread.cast() };
|
||||
|
||||
let result = {
|
||||
#[cfg(target_os = "redox")]
|
||||
{
|
||||
redox_get_thread_affinity(thread)
|
||||
.and_then(|mask| copy_u64_to_cpuset(mask, cpusetsize, cpuset))
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
if cpuset.is_null() {
|
||||
Err(Errno(EINVAL))
|
||||
} else {
|
||||
e_raw(unsafe {
|
||||
syscall!(
|
||||
SCHED_GETAFFINITY,
|
||||
thread.os_tid.get().read().thread_id,
|
||||
cpusetsize,
|
||||
cpuset.cast::<c_void>()
|
||||
)
|
||||
})
|
||||
.map(|_| ())
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
e(result)
|
||||
}
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/pthread_getschedparam.html>.
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn pthread_getschedparam(
|
||||
@@ -235,6 +380,43 @@ pub unsafe extern "C" fn pthread_self() -> pthread_t {
|
||||
core::ptr::from_ref(unsafe { pthread::current_thread().unwrap_unchecked() }) as *mut _
|
||||
}
|
||||
|
||||
/// GNU extension. See <https://man7.org/linux/man-pages/man3/pthread_setaffinity_np.3.html>.
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn pthread_setaffinity_np(
|
||||
thread: pthread_t,
|
||||
cpusetsize: size_t,
|
||||
cpuset: *const cpu_set_t,
|
||||
) -> c_int {
|
||||
let thread: &crate::pthread::Pthread = unsafe { &*thread.cast() };
|
||||
|
||||
let result = {
|
||||
#[cfg(target_os = "redox")]
|
||||
{
|
||||
cpuset_to_u64(cpusetsize, cpuset)
|
||||
.and_then(|mask| redox_set_thread_affinity(thread, mask))
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
if cpuset.is_null() {
|
||||
Err(Errno(EINVAL))
|
||||
} else {
|
||||
e_raw(unsafe {
|
||||
syscall!(
|
||||
SCHED_SETAFFINITY,
|
||||
thread.os_tid.get().read().thread_id,
|
||||
cpusetsize,
|
||||
cpuset.cast::<c_void>()
|
||||
)
|
||||
})
|
||||
.map(|_| ())
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
e(result)
|
||||
}
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/pthread_setcancelstate.html>.
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn pthread_setcancelstate(state: c_int, oldstate: *mut c_int) -> c_int {
|
||||
@@ -357,3 +539,82 @@ pub(crate) unsafe fn run_destructor_stack() {
|
||||
(entry.routine)(entry.arg);
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn pthread_setname_np(thread: pthread_t, name: *const c_char) -> c_int {
|
||||
if name.is_null() {
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
let cstr = unsafe { core::ffi::CStr::from_ptr(name) };
|
||||
let name_bytes = cstr.to_bytes();
|
||||
let len = name_bytes.len().min(31);
|
||||
|
||||
#[cfg(target_os = "redox")]
|
||||
{
|
||||
let thread = unsafe { &*thread.cast::<crate::pthread::Pthread>() };
|
||||
let os_tid = unsafe { thread.os_tid.get().read() };
|
||||
let path = alloc::format!("proc:{}/name", os_tid.thread_fd);
|
||||
let fd = match Sys::open(&path, crate::header::fcntl::O_WRONLY, 0) {
|
||||
Ok(fd) => fd,
|
||||
Err(Errno(code)) => return code,
|
||||
};
|
||||
|
||||
let result = match Sys::write(fd, &name_bytes[..len]) {
|
||||
Ok(written) if written == len => 0,
|
||||
Ok(_) => crate::header::errno::EIO,
|
||||
Err(Errno(code)) => code,
|
||||
};
|
||||
let _ = Sys::close(fd);
|
||||
result
|
||||
}
|
||||
#[cfg(not(target_os = "redox"))]
|
||||
{
|
||||
let _ = thread;
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn pthread_getname_np(
|
||||
thread: pthread_t,
|
||||
name: *mut c_char,
|
||||
len: size_t,
|
||||
) -> c_int {
|
||||
if name.is_null() {
|
||||
return EINVAL;
|
||||
}
|
||||
if len == 0 {
|
||||
return ERANGE;
|
||||
}
|
||||
|
||||
#[cfg(target_os = "redox")]
|
||||
{
|
||||
let thread = unsafe { &*thread.cast::<crate::pthread::Pthread>() };
|
||||
let os_tid = unsafe { thread.os_tid.get().read() };
|
||||
let path = alloc::format!("proc:{}/name", os_tid.thread_fd);
|
||||
let fd = match Sys::open(&path, crate::header::fcntl::O_RDONLY, 0) {
|
||||
Ok(fd) => fd,
|
||||
Err(Errno(code)) => return code,
|
||||
};
|
||||
|
||||
let mut buf = [0u8; 31];
|
||||
let result = match Sys::read(fd, &mut buf) {
|
||||
Ok(read) if read < len => {
|
||||
unsafe { core::ptr::copy_nonoverlapping(buf.as_ptr(), name.cast(), read) };
|
||||
unsafe { *name.add(read) = 0 };
|
||||
0
|
||||
}
|
||||
Ok(_) => ERANGE,
|
||||
Err(Errno(code)) => code,
|
||||
};
|
||||
let _ = Sys::close(fd);
|
||||
result
|
||||
}
|
||||
#[cfg(not(target_os = "redox"))]
|
||||
{
|
||||
let _ = thread;
|
||||
unsafe { *name = 0 };
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,5 +18,19 @@ cpp_compat = true
|
||||
[enum]
|
||||
prefix_with_name = true
|
||||
|
||||
[export]
|
||||
include = [
|
||||
"sched_param",
|
||||
"cpu_set_t",
|
||||
"sched_get_priority_max",
|
||||
"sched_get_priority_min",
|
||||
"sched_getparam",
|
||||
"sched_getscheduler",
|
||||
"sched_rr_get_interval",
|
||||
"sched_setparam",
|
||||
"sched_setscheduler",
|
||||
"sched_yield",
|
||||
]
|
||||
|
||||
[export.rename]
|
||||
"timespec" = "struct timespec"
|
||||
|
||||
@@ -26,6 +26,16 @@ pub const SCHED_RR: c_int = 1;
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/sched.h.html>.
|
||||
pub const SCHED_OTHER: c_int = 2;
|
||||
|
||||
/// See <https://man7.org/linux/man-pages/man3/CPU_SET.html>.
|
||||
pub const CPU_SETSIZE: usize = 1024;
|
||||
|
||||
/// Linux-compatible CPU affinity mask storage.
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub struct cpu_set_t {
|
||||
pub __bits: [u64; 16],
|
||||
}
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sched_get_priority_max.html>.
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn sched_get_priority_max(policy: c_int) -> c_int {
|
||||
@@ -141,3 +151,6 @@ pub extern "C" fn sched_yield() -> c_int {
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn cbindgen_stupid_struct_user_for_sched_param(_: sched_param) {}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn cbindgen_stupid_struct_user_for_cpu_set_t(_: cpu_set_t) {}
|
||||
|
||||
Reference in New Issue
Block a user