From 17eca4c0c0a3ffe6369e8d22acb4eb7a01043b96 Mon Sep 17 00:00:00 2001 From: Mark Poliakov Date: Fri, 15 Nov 2024 23:18:04 +0200 Subject: [PATCH] libc: basic pthread spawn/join --- lib/runtime/src/process/aarch64.rs | 27 +++ lib/runtime/src/process/i686.rs | 27 +++ .../src/{process.rs => process/mod.rs} | 60 ++---- lib/runtime/src/process/x86_64.rs | 35 ++++ test.c | 74 +------- userspace/lib/ygglibc/src/headers/mod.rs | 1 + .../lib/ygglibc/src/headers/pthread/attr.rs | 134 +++++++++++++ .../ygglibc/src/headers/pthread/barrier.rs | 9 + .../ygglibc/src/headers/pthread/cbindgen.toml | 14 ++ .../lib/ygglibc/src/headers/pthread/cond.rs | 16 ++ .../lib/ygglibc/src/headers/pthread/mod.rs | 51 +++++ .../lib/ygglibc/src/headers/pthread/mutex.rs | 31 +++ .../lib/ygglibc/src/headers/pthread/rwlock.rs | 15 ++ .../lib/ygglibc/src/headers/pthread/spin.rs | 6 + .../lib/ygglibc/src/headers/pthread/thread.rs | 119 ++++++++++++ .../lib/ygglibc/src/headers/pthread/tls.rs | 5 + .../src/headers/sys_types/cbindgen.toml | 16 +- .../lib/ygglibc/src/headers/sys_types/mod.rs | 39 +++- userspace/lib/ygglibc/src/lib.rs | 1 + userspace/lib/ygglibc/src/thread/mod.rs | 178 ++++++++++++++++++ userspace/lib/ygglibc/src/thread/tls.rs | 1 + 21 files changed, 751 insertions(+), 108 deletions(-) create mode 100644 lib/runtime/src/process/aarch64.rs create mode 100644 lib/runtime/src/process/i686.rs rename lib/runtime/src/{process.rs => process/mod.rs} (60%) create mode 100644 lib/runtime/src/process/x86_64.rs create mode 100644 userspace/lib/ygglibc/src/headers/pthread/attr.rs create mode 100644 userspace/lib/ygglibc/src/headers/pthread/barrier.rs create mode 100644 userspace/lib/ygglibc/src/headers/pthread/cbindgen.toml create mode 100644 userspace/lib/ygglibc/src/headers/pthread/cond.rs create mode 100644 userspace/lib/ygglibc/src/headers/pthread/mod.rs create mode 100644 userspace/lib/ygglibc/src/headers/pthread/mutex.rs create mode 100644 userspace/lib/ygglibc/src/headers/pthread/rwlock.rs create mode 100644 userspace/lib/ygglibc/src/headers/pthread/spin.rs create mode 100644 userspace/lib/ygglibc/src/headers/pthread/thread.rs create mode 100644 userspace/lib/ygglibc/src/headers/pthread/tls.rs create mode 100644 userspace/lib/ygglibc/src/thread/mod.rs create mode 100644 userspace/lib/ygglibc/src/thread/tls.rs diff --git a/lib/runtime/src/process/aarch64.rs b/lib/runtime/src/process/aarch64.rs new file mode 100644 index 00000000..dfacd2a9 --- /dev/null +++ b/lib/runtime/src/process/aarch64.rs @@ -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::(), 0); + let tp = tp - size_of::() * 2; + + unsafe { super::thread_local_area_common(tp) } +} diff --git a/lib/runtime/src/process/i686.rs b/lib/runtime/src/process/i686.rs new file mode 100644 index 00000000..e69d442b --- /dev/null +++ b/lib/runtime/src/process/i686.rs @@ -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::(), 0); + let tp = tp + size_of::() * 2; + + unsafe { super::thread_local_area_common(tp) } +} diff --git a/lib/runtime/src/process.rs b/lib/runtime/src/process/mod.rs similarity index 60% rename from lib/runtime/src/process.rs rename to lib/runtime/src/process/mod.rs index 080cff8e..6eaf1767 100644 --- a/lib/runtime/src/process.rs +++ b/lib/runtime/src/process/mod.rs @@ -11,6 +11,23 @@ use crate::sys; const THREAD_LOCAL_AREA_SIZE: usize = 4096 / size_of::(); +#[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 /// /// `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) } } -#[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::(), 0); - let tp = tp + size_of::() * 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::(), 0); - let tp = tp - size_of::() * 2; - - unsafe { thread_local_area_common(tp) } -} - /// Returns a reference to the thread-local area, which contains thread-local pointers pub fn thread_local_area() -> &'static mut [*mut u8] { // TODO grow thread local areas based on size requested? - thread_local_area_impl() + imp::thread_local_area_impl() } diff --git a/lib/runtime/src/process/x86_64.rs b/lib/runtime/src/process/x86_64.rs new file mode 100644 index 00000000..45aa628b --- /dev/null +++ b/lib/runtime/src/process/x86_64.rs @@ -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::(), 0); + let tp = tp + size_of::() * 2; + + unsafe { super::thread_local_area_common(tp) } +} diff --git a/test.c b/test.c index c3cfe61e..f3eb283c 100644 --- a/test.c +++ b/test.c @@ -1,73 +1,19 @@ +#include #include #include -#include -#include -#include -#include -#include + +static void *function(void *arg) { + printf("This is thread %u\n", pthread_self()); + printf("argument: %s\n", (const char *) arg); + return NULL; +} int main(int argc, const char **argv) { - char *val; + pthread_t thread; - assert(environ != NULL); - assert(*environ == NULL); + assert(pthread_create(&thread, NULL, function, (void *) "thread1") == 0); - setenv("key1", "value1", 0); - 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); + pthread_join(thread, NULL); return 0; } diff --git a/userspace/lib/ygglibc/src/headers/mod.rs b/userspace/lib/ygglibc/src/headers/mod.rs index 5aa32977..c6146491 100644 --- a/userspace/lib/ygglibc/src/headers/mod.rs +++ b/userspace/lib/ygglibc/src/headers/mod.rs @@ -135,3 +135,4 @@ pub mod sys_wait; // TODO Generate those as part of dyn-loader (and make dyn-loader a shared library) pub mod link; +pub mod pthread; diff --git a/userspace/lib/ygglibc/src/headers/pthread/attr.rs b/userspace/lib/ygglibc/src/headers/pthread/attr.rs new file mode 100644 index 00000000..f8f4f29c --- /dev/null +++ b/userspace/lib/ygglibc/src/headers/pthread/attr.rs @@ -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!() +} diff --git a/userspace/lib/ygglibc/src/headers/pthread/barrier.rs b/userspace/lib/ygglibc/src/headers/pthread/barrier.rs new file mode 100644 index 00000000..965acb98 --- /dev/null +++ b/userspace/lib/ygglibc/src/headers/pthread/barrier.rs @@ -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); diff --git a/userspace/lib/ygglibc/src/headers/pthread/cbindgen.toml b/userspace/lib/ygglibc/src/headers/pthread/cbindgen.toml new file mode 100644 index 00000000..e2e5bd07 --- /dev/null +++ b/userspace/lib/ygglibc/src/headers/pthread/cbindgen.toml @@ -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" diff --git a/userspace/lib/ygglibc/src/headers/pthread/cond.rs b/userspace/lib/ygglibc/src/headers/pthread/cond.rs new file mode 100644 index 00000000..b7f1ef82 --- /dev/null +++ b/userspace/lib/ygglibc/src/headers/pthread/cond.rs @@ -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); diff --git a/userspace/lib/ygglibc/src/headers/pthread/mod.rs b/userspace/lib/ygglibc/src/headers/pthread/mod.rs new file mode 100644 index 00000000..f463ecaf --- /dev/null +++ b/userspace/lib/ygglibc/src/headers/pthread/mod.rs @@ -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)); + diff --git a/userspace/lib/ygglibc/src/headers/pthread/mutex.rs b/userspace/lib/ygglibc/src/headers/pthread/mutex.rs new file mode 100644 index 00000000..1b09c855 --- /dev/null +++ b/userspace/lib/ygglibc/src/headers/pthread/mutex.rs @@ -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); diff --git a/userspace/lib/ygglibc/src/headers/pthread/rwlock.rs b/userspace/lib/ygglibc/src/headers/pthread/rwlock.rs new file mode 100644 index 00000000..8f081f7e --- /dev/null +++ b/userspace/lib/ygglibc/src/headers/pthread/rwlock.rs @@ -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); diff --git a/userspace/lib/ygglibc/src/headers/pthread/spin.rs b/userspace/lib/ygglibc/src/headers/pthread/spin.rs new file mode 100644 index 00000000..6c969db4 --- /dev/null +++ b/userspace/lib/ygglibc/src/headers/pthread/spin.rs @@ -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 *); diff --git a/userspace/lib/ygglibc/src/headers/pthread/thread.rs b/userspace/lib/ygglibc/src/headers/pthread/thread.rs new file mode 100644 index 00000000..e84c939d --- /dev/null +++ b/userspace/lib/ygglibc/src/headers/pthread/thread.rs @@ -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!() +} diff --git a/userspace/lib/ygglibc/src/headers/pthread/tls.rs b/userspace/lib/ygglibc/src/headers/pthread/tls.rs new file mode 100644 index 00000000..9743a870 --- /dev/null +++ b/userspace/lib/ygglibc/src/headers/pthread/tls.rs @@ -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 *); diff --git a/userspace/lib/ygglibc/src/headers/sys_types/cbindgen.toml b/userspace/lib/ygglibc/src/headers/sys_types/cbindgen.toml index a2987928..379b156b 100644 --- a/userspace/lib/ygglibc/src/headers/sys_types/cbindgen.toml +++ b/userspace/lib/ygglibc/src/headers/sys_types/cbindgen.toml @@ -26,5 +26,19 @@ include = [ "suseconds_t", "time_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" ] diff --git a/userspace/lib/ygglibc/src/headers/sys_types/mod.rs b/userspace/lib/ygglibc/src/headers/sys_types/mod.rs index 474c9a88..c59f0af6 100644 --- a/userspace/lib/ygglibc/src/headers/sys_types/mod.rs +++ b/userspace/lib/ygglibc/src/headers/sys_types/mod.rs @@ -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 uid_t = i32; @@ -36,3 +36,40 @@ pub type suseconds_t = c_ulong; impl time_t { 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; diff --git a/userspace/lib/ygglibc/src/lib.rs b/userspace/lib/ygglibc/src/lib.rs index 43c8cfd6..7d0db451 100644 --- a/userspace/lib/ygglibc/src/lib.rs +++ b/userspace/lib/ygglibc/src/lib.rs @@ -39,6 +39,7 @@ mod ssp; mod sync; mod types; mod util; +mod thread; pub mod headers; diff --git a/userspace/lib/ygglibc/src/thread/mod.rs b/userspace/lib/ygglibc/src/thread/mod.rs new file mode 100644 index 00000000..cbdee20a --- /dev/null +++ b/userspace/lib/ygglibc/src/thread/mod.rs @@ -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>> = 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, +} + +#[repr(C)] +pub struct Thread { + id: AtomicU32, + stack: ThreadStack, + result: AtomicPtr, +} + +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 { + // 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> { + 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> { + 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 = + 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 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) { + // 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(); + } + } +} diff --git a/userspace/lib/ygglibc/src/thread/tls.rs b/userspace/lib/ygglibc/src/thread/tls.rs new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/userspace/lib/ygglibc/src/thread/tls.rs @@ -0,0 +1 @@ +