327 lines
9.9 KiB
Diff
327 lines
9.9 KiB
Diff
diff --git a/src/header/pthread/mod.rs b/src/header/pthread/mod.rs
|
|
index c742a42..008090a 100644
|
|
--- a/src/header/pthread/mod.rs
|
|
+++ b/src/header/pthread/mod.rs
|
|
@@ -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,96 @@ 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_u64 << 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)?;
|
|
+ bytes.fill(0);
|
|
+
|
|
+ for (byte_index, dst) in bytes.iter_mut().take(RLCT_AFFINITY_BYTES).enumerate() {
|
|
+ *dst = (mask >> (byte_index * u8::BITS as usize)) as u8;
|
|
+ }
|
|
+
|
|
+ Ok(())
|
|
+}
|
|
+
|
|
+#[cfg(target_os = "redox")]
|
|
+fn redox_set_thread_affinity(thread: &pthread::Pthread, mask: u64) -> Result<(), Errno> {
|
|
+ let mut kernel_cpuset = cpu_set_t::default();
|
|
+ kernel_cpuset.__bits[0] = mask;
|
|
+
|
|
+ let handle = FdGuard::new(unsafe {
|
|
+ syscall::dup(thread.os_tid.get().read().thread_fd, b"sched-affinity")?
|
|
+ });
|
|
+ let _ = handle.write(unsafe {
|
|
+ core::slice::from_raw_parts(
|
|
+ core::ptr::from_ref(&kernel_cpuset).cast::<u8>(),
|
|
+ size_of::<cpu_set_t>(),
|
|
+ )
|
|
+ })?;
|
|
+
|
|
+ Ok(())
|
|
+}
|
|
+
|
|
+#[cfg(target_os = "redox")]
|
|
+fn redox_get_thread_affinity(thread: &pthread::Pthread) -> Result<u64, Errno> {
|
|
+ let handle = FdGuard::new(unsafe {
|
|
+ syscall::dup(thread.os_tid.get().read().thread_fd, b"sched-affinity")?
|
|
+ });
|
|
+ let mut kernel_cpuset = cpu_set_t::default();
|
|
+ let _ = handle.read(unsafe {
|
|
+ core::slice::from_raw_parts_mut(
|
|
+ core::ptr::from_mut(&mut kernel_cpuset).cast::<u8>(),
|
|
+ size_of::<cpu_set_t>(),
|
|
+ )
|
|
+ })?;
|
|
+
|
|
+ if kernel_cpuset.__bits[1..].iter().any(|bits| *bits != 0) {
|
|
+ return Err(Errno(EINVAL));
|
|
+ }
|
|
+
|
|
+ Ok(kernel_cpuset.__bits[0])
|
|
+}
|
|
+
|
|
#[derive(Clone)]
|
|
pub(crate) struct RlctAttr {
|
|
pub detachstate: c_uchar,
|
|
@@ -186,6 +290,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: &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 +376,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: &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 {
|
|
@@ -307,6 +485,13 @@ pub unsafe extern "C" fn pthread_testcancel() {
|
|
unsafe { pthread::testcancel() };
|
|
}
|
|
|
|
+/// <https://man7.org/linux/man-pages/man3/pthread_yield.3.html>
|
|
+///
|
|
+/// Non-standard GNU extension. Prefer `sched_yield()` instead.
|
|
+pub extern "C" fn pthread_yield() {
|
|
+ let _ = Sys::sched_yield();
|
|
+}
|
|
+
|
|
// Must be the same struct as defined in the pthread_cleanup_push macro.
|
|
#[repr(C)]
|
|
pub(crate) struct CleanupLinkedListEntry {
|
|
@@ -350,3 +535,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
|
|
+ }
|
|
+}
|