libc: basic pthread spawn/join

This commit is contained in:
Mark Poliakov 2024-11-15 23:18:04 +02:00
parent 465fc53e02
commit 17eca4c0c0
21 changed files with 751 additions and 108 deletions

View File

@ -0,0 +1,27 @@
#![allow(missing_docs)]
pub fn get_tls() -> usize {
let tp: usize;
unsafe {
core::arch::asm!("mrs {0}, tpidr_el0", out(reg) tp);
}
tp
}
pub unsafe fn set_pthread_self(value: usize) {
todo!()
}
pub fn pthread_self() -> usize {
todo!()
}
pub fn thread_local_area_impl() -> &'static mut [*mut u8] {
let tp = get_tls();
// Kernel reserves two words below the TP
assert_eq!(tp % size_of::<usize>(), 0);
let tp = tp - size_of::<usize>() * 2;
unsafe { super::thread_local_area_common(tp) }
}

View File

@ -0,0 +1,27 @@
#![allow(missing_docs)]
pub fn get_tls() -> usize {
let tp: usize;
unsafe {
core::arch::asm!("mov %gs:0, {0}", out(reg) tp, options(att_syntax));
}
tp
}
pub unsafe fn set_pthread_self(value: usize) {
todo!()
}
pub fn pthread_self() -> usize {
todo!()
}
pub fn thread_local_area_impl() -> &'static mut [*mut u8] {
let tp = get_tls();
// Skip self-pointer
assert_eq!(tp % size_of::<usize>(), 0);
let tp = tp + size_of::<usize>() * 2;
unsafe { super::thread_local_area_common(tp) }
}

View File

@ -11,6 +11,23 @@ use crate::sys;
const THREAD_LOCAL_AREA_SIZE: usize = 4096 / size_of::<usize>(); const THREAD_LOCAL_AREA_SIZE: usize = 4096 / size_of::<usize>();
#[cfg(any(target_arch = "aarch64", rust_analyzer))]
mod aarch64;
#[cfg(any(target_arch = "aarch64", rust_analyzer))]
use aarch64 as imp;
#[cfg(any(target_arch = "x86_64", rust_analyzer))]
mod x86_64;
#[cfg(any(target_arch = "x86_64", rust_analyzer))]
use x86_64 as imp;
#[cfg(any(target_arch = "x86", rust_analyzer))]
mod i686;
#[cfg(any(target_arch = "x86", rust_analyzer))]
use i686 as imp;
pub use imp::{get_tls, pthread_self, set_pthread_self};
/// # Safety /// # Safety
/// ///
/// `tp` must be a proper and aligned pointer to a pair of [usize] words. /// `tp` must be a proper and aligned pointer to a pair of [usize] words.
@ -41,49 +58,8 @@ unsafe fn thread_local_area_common(tp: usize) -> &'static mut [*mut u8] {
unsafe { core::slice::from_raw_parts_mut(base, len) } unsafe { core::slice::from_raw_parts_mut(base, len) }
} }
#[cfg(any(target_arch = "x86", target_arch = "x86_64", rust_analyzer))]
fn thread_local_area_impl() -> &'static mut [*mut u8] {
#[cfg(any(target_arch = "x86", rust_analyzer))]
fn get_tls() -> usize {
let tp: usize;
unsafe {
core::arch::asm!("mov %gs:0, {0}", out(reg) tp, options(att_syntax));
}
tp
}
#[cfg(any(target_arch = "x86_64", rust_analyzer))]
fn get_tls() -> usize {
let tp: usize;
unsafe {
core::arch::asm!("mov %fs:0, {0}", out(reg) tp, options(att_syntax));
}
tp
}
let tp = get_tls();
// Skip self-pointer
assert_eq!(tp % size_of::<usize>(), 0);
let tp = tp + size_of::<usize>() * 2;
unsafe { thread_local_area_common(tp) }
}
#[cfg(any(target_arch = "aarch64", rust_analyzer))]
fn thread_local_area_impl() -> &'static mut [*mut u8] {
let tp: usize;
unsafe {
core::arch::asm!("mrs {0}, tpidr_el0", out(reg) tp);
}
// Kernel reserves two words below the TP
assert_eq!(tp % size_of::<usize>(), 0);
let tp = tp - size_of::<usize>() * 2;
unsafe { thread_local_area_common(tp) }
}
/// Returns a reference to the thread-local area, which contains thread-local pointers /// Returns a reference to the thread-local area, which contains thread-local pointers
pub fn thread_local_area() -> &'static mut [*mut u8] { pub fn thread_local_area() -> &'static mut [*mut u8] {
// TODO grow thread local areas based on size requested? // TODO grow thread local areas based on size requested?
thread_local_area_impl() imp::thread_local_area_impl()
} }

View File

@ -0,0 +1,35 @@
#![allow(missing_docs)]
pub fn get_tls() -> usize {
let tp: usize;
unsafe {
core::arch::asm!("mov %fs:0, {0}", out(reg) tp, options(att_syntax));
}
tp
}
pub unsafe fn set_pthread_self(value: usize) {
let tls: *mut usize = core::ptr::with_exposed_provenance_mut(get_tls());
if tls.is_null() {
panic!("TLS pointer is NULL");
}
tls.add(1).write(value);
}
pub fn pthread_self() -> usize {
let tls: *const usize = core::ptr::with_exposed_provenance(get_tls());
if tls.is_null() {
panic!("TLS pointer is NULL");
}
unsafe { tls.add(1).read() }
}
pub fn thread_local_area_impl() -> &'static mut [*mut u8] {
let tp = get_tls();
// Skip self-pointer
assert_eq!(tp % size_of::<usize>(), 0);
let tp = tp + size_of::<usize>() * 2;
unsafe { super::thread_local_area_common(tp) }
}

74
test.c
View File

@ -1,73 +1,19 @@
#include <pthread.h>
#include <assert.h> #include <assert.h>
#include <stdio.h> #include <stdio.h>
#include <math.h>
#include <setjmp.h> static void *function(void *arg) {
#include <stdlib.h> printf("This is thread %u\n", pthread_self());
#include <string.h> printf("argument: %s\n", (const char *) arg);
#include <unistd.h> return NULL;
}
int main(int argc, const char **argv) { int main(int argc, const char **argv) {
char *val; pthread_t thread;
assert(environ != NULL); assert(pthread_create(&thread, NULL, function, (void *) "thread1") == 0);
assert(*environ == NULL);
setenv("key1", "value1", 0); pthread_join(thread, NULL);
assert(environ != NULL);
assert(!strcmp(*environ, "key1=value1"));
assert(*(environ + 1) == NULL);
setenv("key1", "value2", 0);
assert(environ != NULL);
assert(!strcmp(*environ, "key1=value1"));
assert(*(environ + 1) == NULL);
setenv("key1", "value3", 1);
assert(environ != NULL);
assert(!strcmp(*environ, "key1=value3"));
assert(*(environ + 1) == NULL);
setenv("key1", "longer-value", 1);
assert(environ != NULL);
assert(!strcmp(*environ, "key1=longer-value"));
assert(*(environ + 1) == NULL);
setenv("key2", "other-value", 0);
assert(environ != NULL);
assert(!strcmp(environ[0], "key1=longer-value"));
assert(!strcmp(environ[1], "key2=other-value"));
assert(environ[2] == NULL);
assert(!strcmp(getenv("key1"), "longer-value"));
assert(!strcmp(getenv("key2"), "other-value"));
assert(getenv("key3") == NULL);
unsetenv("key2");
assert(!strcmp(getenv("key1"), "longer-value"));
assert(getenv("key2") == NULL);
assert(getenv("key3") == NULL);
environ[0] = strdup("key2=???");
assert(!strcmp(getenv("key2"), "???"));
char *value = strdup("key4=!!!");
putenv(value);
assert(!strcmp(getenv("key2"), "???"));
assert(!strcmp(getenv("key4"), "!!!"));
value[6] = '@';
assert(!strcmp(getenv("key2"), "???"));
assert(!strcmp(getenv("key4"), "!@!"));
unsetenv("key4");
unsetenv("key2");
assert(environ != NULL);
assert(*environ == NULL);
return 0; return 0;
} }

View File

@ -135,3 +135,4 @@ pub mod sys_wait;
// TODO Generate those as part of dyn-loader (and make dyn-loader a shared library) // TODO Generate those as part of dyn-loader (and make dyn-loader a shared library)
pub mod link; pub mod link;
pub mod pthread;

View File

@ -0,0 +1,134 @@
use core::ffi::{c_int, c_void};
use crate::headers::{sched::__ygg_sched_param_t, sys_types::pthread_attr_t};
#[no_mangle]
unsafe extern "C" fn pthread_attr_destroy(_attr: *mut pthread_attr_t) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn pthread_attr_getdetachstate(
_attr: *const pthread_attr_t,
_state: *mut c_int,
) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn pthread_attr_getguardsize(
_attr: *const pthread_attr_t,
_size: *mut usize,
) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn pthread_attr_getinheritsched(
_attr: *const pthread_attr_t,
_inherit: *mut c_int,
) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn pthread_attr_getschedparam(
_attr: *const pthread_attr_t,
_param: *mut __ygg_sched_param_t,
) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn pthread_attr_getschedpolicy(
_attr: *const pthread_attr_t,
_policy: *mut c_int,
) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn pthread_attr_getscope(
_attr: *const pthread_attr_t,
_scope: *mut c_int,
) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn pthread_attr_getstack(
_attr: *const pthread_attr_t,
_stack: *mut *mut c_void,
_size: *mut usize,
) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn pthread_attr_getstacksize(
_attr: *const pthread_attr_t,
_size: *mut usize,
) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn pthread_attr_init(_attr: *mut pthread_attr_t) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn pthread_attr_setdetachstate(
_attr: *mut pthread_attr_t,
_detach: c_int,
) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn pthread_attr_setguardsize(_attr: *mut pthread_attr_t, _size: usize) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn pthread_attr_setinheritsched(
_attr: *mut pthread_attr_t,
_inherit: c_int,
) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn pthread_attr_setschedparam(
_attr: *mut pthread_attr_t,
_param: *const __ygg_sched_param_t,
) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn pthread_attr_setschedpolicy(
_attr: *mut pthread_attr_t,
_policy: c_int,
) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn pthread_attr_setscope(_attr: *mut pthread_attr_t, _scope: c_int) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn pthread_attr_setstack(
_attr: *mut pthread_attr_t,
_stack: *mut c_void,
_size: usize,
) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn pthread_attr_setstacksize(_attr: *mut pthread_attr_t, _size: usize) -> c_int {
todo!()
}

View File

@ -0,0 +1,9 @@
// PTHREAD_BARRIER_SERIAL_THREAD
// int pthread_barrier_destroy(pthread_barrier_t *);
// int pthread_barrier_init(pthread_barrier_t *restrict, const pthread_barrierattr_t *restrict, unsigned);
// int pthread_barrier_wait(pthread_barrier_t *);
// int pthread_barrierattr_destroy(pthread_barrierattr_t *);
// int pthread_barrierattr_getpshared(const pthread_barrierattr_t *restrict, int *restrict);
// int pthread_barrierattr_init(pthread_barrierattr_t *);
// int pthread_barrierattr_setpshared(pthread_barrierattr_t *, int);

View File

@ -0,0 +1,14 @@
language = "C"
style = "Type"
sys_includes = [
"sys/types.h",
"sched.h",
"time.h"
]
no_includes = true
include_guard = "_PTHREAD_H"
usize_type = "size_t"
isize_type = "ssize_t"

View File

@ -0,0 +1,16 @@
// PTHREAD_COND_INITIALIZER (macro)
// int pthread_cond_broadcast(pthread_cond_t *);
// int pthread_cond_destroy(pthread_cond_t *);
// int pthread_cond_init(pthread_cond_t *restrict, const pthread_condattr_t *restrict);
// int pthread_cond_signal(pthread_cond_t *);
// int pthread_cond_timedwait(pthread_cond_t *restrict, pthread_mutex_t *restrict, const struct timespec *restrict);
// int pthread_cond_wait(pthread_cond_t *restrict, pthread_mutex_t *restrict);
// int pthread_condattr_destroy(pthread_condattr_t *);
// int pthread_condattr_getclock(const pthread_condattr_t *restrict, clockid_t *restrict);
// int pthread_condattr_getpshared(const pthread_condattr_t *restrict, int *restrict);
// int pthread_condattr_init(pthread_condattr_t *);
// int pthread_condattr_setclock(pthread_condattr_t *, clockid_t);
// int pthread_condattr_setpshared(pthread_condattr_t *, int);

View File

@ -0,0 +1,51 @@
use core::{
ffi::c_void,
mem::MaybeUninit,
ptr::null_mut,
sync::atomic::{AtomicPtr, Ordering},
};
use alloc::{boxed::Box, collections::BTreeMap, sync::Arc};
use yggdrasil_rt::{
mem::{MappingFlags, MappingSource},
process::{ThreadId, ThreadSpawnOptions},
};
use crate::{
error::{EResult, ResultExt},
headers::errno,
sync::Mutex,
};
use super::sys_types::{pthread_attr_t, pthread_t};
mod attr;
mod barrier;
mod cond;
mod mutex;
mod rwlock;
mod spin;
mod thread;
mod tls;
// PTHREAD_CANCEL_ASYNCHRONOUS
// PTHREAD_CANCEL_ENABLE
// PTHREAD_CANCEL_DEFERRED
// PTHREAD_CANCEL_DISABLE
// PTHREAD_CANCELED
// PTHREAD_CREATE_DETACHED
// PTHREAD_CREATE_JOINABLE
// PTHREAD_EXPLICIT_SCHED
// PTHREAD_INHERIT_SCHED
// PTHREAD_ONCE_INIT
// PTHREAD_PRIO_INHERIT
// PTHREAD_PRIO_NONE
// PTHREAD_PRIO_PROTECT
// PTHREAD_PROCESS_SHARED
// PTHREAD_PROCESS_PRIVATE
// PTHREAD_SCOPE_PROCESS
// PTHREAD_SCOPE_SYSTEM
//
// int pthread_once(pthread_once_t *, void (*)(void));

View File

@ -0,0 +1,31 @@
// PTHREAD_MUTEX_DEFAULT
// PTHREAD_MUTEX_ERRORCHECK
// PTHREAD_MUTEX_NORMAL
// PTHREAD_MUTEX_RECURSIVE
// PTHREAD_MUTEX_ROBUST
// PTHREAD_MUTEX_STALLED
//
// PTHREAD_MUTEX_INITIALIZER (macro)
// int pthread_mutex_consistent(pthread_mutex_t *);
// int pthread_mutex_destroy(pthread_mutex_t *);
// int pthread_mutex_getprioceiling(const pthread_mutex_t *restrict, int *restrict);
// int pthread_mutex_init(pthread_mutex_t *restrict, const pthread_mutexattr_t *restrict);
// int pthread_mutex_lock(pthread_mutex_t *);
// int pthread_mutex_setprioceiling(pthread_mutex_t *restrict, int, int *restrict);
// int pthread_mutex_timedlock(pthread_mutex_t *restrict, const struct timespec *restrict);
// int pthread_mutex_trylock(pthread_mutex_t *);
// int pthread_mutex_unlock(pthread_mutex_t *);
// int pthread_mutexattr_destroy(pthread_mutexattr_t *);
// int pthread_mutexattr_getprioceiling( const pthread_mutexattr_t *restrict, int *restrict);
// int pthread_mutexattr_getprotocol(const pthread_mutexattr_t *restrict, int *restrict);
// int pthread_mutexattr_getpshared(const pthread_mutexattr_t *restrict, int *restrict);
// int pthread_mutexattr_getrobust(const pthread_mutexattr_t *restrict, int *restrict);
// int pthread_mutexattr_gettype(const pthread_mutexattr_t *restrict, int *restrict);
// int pthread_mutexattr_init(pthread_mutexattr_t *);
// int pthread_mutexattr_setprioceiling(pthread_mutexattr_t *, int);
// int pthread_mutexattr_setprotocol(pthread_mutexattr_t *, int);
// int pthread_mutexattr_setpshared(pthread_mutexattr_t *, int);
// int pthread_mutexattr_setrobust(pthread_mutexattr_t *, int);
// int pthread_mutexattr_settype(pthread_mutexattr_t *, int);

View File

@ -0,0 +1,15 @@
// PTHREAD_RWLOCK_INITIALIZER (macro)
// int pthread_rwlock_destroy(pthread_rwlock_t *);
// int pthread_rwlock_init(pthread_rwlock_t *restrict, const pthread_rwlockattr_t *restrict);
// int pthread_rwlock_rdlock(pthread_rwlock_t *);
// int pthread_rwlock_timedrdlock(pthread_rwlock_t *restrict, const struct timespec *restrict);
// int pthread_rwlock_timedwrlock(pthread_rwlock_t *restrict, const struct timespec *restrict);
// int pthread_rwlock_tryrdlock(pthread_rwlock_t *);
// int pthread_rwlock_trywrlock(pthread_rwlock_t *);
// int pthread_rwlock_unlock(pthread_rwlock_t *);
// int pthread_rwlock_wrlock(pthread_rwlock_t *);
// int pthread_rwlockattr_destroy(pthread_rwlockattr_t *);
// int pthread_rwlockattr_getpshared(const pthread_rwlockattr_t *restrict, int *restrict);
// int pthread_rwlockattr_init(pthread_rwlockattr_t *);
// int pthread_rwlockattr_setpshared(pthread_rwlockattr_t *, int);

View File

@ -0,0 +1,6 @@
// int pthread_spin_destroy(pthread_spinlock_t *);
// int pthread_spin_init(pthread_spinlock_t *, int);
// int pthread_spin_lock(pthread_spinlock_t *);
// int pthread_spin_trylock(pthread_spinlock_t *);
// int pthread_spin_unlock(pthread_spinlock_t *);

View File

@ -0,0 +1,119 @@
use core::ffi::{c_int, c_void};
use crate::{
error::CIntZeroResult,
headers::{
sched::__ygg_sched_param_t,
sys_types::{clockid_t, pthread_attr_t, pthread_t},
},
thread::Thread,
};
#[no_mangle]
unsafe extern "C" fn pthread_atfork(
_prepare: extern "C" fn(),
_parent: extern "C" fn(),
_child: extern "C" fn(),
) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn pthread_cancel(_thread: pthread_t) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn pthread_create(
result: *mut pthread_t,
attr: *const pthread_attr_t,
entry: extern "C" fn(*mut c_void) -> *mut c_void,
argument: *mut c_void,
) -> CIntZeroResult {
let attr = attr.as_ref();
let id = Thread::spawn(attr, entry, argument)?;
if let Some(result) = result.as_mut() {
*result = id;
}
CIntZeroResult::SUCCESS
}
#[no_mangle]
unsafe extern "C" fn pthread_detach(_thread: pthread_t) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn pthread_equal(_thread1: pthread_t, _thread2: pthread_t) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn pthread_exit(_result: *mut c_void) -> c_void {
todo!()
}
#[no_mangle]
unsafe extern "C" fn pthread_getconcurrency() -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn pthread_getcpuclockid(_thread: pthread_t, _clock_id: *mut clockid_t) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn pthread_getschedparam(
_thread: pthread_t,
_policy: *mut c_int,
_param: *mut __ygg_sched_param_t,
) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn pthread_join(thread: pthread_t, result: *mut *mut c_void) -> CIntZeroResult {
let thread = Thread::join(thread)?;
// TODO write result
CIntZeroResult::SUCCESS
}
#[no_mangle]
unsafe extern "C" fn pthread_self() -> pthread_t {
Thread::this().expect("pthread_self() failed").id()
}
#[no_mangle]
unsafe extern "C" fn pthread_setcancelstate(_state: c_int, _old_state: *mut c_int) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn pthread_setcanceltype(_ty: c_int, _old_ty: *mut c_int) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn pthread_setconcurrency(_c: c_int) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn pthread_setschedparam(
_thread: pthread_t,
_policy: c_int,
_param: *const __ygg_sched_param_t,
) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn pthread_setschedprio(_thread: pthread_t, _priority: c_int) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn pthread_testcancel() {
todo!()
}

View File

@ -0,0 +1,5 @@
// void *pthread_getspecific(pthread_key_t);
// int pthread_key_create(pthread_key_t *, void (*)(void*));
// int pthread_key_delete(pthread_key_t);
// int pthread_setspecific(pthread_key_t, const void *);

View File

@ -26,5 +26,19 @@ include = [
"suseconds_t", "suseconds_t",
"time_t", "time_t",
"timer_t", "timer_t",
"uid_t" "uid_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"
] ]

View File

@ -1,4 +1,4 @@
use core::ffi::{c_int, c_ulong}; use core::ffi::{c_int, c_ulong, c_void};
pub type pid_t = i32; pub type pid_t = i32;
pub type uid_t = i32; pub type uid_t = i32;
@ -36,3 +36,40 @@ pub type suseconds_t = c_ulong;
impl time_t { impl time_t {
pub const INVALID: Self = Self(i64::MAX); pub const INVALID: Self = Self(i64::MAX);
} }
// pthread types
#[repr(C)]
pub struct pthread_attr_t {
pub stack: *mut c_void,
pub stack_size: usize,
}
pub struct pthread_barrier_t {}
pub struct pthread_barrierattr_t {}
#[repr(C)]
pub struct pthread_cond_t {
__dummy: c_int
}
pub struct pthread_condattr_t {}
pub struct pthread_key_t {}
#[repr(C)]
pub struct pthread_mutex_t {
__dummy: c_int
}
pub struct pthread_mutexattr_t {}
pub struct pthread_once_t {}
#[repr(C)]
pub struct pthread_rwlock_t {
__dummy: c_int
}
pub struct pthread_rwlockattr_t {}
pub struct pthread_spinlock_t {}
pub type pthread_t = u32;

View File

@ -39,6 +39,7 @@ mod ssp;
mod sync; mod sync;
mod types; mod types;
mod util; mod util;
mod thread;
pub mod headers; pub mod headers;

View File

@ -0,0 +1,178 @@
use core::{
ffi::c_void,
mem,
pin::Pin,
ptr::{self, null_mut},
sync::atomic::{AtomicPtr, AtomicU32, Ordering},
};
use alloc::{boxed::Box, collections::BTreeMap, sync::Arc};
use yggdrasil_rt::{
mem::{MappingFlags, MappingSource},
process::{get_tls, pthread_self, set_pthread_self, ThreadId, ThreadSpawnOptions},
};
use crate::{
error::EResult,
headers::sys_types::{pthread_attr_t, pthread_t},
sync::Mutex,
};
mod tls;
static THREADS: Mutex<BTreeMap<pthread_t, Arc<Thread>>> = Mutex::new(BTreeMap::new());
enum ThreadStack {
Owned(usize, usize),
Borrowed,
}
struct ThreadArgument {
entry: extern "C" fn(*mut c_void) -> *mut c_void,
argument: *mut c_void,
thread: Arc<Thread>,
}
#[repr(C)]
pub struct Thread {
id: AtomicU32,
stack: ThreadStack,
result: AtomicPtr<c_void>,
}
impl Thread {
pub fn spawn(
attr: Option<&pthread_attr_t>,
entry: extern "C" fn(*mut c_void) -> *mut c_void,
argument: *mut c_void,
) -> EResult<pthread_t> {
// TODO move defaults somewhere else
let stack_size = attr
.as_ref()
.map(|attr| attr.stack_size)
.unwrap_or(4096 * 16);
// If no custom stack was specified via pthread_attr_setstack, allocate owned stack
let stack_base = attr.as_ref().map(|attr| attr.stack).unwrap_or(null_mut());
let stack_size = stack_size.max(4096 * 8);
let (stack, stack_top) = match stack_base.is_null() {
// program-supplied stack
false => todo!(),
// NULL or unspecified stack
true => {
let base = unsafe {
yggdrasil_rt::sys::map_memory(
None,
stack_size,
MappingFlags::WRITE,
&MappingSource::Anonymous,
)
}?;
let top = base + stack_size;
yggdrasil_rt::debug_trace!("Allocate stack: {:#x?}", base..top);
(ThreadStack::Owned(base, stack_size), top)
}
};
let thread = Arc::new(Self {
id: AtomicU32::new(0),
result: AtomicPtr::new(null_mut()),
stack,
});
let argument = Box::into_raw(Box::new(ThreadArgument {
entry,
argument,
thread: thread.clone(),
}));
let options = ThreadSpawnOptions {
entry: Self::thread_entry,
argument: argument.addr(),
stack_top,
};
let id = unsafe { yggdrasil_rt::sys::spawn_thread(&options) }?;
thread.id.store(id, Ordering::Release);
THREADS.lock().insert(id, thread);
EResult::Ok(id)
}
pub fn join(id: pthread_t) -> EResult<Arc<Thread>> {
unsafe { yggdrasil_rt::sys::wait_thread(id) }?;
let thread = THREADS
.lock()
.remove(&id)
.expect("wait_thread() succeeded but thread not registered?");
EResult::Ok(thread)
}
pub fn id(&self) -> pthread_t {
self.id.load(Ordering::Acquire)
}
pub fn this() -> EResult<Arc<Thread>> {
let pthread_self: *const Self = ptr::with_exposed_provenance(pthread_self());
if !pthread_self.is_null() {
// Arc::from_raw creates an owned Arc, which will decrement refcount when dropped,
// prevent this by incrementing the count once more
unsafe { Arc::increment_strong_count(pthread_self) };
let arc = unsafe { Arc::from_raw(pthread_self) };
EResult::Ok(arc)
} else {
panic!("pthread_self is NULL")
}
}
pub fn result(&self) -> *mut c_void {
self.result.load(Ordering::Acquire)
}
extern "C" fn thread_entry(argument: usize) -> ! {
{
assert_ne!(argument, 0);
let argument: Box<ThreadArgument> =
unsafe { Box::from_raw(ptr::with_exposed_provenance_mut(argument)) };
yggdrasil_rt::debug_trace!(
"thread_entry entry={:p}, argument={:p}",
argument.entry,
argument.argument
);
yggdrasil_rt::debug_trace!("tls pointer: {:#x}", get_tls());
// TODO better way to initialize the thread ID
while argument.thread.id.load(Ordering::Acquire) == 0 {
core::hint::spin_loop();
}
argument.thread.clone().setup();
let result = (argument.entry)(argument.argument);
argument.thread.result.store(result, Ordering::Release);
// Drop the Arc<Self> written to TLS
let this = Self::this().expect("pthread_self() is NULL");
unsafe { Arc::decrement_strong_count(Arc::as_ptr(&this)) };
}
// Exit the thread when everything's dropped
unsafe { yggdrasil_rt::sys::exit_thread() }
}
fn setup(self: Arc<Self>) {
// Setup self-pointer
let pthread_self = Arc::into_raw(self);
unsafe { set_pthread_self(pthread_self.addr()) };
}
}
impl Drop for Thread {
fn drop(&mut self) {
yggdrasil_rt::debug_trace!("Drop thread {:?}", self.id);
if let &ThreadStack::Owned(base, size) = &self.stack {
yggdrasil_rt::debug_trace!("Drop stack {:#x?}", base..base + size);
unsafe { yggdrasil_rt::sys::unmap_memory(base, size) }.ok();
}
}
}

View File

@ -0,0 +1 @@