Skip to main content

relibc/header/pthread/
mod.rs

1//! `pthread.h` implementation.
2//!
3//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/pthread.h.html>.
4
5use alloc::collections::LinkedList;
6use core::{cell::Cell, ptr::NonNull};
7
8use crate::{
9    error::Errno,
10    header::{sched::*, time::timespec},
11    platform::{
12        Pal, Sys,
13        types::{
14            c_int, c_uchar, c_uint, c_void, clockid_t, pthread_attr_t, pthread_barrier_t,
15            pthread_barrierattr_t, pthread_cond_t, pthread_condattr_t, pthread_key_t,
16            pthread_mutex_t, pthread_mutexattr_t, pthread_once_t, pthread_rwlock_t,
17            pthread_rwlockattr_t, pthread_t, size_t,
18        },
19    },
20    pthread,
21};
22
23pub fn e(result: Result<(), Errno>) -> i32 {
24    match result {
25        Ok(()) => 0,
26        Err(Errno(error)) => error,
27    }
28}
29
30#[derive(Clone)]
31pub(crate) struct RlctAttr {
32    pub detachstate: c_uchar,
33    pub inheritsched: c_uchar,
34    pub schedpolicy: c_uchar,
35    pub scope: c_uchar,
36    pub guardsize: size_t,
37    pub stacksize: size_t,
38    pub stack: size_t,
39    pub param: sched_param,
40    #[cfg(target_pointer_width = "32")]
41    _pad: [u8; 12],
42}
43
44pub const _POSIX_THREADS: c_int = 1;
45
46pub const PTHREAD_BARRIER_SERIAL_THREAD: c_int = -1;
47
48pub const PTHREAD_CANCEL_ASYNCHRONOUS: c_int = 0;
49pub const PTHREAD_CANCEL_ENABLE: c_int = 1;
50pub const PTHREAD_CANCEL_DEFERRED: c_int = 2;
51pub const PTHREAD_CANCEL_DISABLE: c_int = 3;
52pub const PTHREAD_CANCELED: *mut c_void = (!0_usize) as *mut c_void;
53
54pub const PTHREAD_CREATE_DETACHED: c_int = 0;
55pub const PTHREAD_CREATE_JOINABLE: c_int = 1;
56
57pub const PTHREAD_EXPLICIT_SCHED: c_int = 0;
58pub const PTHREAD_INHERIT_SCHED: c_int = 1;
59
60pub const PTHREAD_MUTEX_DEFAULT: c_int = 0;
61pub const PTHREAD_MUTEX_ERRORCHECK: c_int = 1;
62pub const PTHREAD_MUTEX_NORMAL: c_int = 2;
63pub const PTHREAD_MUTEX_RECURSIVE: c_int = 3;
64
65pub const PTHREAD_MUTEX_ROBUST: c_int = 0;
66pub const PTHREAD_MUTEX_STALLED: c_int = 1;
67
68pub const PTHREAD_PRIO_INHERIT: c_int = 0;
69
70pub const PTHREAD_PRIO_NONE: c_int = 0;
71
72pub const PTHREAD_PRIO_PROTECT: c_int = 0;
73
74pub const PTHREAD_PROCESS_SHARED: c_int = 0;
75pub const PTHREAD_PROCESS_PRIVATE: c_int = 1;
76
77pub const PTHREAD_SCOPE_PROCESS: c_int = 0;
78pub const PTHREAD_SCOPE_SYSTEM: c_int = 1;
79
80pub mod attr;
81pub use self::attr::*;
82
83pub mod barrier;
84pub use self::barrier::*;
85
86pub mod cond;
87pub use self::cond::*;
88
89#[thread_local]
90pub static mut fork_hooks: [LinkedList<extern "C" fn()>; 3] = [const { LinkedList::new() }; 3];
91
92/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/pthread_cancel.html>.
93#[unsafe(no_mangle)]
94pub unsafe extern "C" fn pthread_cancel(thread: pthread_t) -> c_int {
95    match unsafe { pthread::cancel(&*thread.cast()) } {
96        Ok(()) => 0,
97        Err(Errno(error)) => error,
98    }
99}
100
101/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/pthread_create.html>.
102#[unsafe(no_mangle)]
103pub unsafe extern "C" fn pthread_create(
104    pthread: *mut pthread_t,
105    attr: *const pthread_attr_t,
106    start_routine: extern "C" fn(arg: *mut c_void) -> *mut c_void,
107    arg: *mut c_void,
108) -> c_int {
109    let attr = unsafe { attr.cast::<RlctAttr>().as_ref() };
110
111    match unsafe { pthread::create(attr, start_routine, arg) } {
112        Ok(ptr) => {
113            unsafe { core::ptr::write(pthread, ptr) };
114            0
115        }
116        Err(Errno(code)) => code,
117    }
118}
119
120/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/pthread_detach.html>.
121#[unsafe(no_mangle)]
122pub unsafe extern "C" fn pthread_detach(pthread: pthread_t) -> c_int {
123    match unsafe { pthread::detach(&*pthread.cast()) } {
124        Ok(()) => 0,
125        Err(Errno(errno)) => errno,
126    }
127}
128
129/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/pthread_equal.html>.
130#[unsafe(no_mangle)]
131pub extern "C" fn pthread_equal(pthread1: pthread_t, pthread2: pthread_t) -> c_int {
132    core::ptr::eq(pthread1, pthread2).into()
133}
134
135/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/pthread_atfork.html>.
136#[unsafe(no_mangle)]
137pub extern "C" fn pthread_atfork(
138    prepare: Option<extern "C" fn()>,
139    parent: Option<extern "C" fn()>,
140    child: Option<extern "C" fn()>,
141) -> c_int {
142    if let Some(prepare) = prepare {
143        unsafe {
144            fork_hooks[0].push_back(prepare);
145        }
146    }
147    if let Some(parent) = parent {
148        unsafe {
149            fork_hooks[1].push_back(parent);
150        }
151    }
152    if let Some(child) = child {
153        unsafe {
154            fork_hooks[2].push_back(child);
155        }
156    }
157    0
158}
159
160/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/pthread_exit.html>.
161#[unsafe(no_mangle)]
162pub unsafe extern "C" fn pthread_exit(retval: *mut c_void) -> ! {
163    unsafe { pthread::exit_current_thread(pthread::Retval(retval)) }
164}
165
166// Not in latest POSIX, mark as depreciated?
167/// See <https://pubs.opengroup.org/onlinepubs/009696799/functions/pthread_getconcurrency.html>.
168#[unsafe(no_mangle)]
169pub unsafe extern "C" fn pthread_getconcurrency() -> c_int {
170    // Redox and Linux threads are 1:1, not M:N.
171    1
172}
173
174/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/pthread_getcpuclockid.html>.
175#[unsafe(no_mangle)]
176pub unsafe extern "C" fn pthread_getcpuclockid(
177    thread: pthread_t,
178    clock_out: *mut clockid_t,
179) -> c_int {
180    match pthread::get_cpu_clkid(unsafe { &*thread.cast() }) {
181        Ok(clock) => {
182            unsafe { clock_out.write(clock) };
183            0
184        }
185        Err(Errno(error)) => error,
186    }
187}
188
189/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/pthread_getschedparam.html>.
190#[unsafe(no_mangle)]
191pub unsafe extern "C" fn pthread_getschedparam(
192    thread: pthread_t,
193    policy_out: *mut c_int,
194    param_out: *mut sched_param,
195) -> c_int {
196    match pthread::get_sched_param(unsafe { &*thread.cast() }) {
197        Ok((policy, param)) => {
198            unsafe { policy_out.write(policy) };
199            unsafe { param_out.write(param) };
200            0
201        }
202        Err(Errno(error)) => error,
203    }
204}
205
206pub mod tls;
207pub use tls::*;
208
209/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/pthread_join.html>.
210#[unsafe(no_mangle)]
211pub unsafe extern "C" fn pthread_join(thread: pthread_t, retval: *mut *mut c_void) -> c_int {
212    match unsafe { pthread::join(&*thread.cast()) } {
213        Ok(pthread::Retval(ret)) => {
214            if !retval.is_null() {
215                unsafe { core::ptr::write(retval, ret) };
216            }
217            0
218        }
219        Err(Errno(error)) => error,
220    }
221}
222
223pub mod mutex;
224pub use self::mutex::*;
225
226pub mod once;
227pub use self::once::*;
228
229pub mod rwlock;
230pub use self::rwlock::*;
231
232/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/pthread_self.html>.
233#[unsafe(no_mangle)]
234pub unsafe extern "C" fn pthread_self() -> pthread_t {
235    core::ptr::from_ref(unsafe { pthread::current_thread().unwrap_unchecked() }) as *mut _
236}
237
238/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/pthread_setcancelstate.html>.
239#[unsafe(no_mangle)]
240pub unsafe extern "C" fn pthread_setcancelstate(state: c_int, oldstate: *mut c_int) -> c_int {
241    match pthread::set_cancel_state(state) {
242        Ok(old) => {
243            // POSIX doesn't imply oldstate can be NULL anywhere, but a lot of C code probably
244            // relies on it...
245            if let Some(oldstate) = NonNull::new(oldstate) {
246                unsafe { oldstate.write(old) };
247            }
248            0
249        }
250        Err(Errno(error)) => error,
251    }
252}
253
254/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/pthread_setcancelstate.html>.
255#[unsafe(no_mangle)]
256pub unsafe extern "C" fn pthread_setcanceltype(ty: c_int, oldty: *mut c_int) -> c_int {
257    match pthread::set_cancel_type(ty) {
258        Ok(old) => {
259            // POSIX doesn't imply oldty can be NULL anywhere, but a lot of C code probably relies
260            // on it...
261            if let Some(oldty) = NonNull::new(oldty) {
262                unsafe { oldty.write(old) };
263            }
264            0
265        }
266        Err(Errno(error)) => error,
267    }
268}
269
270// Not in latest POSIX, mark as depreciated?
271/// See <https://pubs.opengroup.org/onlinepubs/000095399/functions/pthread_setconcurrency.html>.
272#[unsafe(no_mangle)]
273pub extern "C" fn pthread_setconcurrency(concurrency: c_int) -> c_int {
274    // Redox and Linux threads are 1:1, not M:N.
275    0
276}
277
278/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/pthread_setschedparam.html>.
279#[unsafe(no_mangle)]
280pub unsafe extern "C" fn pthread_setschedparam(
281    thread: pthread_t,
282    policy: c_int,
283    param: *const sched_param,
284) -> c_int {
285    e(pthread::set_sched_param(
286        unsafe { &*thread.cast() },
287        policy,
288        unsafe { &*param },
289    ))
290}
291
292/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/pthread_setschedprio.html>.
293#[unsafe(no_mangle)]
294pub unsafe extern "C" fn pthread_setschedprio(thread: pthread_t, prio: c_int) -> c_int {
295    e(pthread::set_sched_priority(
296        unsafe { &*thread.cast() },
297        prio,
298    ))
299}
300
301pub mod spin;
302pub use self::spin::*;
303
304/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/pthread_setcancelstate.html>.
305#[unsafe(no_mangle)]
306pub unsafe extern "C" fn pthread_testcancel() {
307    unsafe { pthread::testcancel() };
308}
309
310// Must be the same struct as defined in the pthread_cleanup_push macro.
311#[repr(C)]
312pub(crate) struct CleanupLinkedListEntry {
313    routine: extern "C" fn(*mut c_void),
314    arg: *mut c_void,
315    prev: *const c_void,
316}
317
318#[thread_local]
319pub(crate) static CLEANUP_LL_HEAD: Cell<*const CleanupLinkedListEntry> =
320    Cell::new(core::ptr::null());
321
322// TODO: unwind? setjmp/longjmp?
323
324#[unsafe(no_mangle)]
325pub unsafe extern "C" fn __relibc_internal_pthread_cleanup_push(new_entry: *mut c_void) {
326    let new_entry = unsafe { &mut *new_entry.cast::<CleanupLinkedListEntry>() };
327
328    new_entry.prev = CLEANUP_LL_HEAD.get().cast();
329    CLEANUP_LL_HEAD.set(new_entry);
330}
331#[unsafe(no_mangle)]
332pub unsafe extern "C" fn __relibc_internal_pthread_cleanup_pop(execute: c_int) {
333    let prev_head = unsafe { CLEANUP_LL_HEAD.get().read() };
334    CLEANUP_LL_HEAD.set(prev_head.prev.cast());
335
336    if execute != 0 {
337        (prev_head.routine)(prev_head.arg);
338    }
339}
340
341pub(crate) unsafe fn run_destructor_stack() {
342    unsafe { crate::cxa::__cxa_thread_finalize() };
343
344    let mut ptr = CLEANUP_LL_HEAD.get();
345
346    while !ptr.is_null() {
347        let entry = unsafe { ptr.read() };
348        ptr = entry.prev.cast();
349
350        (entry.routine)(entry.arg);
351    }
352}