Files
RedBear-OS/src/header/pthread/mod.rs
T
2026-01-21 00:41:57 +01:00

353 lines
10 KiB
Rust

//! `pthread.h` implementation.
//!
//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/pthread.h.html>.
use alloc::collections::LinkedList;
use core::{cell::Cell, ptr::NonNull};
use crate::{
error::Errno,
header::{sched::*, time::timespec},
platform::{
Pal, Sys,
types::{
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,
},
},
pthread,
};
pub fn e(result: Result<(), Errno>) -> i32 {
match result {
Ok(()) => 0,
Err(Errno(error)) => error,
}
}
#[derive(Clone, Copy)]
pub(crate) struct RlctAttr {
pub detachstate: c_uchar,
pub inheritsched: c_uchar,
pub schedpolicy: c_uchar,
pub scope: c_uchar,
pub guardsize: size_t,
pub stacksize: size_t,
pub stack: size_t,
pub param: sched_param,
#[cfg(target_pointer_width = "32")]
_pad: [u8; 12],
}
pub const _POSIX_THREADS: c_int = 1;
pub const PTHREAD_BARRIER_SERIAL_THREAD: c_int = -1;
pub const PTHREAD_CANCEL_ASYNCHRONOUS: c_int = 0;
pub const PTHREAD_CANCEL_ENABLE: c_int = 1;
pub const PTHREAD_CANCEL_DEFERRED: c_int = 2;
pub const PTHREAD_CANCEL_DISABLE: c_int = 3;
pub const PTHREAD_CANCELED: *mut c_void = (!0_usize) as *mut c_void;
pub const PTHREAD_CREATE_DETACHED: c_int = 0;
pub const PTHREAD_CREATE_JOINABLE: c_int = 1;
pub const PTHREAD_EXPLICIT_SCHED: c_int = 0;
pub const PTHREAD_INHERIT_SCHED: c_int = 1;
pub const PTHREAD_MUTEX_DEFAULT: c_int = 0;
pub const PTHREAD_MUTEX_ERRORCHECK: c_int = 1;
pub const PTHREAD_MUTEX_NORMAL: c_int = 2;
pub const PTHREAD_MUTEX_RECURSIVE: c_int = 3;
pub const PTHREAD_MUTEX_ROBUST: c_int = 0;
pub const PTHREAD_MUTEX_STALLED: c_int = 1;
pub const PTHREAD_PRIO_INHERIT: c_int = 0;
pub const PTHREAD_PRIO_NONE: c_int = 0;
pub const PTHREAD_PRIO_PROTECT: c_int = 0;
pub const PTHREAD_PROCESS_SHARED: c_int = 0;
pub const PTHREAD_PROCESS_PRIVATE: c_int = 1;
pub const PTHREAD_SCOPE_PROCESS: c_int = 0;
pub const PTHREAD_SCOPE_SYSTEM: c_int = 1;
pub mod attr;
pub use self::attr::*;
pub mod barrier;
pub use self::barrier::*;
pub mod cond;
pub use self::cond::*;
#[thread_local]
pub static mut fork_hooks: [LinkedList<extern "C" fn()>; 3] = [const { LinkedList::new() }; 3];
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/pthread_cancel.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn pthread_cancel(thread: pthread_t) -> c_int {
match unsafe { pthread::cancel(&*thread.cast()) } {
Ok(()) => 0,
Err(Errno(error)) => error,
}
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/pthread_create.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn pthread_create(
pthread: *mut pthread_t,
attr: *const pthread_attr_t,
start_routine: extern "C" fn(arg: *mut c_void) -> *mut c_void,
arg: *mut c_void,
) -> c_int {
let attr = unsafe { attr.cast::<RlctAttr>().as_ref() };
match unsafe { pthread::create(attr, start_routine, arg) } {
Ok(ptr) => {
unsafe { core::ptr::write(pthread, ptr) };
0
}
Err(Errno(code)) => code,
}
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/pthread_detach.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn pthread_detach(pthread: pthread_t) -> c_int {
match unsafe { pthread::detach(&*pthread.cast()) } {
Ok(()) => 0,
Err(Errno(errno)) => errno,
}
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/pthread_equal.html>.
#[unsafe(no_mangle)]
pub extern "C" fn pthread_equal(pthread1: pthread_t, pthread2: pthread_t) -> c_int {
core::ptr::eq(pthread1, pthread2).into()
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/pthread_atfork.html>.
#[unsafe(no_mangle)]
pub extern "C" fn pthread_atfork(
prepare: Option<extern "C" fn()>,
parent: Option<extern "C" fn()>,
child: Option<extern "C" fn()>,
) -> c_int {
if let Some(prepare) = prepare {
unsafe {
fork_hooks[0].push_back(prepare);
}
}
if let Some(parent) = parent {
unsafe {
fork_hooks[1].push_back(parent);
}
}
if let Some(child) = child {
unsafe {
fork_hooks[2].push_back(child);
}
}
0
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/pthread_exit.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn pthread_exit(retval: *mut c_void) -> ! {
unsafe { pthread::exit_current_thread(pthread::Retval(retval)) }
}
// Not in latest POSIX, mark as depreciated?
/// See <https://pubs.opengroup.org/onlinepubs/009696799/functions/pthread_getconcurrency.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn pthread_getconcurrency() -> c_int {
// Redox and Linux threads are 1:1, not M:N.
1
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/pthread_getcpuclockid.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn pthread_getcpuclockid(
thread: pthread_t,
clock_out: *mut clockid_t,
) -> c_int {
match pthread::get_cpu_clkid(unsafe { &*thread.cast() }) {
Ok(clock) => {
unsafe { clock_out.write(clock) };
0
}
Err(Errno(error)) => error,
}
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/pthread_getschedparam.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn pthread_getschedparam(
thread: pthread_t,
policy_out: *mut c_int,
param_out: *mut sched_param,
) -> c_int {
match pthread::get_sched_param(unsafe { &*thread.cast() }) {
Ok((policy, param)) => {
unsafe { policy_out.write(policy) };
unsafe { param_out.write(param) };
0
}
Err(Errno(error)) => error,
}
}
pub mod tls;
pub use tls::*;
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/pthread_join.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn pthread_join(thread: pthread_t, retval: *mut *mut c_void) -> c_int {
match unsafe { pthread::join(&*thread.cast()) } {
Ok(pthread::Retval(ret)) => {
if !retval.is_null() {
unsafe { core::ptr::write(retval, ret) };
}
0
}
Err(Errno(error)) => error,
}
}
pub mod mutex;
pub use self::mutex::*;
pub mod once;
pub use self::once::*;
pub mod rwlock;
pub use self::rwlock::*;
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/pthread_self.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn pthread_self() -> pthread_t {
(unsafe { pthread::current_thread().unwrap_unchecked() }) as *const _ as *mut _
}
/// 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 {
match pthread::set_cancel_state(state) {
Ok(old) => {
// POSIX doesn't imply oldstate can be NULL anywhere, but a lot of C code probably
// relies on it...
if let Some(oldstate) = NonNull::new(oldstate) {
unsafe { oldstate.write(old) };
}
0
}
Err(Errno(error)) => error,
}
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/pthread_setcancelstate.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn pthread_setcanceltype(ty: c_int, oldty: *mut c_int) -> c_int {
match pthread::set_cancel_type(ty) {
Ok(old) => {
// POSIX doesn't imply oldty can be NULL anywhere, but a lot of C code probably relies
// on it...
if let Some(oldty) = NonNull::new(oldty) {
unsafe { oldty.write(old) };
}
0
}
Err(Errno(error)) => error,
}
}
// Not in latest POSIX, mark as depreciated?
/// See <https://pubs.opengroup.org/onlinepubs/000095399/functions/pthread_setconcurrency.html>.
#[unsafe(no_mangle)]
pub extern "C" fn pthread_setconcurrency(concurrency: c_int) -> c_int {
// Redox and Linux threads are 1:1, not M:N.
0
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/pthread_setschedparam.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn pthread_setschedparam(
thread: pthread_t,
policy: c_int,
param: *const sched_param,
) -> c_int {
e(pthread::set_sched_param(
unsafe { &*thread.cast() },
policy,
unsafe { &*param },
))
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/pthread_setschedprio.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn pthread_setschedprio(thread: pthread_t, prio: c_int) -> c_int {
e(pthread::set_sched_priority(
unsafe { &*thread.cast() },
prio,
))
}
pub mod spin;
pub use self::spin::*;
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/pthread_setcancelstate.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn pthread_testcancel() {
unsafe { pthread::testcancel() };
}
// Must be the same struct as defined in the pthread_cleanup_push macro.
#[repr(C)]
pub(crate) struct CleanupLinkedListEntry {
routine: extern "C" fn(*mut c_void),
arg: *mut c_void,
prev: *const c_void,
}
#[thread_local]
pub(crate) static CLEANUP_LL_HEAD: Cell<*const CleanupLinkedListEntry> =
Cell::new(core::ptr::null());
// TODO: unwind? setjmp/longjmp?
#[unsafe(no_mangle)]
pub unsafe extern "C" fn __relibc_internal_pthread_cleanup_push(new_entry: *mut c_void) {
let new_entry = unsafe { &mut *new_entry.cast::<CleanupLinkedListEntry>() };
new_entry.prev = CLEANUP_LL_HEAD.get().cast();
CLEANUP_LL_HEAD.set(new_entry);
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn __relibc_internal_pthread_cleanup_pop(execute: c_int) {
let prev_head = unsafe { CLEANUP_LL_HEAD.get().read() };
CLEANUP_LL_HEAD.set(prev_head.prev.cast());
if execute != 0 {
(prev_head.routine)(prev_head.arg);
}
}
pub(crate) unsafe fn run_destructor_stack() {
unsafe { crate::cxa::__cxa_thread_finalize() };
let mut ptr = CLEANUP_LL_HEAD.get();
while !ptr.is_null() {
let entry = unsafe { ptr.read() };
ptr = entry.prev.cast();
(entry.routine)(entry.arg);
}
}