From 774a9a658d0ecf6d4ba4cd0e8fc44a23d5379b7c Mon Sep 17 00:00:00 2001 From: Connor-GH Date: Sun, 10 May 2026 19:40:05 -0500 Subject: [PATCH] implement threads.h Most of the functions are wrappers around the pthread.h functions, but we are not allowed to include pthread.h. --- src/header/mod.rs | 2 +- src/header/stdlib/mod.rs | 2 +- src/header/threads/cbindgen.toml | 18 ++ src/header/threads/mod.rs | 324 +++++++++++++++++++++++++++++++ 4 files changed, 344 insertions(+), 2 deletions(-) create mode 100644 src/header/threads/cbindgen.toml create mode 100644 src/header/threads/mod.rs diff --git a/src/header/mod.rs b/src/header/mod.rs index f8fa4f816b..e8a877127e 100644 --- a/src/header/mod.rs +++ b/src/header/mod.rs @@ -145,7 +145,7 @@ pub mod tar; // TODO: term.h (deprecated) pub mod termios; // TODO: tgmath.h (likely C implementation currenly through openlibm) -// TODO: threads.h +pub mod threads; pub mod time; // TODO: uchar.h // TODO: ucontext.h (deprecated) diff --git a/src/header/stdlib/mod.rs b/src/header/stdlib/mod.rs index 7bedfd9ca0..70662f4230 100644 --- a/src/header/stdlib/mod.rs +++ b/src/header/stdlib/mod.rs @@ -924,7 +924,7 @@ pub unsafe extern "C" fn posix_memalign( #[unsafe(no_mangle)] pub unsafe extern "C" fn posix_openpt(flags: c_int) -> c_int { #[cfg(target_os = "redox")] - let r = unsafe { open(c"/scheme/pty".as_ptr(), O_CREAT) }; + let r = unsafe { open(c"/scheme/pty".as_ptr(), flags) }; #[cfg(target_os = "linux")] let r = unsafe { open(c"/dev/ptmx".as_ptr(), flags) }; diff --git a/src/header/threads/cbindgen.toml b/src/header/threads/cbindgen.toml new file mode 100644 index 0000000000..4e762f277d --- /dev/null +++ b/src/header/threads/cbindgen.toml @@ -0,0 +1,18 @@ +sys_includes = [] +include_guard = "_RELIBC_THREADS_H" +language = "C" +style = "tag" +no_includes = true +cpp_compat = true +after_includes = """ +#include // for timespec +#include // for pthread-related types +#define thread_local _Thread_local +#define ONCE_FLAG_INIT PTHREAD_ONCE_INIT // once_flag == pthread_once_t +""" + +[export.rename] +"timespec" = "struct timespec" + +[enum] +prefix_with_name = true diff --git a/src/header/threads/mod.rs b/src/header/threads/mod.rs new file mode 100644 index 0000000000..56c24765b3 --- /dev/null +++ b/src/header/threads/mod.rs @@ -0,0 +1,324 @@ +//! `threads.h` implementation. +//! Based on the musl implementation, with regard for our special implementation of pthread types +//! +//! See . +#![allow(non_camel_case_types)] +#![allow(non_upper_case_globals)] +use super::{ + bits_pthread::{self, pthread_cond_t, pthread_key_t, pthread_mutex_t, pthread_mutexattr_t}, + errno::{EBUSY, EINTR, ENOMEM, ETIMEDOUT}, + limits, + pthread::{ + self, PTHREAD_MUTEX_NORMAL, PTHREAD_MUTEX_RECURSIVE, RlctCond, pthread_cond_broadcast, + pthread_cond_destroy, pthread_cond_signal, pthread_cond_timedwait, pthread_cond_wait, + pthread_create, pthread_detach, pthread_equal, pthread_exit, pthread_getspecific, + pthread_join, pthread_key_create, pthread_key_delete, pthread_mutex_destroy, + pthread_mutex_init, pthread_mutex_lock, pthread_mutex_timedlock, pthread_mutex_trylock, + pthread_mutex_unlock, pthread_mutexattr_init, pthread_mutexattr_settype, pthread_self, + pthread_setspecific, + }, + sched::sched_yield, + time::{CLOCK_REALTIME, clock_nanosleep, timespec}, +}; +use crate::platform::{ + ERRNO, + types::{c_int, c_long, c_void}, +}; +use core::mem::MaybeUninit; + +pub type thrd_t = bits_pthread::pthread_t; +pub type once_flag = bits_pthread::pthread_once_t; +pub type tss_t = bits_pthread::pthread_key_t; +pub type mtx_t = bits_pthread::pthread_mutex_t; +pub type cnd_t = bits_pthread::pthread_cond_t; + +pub type thrd_start_t = extern "C" fn(*mut c_void) -> c_int; +pub type tss_dtor_t = Option; + +pub const thrd_success: c_int = 0; +pub const thrd_busy: c_int = 1; +pub const thrd_error: c_int = 2; +pub const thrd_nomem: c_int = 3; +pub const thrd_timedout: c_int = 4; + +pub const mtx_plain: c_int = 0; +pub const mtx_recursive: c_int = 1; +pub const mtx_timed: c_int = 2; + +pub const TSS_DTOR_ITERATIONS: c_long = limits::PTHREAD_DESTRUCTOR_ITERATIONS; + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn call_once(flag: *mut once_flag, func: extern "C" fn()) { + unsafe { + pthread::pthread_once(flag, func); + }; +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn cnd_broadcast(cnd: *mut cnd_t) -> c_int { + if unsafe { pthread_cond_broadcast(cnd.cast::()) } == 0 { + thrd_success + } else { + thrd_error + } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn cnd_destroy(cnd: *mut cnd_t) { + unsafe { pthread_cond_destroy(cnd.cast::()) }; +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn cnd_init(cnd: *mut cnd_t) -> c_int { + unsafe { + cnd.cast::().write(RlctCond::new()); + } + thrd_success +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn cnd_signal(cnd: *mut cnd_t) -> c_int { + if unsafe { pthread_cond_signal(cnd.cast::()) } == 0 { + thrd_success + } else { + thrd_error + } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn cnd_timedwait( + cnd: *mut cnd_t, + mtx: *mut mtx_t, + timeout: *const timespec, +) -> c_int { + ERRNO.set(0); + let ret = unsafe { + pthread_cond_timedwait( + cnd.cast::(), + mtx.cast::(), + timeout, + ) + }; + let new_errno = ERRNO.get(); + match ret { + 0 => thrd_success, + _ if new_errno == ETIMEDOUT => thrd_timedout, + _ => thrd_error, + } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn cnd_wait(cnd: *mut cnd_t, mtx: *mut mtx_t) -> c_int { + if unsafe { pthread_cond_wait(cnd.cast::(), mtx.cast::()) } + == 0 + { + thrd_success + } else { + thrd_error + } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn mtx_destroy(mtx: *mut mtx_t) { + unsafe { + pthread_mutex_destroy(mtx.cast::()); + } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn mtx_init(mtx: *mut mtx_t, ty: c_int) -> c_int { + let mut attr = MaybeUninit::::uninit(); + let _ = unsafe { + pthread_mutexattr_init(attr.as_mut_ptr()); + }; + let mut attr = unsafe { attr.assume_init() }; + + let _ = unsafe { + pthread_mutexattr_settype( + &raw mut attr, + if ty & mtx_recursive == mtx_recursive { + PTHREAD_MUTEX_RECURSIVE + } else { + PTHREAD_MUTEX_NORMAL + }, + ) + }; + if unsafe { pthread_mutex_init(mtx.cast::(), &raw const attr) } == 0 { + thrd_success + } else { + thrd_error + } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn mtx_lock(mtx: *mut mtx_t) -> c_int { + if unsafe { pthread_mutex_lock(mtx.cast::()) } == 0 { + thrd_success + } else { + thrd_error + } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn mtx_timedlock(mtx: *mut mtx_t, timeout: ×pec) -> c_int { + ERRNO.set(0); + let ret = unsafe { pthread_mutex_timedlock(mtx.cast::(), timeout) }; + let new_errno = ERRNO.get(); + match ret { + 0 => thrd_success, + _ if new_errno == ETIMEDOUT => thrd_timedout, + _ => thrd_error, + } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn mtx_trylock(mtx: *mut mtx_t) -> c_int { + ERRNO.set(0); + let ret = unsafe { pthread_mutex_trylock(mtx.cast::()) }; + let new_errno = ERRNO.get(); + match ret { + 0 => thrd_success, + _ if new_errno == EBUSY => thrd_busy, + _ => thrd_error, + } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn mtx_unlock(mtx: *mut mtx_t) -> c_int { + if unsafe { pthread_mutex_unlock(mtx.cast::()) } == 0 { + thrd_success + } else { + thrd_error + } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn thrd_create( + thrd: *mut thrd_t, + start: thrd_start_t, + arg: *mut c_void, +) -> c_int { + ERRNO.set(0); + let ret = unsafe { + pthread_create( + thrd, + core::ptr::null(), + core::mem::transmute::<_, extern "C" fn(*mut c_void) -> *mut c_void>(start), + arg, + ) + }; + let new_errno = ERRNO.get(); + match ret { + 0 => thrd_success, + _ if new_errno == ENOMEM => thrd_nomem, + _ => thrd_error, + } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn thrd_current() -> thrd_t { + unsafe { pthread_self() } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn thrd_detach(thrd: thrd_t) -> c_int { + if unsafe { pthread_detach(thrd) } == 0 { + thrd_success + } else { + thrd_error + } +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn thrd_equal(thrd1: thrd_t, thrd2: thrd_t) -> c_int { + pthread_equal(thrd1, thrd2) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn thrd_exit(ret: c_int) -> ! { + unsafe { pthread_exit(ret as *mut c_void) } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn thrd_join(thrd: thrd_t, retval: *mut c_int) -> c_int { + if unsafe { pthread_join(thrd, retval as *mut *mut c_void) } == 0 { + thrd_success + } else { + thrd_error + } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn thrd_sleep(time1: ×pec, time2: *mut timespec) -> c_int { + ERRNO.set(0); + let ret = clock_nanosleep(CLOCK_REALTIME, 0, time1, time2); + let new_errno = ERRNO.get(); + match ret { + 0 => 0, + -1 if new_errno == EINTR => -1, + _ => -2, + } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn thrd_yield() { + sched_yield(); +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn tss_create(tss: *mut tss_t, dtor: tss_dtor_t) -> c_int { + if unsafe { pthread_key_create(tss.cast::(), dtor) } == 0 { + thrd_success + } else { + thrd_error + } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn tss_delete(tss: tss_t) { + // note: tss_t is a transparent type alias to pthread_key_t + unsafe { + pthread_key_delete(tss); + } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn tss_get(tss: tss_t) -> *mut c_void { + // note: tss_t is a transparent type alias to pthread_key_t + unsafe { pthread_getspecific(tss) } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn tss_set(tss: tss_t, value: *mut c_void) -> c_int { + // note: tss_t is a transparent type alias to pthread_key_t + if unsafe { pthread_setspecific(tss, value) } == 0 { + thrd_success + } else { + thrd_error + } +}