1use core::{
4 cell::UnsafeCell,
5 ptr,
6 sync::atomic::{AtomicBool, AtomicUsize, Ordering},
7};
8
9use alloc::collections::BTreeMap;
10
11use crate::{
12 error::Errno,
13 header::{errno::*, pthread as header, sched::sched_param, sys_mman},
14 ld_so::{ExpectTlsFree, tcb::Tcb},
15 platform::{Pal, Sys, types::*},
16};
17
18use crate::sync::{Mutex, waitval::Waitval};
19
20#[allow(unused_mut)]
22pub unsafe fn init() {
23 let mut thread = Pthread {
24 waitval: Waitval::new(),
25 has_enabled_cancelation: AtomicBool::new(false),
26 has_queued_cancelation: AtomicBool::new(false),
27 flags: PthreadFlags::empty().bits().into(),
28
29 stack_base: ptr::null_mut(),
33 stack_size: 0,
34
35 os_tid: UnsafeCell::new(Sys::current_os_tid()),
36 };
37
38 #[cfg(target_os = "redox")]
39 {
40 use redox_rt::arch::{STACK_SIZE, STACK_TOP};
42 thread.stack_base = (STACK_TOP - STACK_SIZE) as *mut c_void;
43 thread.stack_size = STACK_SIZE;
44 }
45
46 unsafe { Tcb::current() }
47 .expect_notls("no TCB present for main thread")
48 .pthread = thread;
49}
50
51pub unsafe fn terminate_from_main_thread() {
55 for tcb in OS_TID_TO_PTHREAD.lock().values() {
56 let _ = unsafe { cancel(&(*tcb.0).pthread) };
57 }
58}
59
60bitflags::bitflags! {
61 pub struct PthreadFlags: usize {
62 const DETACHED = 1;
63 }
64}
65
66#[derive(Debug)]
67pub struct Pthread {
68 pub(crate) waitval: Waitval<Retval>,
69 pub(crate) has_queued_cancelation: AtomicBool,
70 pub(crate) has_enabled_cancelation: AtomicBool,
71 pub(crate) flags: AtomicUsize,
72
73 pub(crate) stack_base: *mut c_void,
74 pub(crate) stack_size: usize,
75
76 pub os_tid: UnsafeCell<OsTid>,
77}
78
79#[derive(Clone, Copy, Debug, Default, Ord, Eq, PartialOrd, PartialEq)]
80pub struct OsTid {
81 #[cfg(target_os = "redox")]
82 pub thread_fd: usize,
83 #[cfg(target_os = "linux")]
84 pub thread_id: usize,
85}
86
87unsafe impl Send for Pthread {}
88unsafe impl Sync for Pthread {}
89
90#[derive(Clone, Copy, Debug)]
91pub struct Retval(pub *mut c_void);
92
93struct MmapGuard {
94 page_start: *mut c_void,
95 mmap_size: usize,
96}
97impl Drop for MmapGuard {
98 fn drop(&mut self) {
99 unsafe {
100 let _ = Sys::munmap(self.page_start, self.mmap_size);
101 }
102 }
103}
104
105#[allow(unused_mut)]
106pub(crate) unsafe fn create(
107 attrs: Option<&header::RlctAttr>,
108 start_routine: extern "C" fn(arg: *mut c_void) -> *mut c_void,
109 arg: *mut c_void,
110) -> Result<pthread_t, Errno> {
111 let attrs = attrs.cloned().unwrap_or_default();
112
113 #[cfg(not(target_os = "redox"))]
114 let mut current_sigmask = 0_u64;
115 #[cfg(target_os = "redox")]
116 let mut current_sigmask =
117 redox_rt::signal::get_sigmask().expect("failed to obtain sigprocmask for caller");
118
119 let synchronization_mutex = unsafe { Mutex::locked(current_sigmask) };
121 let synchronization_mutex = &synchronization_mutex;
122
123 let stack_size = attrs.stacksize.next_multiple_of(Sys::getpagesize());
124
125 let stack_base = if attrs.stack != 0 {
126 attrs.stack as *mut c_void
127 } else {
128 let ret = unsafe {
129 sys_mman::mmap(
130 core::ptr::null_mut(),
131 stack_size,
132 sys_mman::PROT_READ | sys_mman::PROT_WRITE,
133 sys_mman::MAP_PRIVATE | sys_mman::MAP_ANONYMOUS,
134 -1,
135 0,
136 )
137 };
138 if ret as isize == -1 {
139 return Err(Errno(EAGAIN));
141 }
142 ret
143 };
144
145 let mut flags = PthreadFlags::empty();
146 match i32::from(attrs.detachstate) {
147 header::PTHREAD_CREATE_DETACHED => flags |= PthreadFlags::DETACHED,
148 header::PTHREAD_CREATE_JOINABLE => (),
149
150 other => unreachable!("unknown detachstate {}", other),
151 }
152
153 let stack_raii = MmapGuard {
154 page_start: stack_base,
155 mmap_size: stack_size,
156 };
157
158 let current_tcb = unsafe { Tcb::current() }.expect("no TCB!");
159 let new_tcb = unsafe { Tcb::new(current_tcb.tls_len) }.map_err(|_| Errno(ENOMEM))?;
160 new_tcb.pthread.flags = flags.bits().into();
161 new_tcb.pthread.stack_base = stack_base;
162 new_tcb.pthread.stack_size = stack_size;
163
164 new_tcb.masters_ptr = current_tcb.masters_ptr;
165 new_tcb.masters_len = current_tcb.masters_len;
166 new_tcb.linker_ptr = current_tcb.linker_ptr;
167 new_tcb.mspace = current_tcb.mspace;
168
169 let stack_end = unsafe { stack_base.add(stack_size) };
170 let mut stack = stack_end.cast::<usize>();
171 {
172 let mut push = |value: usize| {
173 stack = unsafe { stack.sub(1) };
174 unsafe { stack.write(value) };
175 };
176
177 if cfg!(target_arch = "aarch64") {
178 push(0);
183 }
184 push(0);
185 push(0);
186 push(ptr::from_ref(synchronization_mutex) as usize);
187 push(ptr::from_mut(new_tcb) as usize);
188
189 push(arg as usize);
190 push(start_routine as usize);
191
192 push(new_thread_shim as *const () as usize);
193 }
194
195 let Ok(os_tid) = (unsafe { Sys::rlct_clone(stack, &mut new_tcb.os_specific) }) else {
196 return Err(Errno(EAGAIN));
197 };
198 core::mem::forget(stack_raii);
199
200 let _ = synchronization_mutex.lock();
201
202 OS_TID_TO_PTHREAD
203 .lock()
204 .insert(os_tid, ForceSendSync(new_tcb));
205
206 Ok(&raw const new_tcb.pthread as *mut _)
207}
208
209unsafe extern "C" fn new_thread_shim(
211 entry_point: unsafe extern "C" fn(*mut c_void) -> *mut c_void,
212 arg: *mut c_void,
213 tcb: *mut Tcb,
214 synchronization_mutex: *const Mutex<u64>,
215) -> ! {
216 let tcb = unsafe { tcb.as_mut() }.expect_notls("non-null TLS is required");
217
218 #[cfg(not(target_os = "redox"))]
219 {
220 unsafe { tcb.activate() };
221 }
222 #[cfg(target_os = "redox")]
223 {
224 unsafe {
227 tcb.activate(None);
228 }
229 redox_rt::signal::setup_sighandler(&tcb.os_specific, false);
230 }
231
232 let procmask = unsafe { (&*synchronization_mutex).as_ptr().read() };
233
234 unsafe { tcb.copy_masters() }.unwrap();
235
236 unsafe { tcb.pthread.os_tid.get().write(Sys::current_os_tid()) };
237
238 unsafe { (&*synchronization_mutex).manual_unlock() };
239
240 #[cfg(target_os = "redox")]
241 {
242 redox_rt::signal::set_sigmask(Some(procmask), None)
243 .expect("failed to set procmask in child thread");
244 }
245
246 let retval = unsafe { entry_point(arg) };
247
248 unsafe { exit_current_thread(Retval(retval)) }
249}
250pub unsafe fn join(thread: &Pthread) -> Result<Retval, Errno> {
251 if core::ptr::eq(
254 thread,
255 current_thread().expect("current thread not present"),
256 ) {
257 return Err(Errno(EDEADLK));
258 }
259
260 let retval = *thread.waitval.wait();
262
263 unsafe { dealloc_thread(thread) };
268
269 Ok(retval)
270}
271
272pub unsafe fn detach(thread: &Pthread) -> Result<(), Errno> {
273 thread
274 .flags
275 .fetch_or(PthreadFlags::DETACHED.bits(), Ordering::AcqRel);
276 Ok(())
277}
278
279pub fn current_thread() -> Option<&'static Pthread> {
280 unsafe { Tcb::current().map(|p| &p.pthread) }
281}
282
283pub unsafe fn testcancel() {
284 let this_thread = current_thread().expect("current thread not present");
285
286 if this_thread.has_queued_cancelation.load(Ordering::Acquire)
287 && this_thread.has_enabled_cancelation.load(Ordering::Acquire)
288 {
289 unsafe { cancel_current_thread() };
290 }
291}
292
293pub unsafe fn exit_current_thread(retval: Retval) -> ! {
294 unsafe { header::run_destructor_stack() };
296
297 unsafe { header::tls::run_all_destructors() };
298
299 let this = current_thread().expect("failed to obtain current thread when exiting");
300 let stack_base = this.stack_base;
301 let stack_size = this.stack_size;
302
303 if this.flags.load(Ordering::Acquire) & PthreadFlags::DETACHED.bits() != 0 {
304 unsafe { dealloc_thread(this) };
307 } else {
308 unsafe { this.waitval.post(retval) };
310 }
311
312 unsafe { Sys::exit_thread(stack_base.cast(), stack_size) }
313}
314
315unsafe fn dealloc_thread(thread: &Pthread) {
316 unsafe {
318 OS_TID_TO_PTHREAD.lock().remove(&thread.os_tid.get().read());
319 }
320}
321pub const SIGRT_RLCT_CANCEL: usize = 33;
322pub const SIGRT_RLCT_TIMER: usize = 34;
323
324unsafe extern "C" fn cancel_sighandler(_: c_int) {
325 unsafe { cancel_current_thread() };
326}
327unsafe fn cancel_current_thread() {
328 unsafe { exit_current_thread(Retval(header::PTHREAD_CANCELED)) };
330}
331
332pub unsafe fn cancel(thread: &Pthread) -> Result<(), Errno> {
333 thread.has_queued_cancelation.store(true, Ordering::Release);
335
336 if thread.has_enabled_cancelation.load(Ordering::Acquire) {
337 (unsafe { Sys::rlct_kill(thread.os_tid.get().read(), SIGRT_RLCT_CANCEL) })?;
338 }
339
340 Ok(())
341}
342
343pub fn set_sched_param(
344 _thread: &Pthread,
345 _policy: c_int,
346 _param: &sched_param,
347) -> Result<(), Errno> {
348 Ok(())
350}
351pub fn set_sched_priority(_thread: &Pthread, _prio: c_int) -> Result<(), Errno> {
352 Ok(())
354}
355pub fn set_cancel_state(state: c_int) -> Result<c_int, Errno> {
356 let this_thread = current_thread().expect("current thread not present");
357
358 let was_cancelable = match state {
359 header::PTHREAD_CANCEL_ENABLE => {
360 let old = this_thread
361 .has_enabled_cancelation
362 .swap(true, Ordering::Release);
363
364 if this_thread.has_queued_cancelation.load(Ordering::Acquire) {
365 unsafe {
366 cancel_current_thread();
367 }
368 }
369 old
370 }
371 header::PTHREAD_CANCEL_DISABLE => this_thread
372 .has_enabled_cancelation
373 .swap(false, Ordering::Release),
374
375 _ => return Err(Errno(EINVAL)),
376 };
377
378 Ok(match was_cancelable {
379 true => header::PTHREAD_CANCEL_ENABLE,
380 false => header::PTHREAD_CANCEL_DISABLE,
381 })
382}
383pub fn set_cancel_type(ty: c_int) -> Result<c_int, Errno> {
384 let this_thread = current_thread().expect("current thread not present");
385
386 match ty {
388 header::PTHREAD_CANCEL_DEFERRED => (),
389 header::PTHREAD_CANCEL_ASYNCHRONOUS => (),
390
391 _ => return Err(Errno(EINVAL)),
392 }
393 Ok(header::PTHREAD_CANCEL_DEFERRED)
394}
395pub fn get_cpu_clkid(thread: &Pthread) -> Result<clockid_t, Errno> {
396 Err(Errno(ENOENT))
398}
399pub fn get_sched_param(thread: &Pthread) -> Result<(clockid_t, sched_param), Errno> {
400 Err(Errno(ENOSYS))
402}
403
404static OS_TID_TO_PTHREAD: Mutex<BTreeMap<OsTid, ForceSendSync<*mut Tcb>>> =
407 Mutex::new(BTreeMap::new());
408
409#[derive(Clone, Copy)]
410struct ForceSendSync<T>(T);
411unsafe impl<T> Send for ForceSendSync<T> {}
412unsafe impl<T> Sync for ForceSendSync<T> {}
413
414#[derive(Clone, Copy, Default, Debug)]
419pub enum Pshared {
420 #[default]
421 Private,
422
423 Shared,
424}
425impl Pshared {
426 pub const fn from_raw(raw: c_int) -> Option<Self> {
427 Some(match raw {
428 header::PTHREAD_PROCESS_PRIVATE => Self::Private,
429 header::PTHREAD_PROCESS_SHARED => Self::Shared,
430
431 _ => return None,
432 })
433 }
434 pub const fn raw(self) -> c_int {
435 match self {
436 Self::Private => header::PTHREAD_PROCESS_PRIVATE,
437 Self::Shared => header::PTHREAD_PROCESS_SHARED,
438 }
439 }
440}