From db9db2cc40bf3f6c681a48d51af8d5b70e63c593 Mon Sep 17 00:00:00 2001 From: Bananymous Date: Sun, 20 Apr 2025 03:01:33 +0300 Subject: [PATCH] LibC: Implement basic pthread mutexes This implementation is just calling sched_yield if it could not get mutex. This is not optimal as it does not allow the CPU to idle, but it works for now :) Also I did not test this code at all, but it feels correct :D --- .../LibC/include/bits/pthread_types.h | 22 +-- userspace/libraries/LibC/include/pthread.h | 21 ++- userspace/libraries/LibC/pthread.cpp | 176 +++++++++++++++++- 3 files changed, 198 insertions(+), 21 deletions(-) diff --git a/userspace/libraries/LibC/include/bits/pthread_types.h b/userspace/libraries/LibC/include/bits/pthread_types.h index b2ef093c..e83d7ecf 100644 --- a/userspace/libraries/LibC/include/bits/pthread_types.h +++ b/userspace/libraries/LibC/include/bits/pthread_types.h @@ -40,18 +40,24 @@ __BEGIN_DECLS #endif #undef __need_pthread_key_t -#if !defined(__pthread_mutex_t_defined) && (defined(__need_all_types) || defined(__need_pthread_mutex_t)) - #define __pthread_mutex_t_defined 1 - typedef int pthread_mutex_t; +#if !defined(__pthread_t_defined) && (defined(__need_all_types) || defined(__need_pthread_t)) + #define __pthread_t_defined 1 + typedef pid_t pthread_t; #endif -#undef __need_pthread_mutex_t +#undef __need_pthread_t #if !defined(__pthread_mutexattr_t_defined) && (defined(__need_all_types) || defined(__need_pthread_mutexattr_t)) #define __pthread_mutexattr_t_defined 1 - typedef int pthread_mutexattr_t; + typedef struct { int type; bool shared; } pthread_mutexattr_t; #endif #undef __need_pthread_mutexattr_t +#if !defined(__pthread_mutex_t_defined) && (defined(__need_all_types) || defined(__need_pthread_mutex_t)) + #define __pthread_mutex_t_defined 1 + typedef struct { pthread_mutexattr_t attr; pthread_t locker; unsigned lock_depth; } pthread_mutex_t; +#endif +#undef __need_pthread_mutex_t + #if !defined(__pthread_once_t_defined) && (defined(__need_all_types) || defined(__need_pthread_once_t)) #define __pthread_once_t_defined 1 typedef int pthread_once_t; @@ -70,12 +76,6 @@ __BEGIN_DECLS #endif #undef __need_pthread_rwlockattr_t -#if !defined(__pthread_t_defined) && (defined(__need_all_types) || defined(__need_pthread_t)) - #define __pthread_t_defined 1 - typedef pid_t pthread_t; -#endif -#undef __need_pthread_t - #if !defined(__pthread_spinlock_t_defined) && (defined(__need_all_types) || defined(__need_pthread_spinlock_t)) #define __pthread_spinlock_t_defined 1 typedef pthread_t pthread_spinlock_t; diff --git a/userspace/libraries/LibC/include/pthread.h b/userspace/libraries/LibC/include/pthread.h index 273ffbcd..04318efa 100644 --- a/userspace/libraries/LibC/include/pthread.h +++ b/userspace/libraries/LibC/include/pthread.h @@ -46,23 +46,26 @@ struct uthread #define PTHREAD_CREATE_JOINABLE 8 #define PTHREAD_EXPLICIT_SCHED 9 #define PTHREAD_INHERIT_SCHED 10 -#define PTHREAD_MUTEX_DEFAULT 11 -#define PTHREAD_MUTEX_ERRORCHECK 12 -#define PTHREAD_MUTEX_NORMAL 13 -#define PTHREAD_MUTEX_RECURSIVE 14 -#define PTHREAD_MUTEX_ROBUST 15 -#define PTHREAD_MUTEX_STALLED 16 #define PTHREAD_ONCE_INIT 17 #define PTHREAD_PRIO_INHERIT 18 #define PTHREAD_PRIO_NONE 19 #define PTHREAD_PRIO_PROTECT 20 -#define PTHREAD_PROCESS_SHARED 21 -#define PTHREAD_PROCESS_PRIVATE 22 #define PTHREAD_SCOPE_PROCESS 23 #define PTHREAD_SCOPE_SYSTEM 24 +#define PTHREAD_PROCESS_SHARED 0 +#define PTHREAD_PROCESS_PRIVATE 1 + +#define PTHREAD_MUTEX_ROBUST 0 +#define PTHREAD_MUTEX_STALLED 1 + +#define PTHREAD_MUTEX_DEFAULT 0 +#define PTHREAD_MUTEX_ERRORCHECK 1 +#define PTHREAD_MUTEX_NORMAL 2 +#define PTHREAD_MUTEX_RECURSIVE 3 + #define PTHREAD_COND_INITIALIZER (pthread_cond_t)0 -#define PTHREAD_MUTEX_INITIALIZER (pthread_mutex_t)0 +#define PTHREAD_MUTEX_INITIALIZER (pthread_mutex_t){ { PTHREAD_MUTEX_DEFAULT, false }, 0, 0 } #define PTHREAD_RWLOCK_INITIALIZER (pthread_rwlock_t)0 int pthread_atfork(void (*prepare)(void), void (*parent)(void), void(*child)(void)); diff --git a/userspace/libraries/LibC/pthread.cpp b/userspace/libraries/LibC/pthread.cpp index f157fd15..f9d81ecc 100644 --- a/userspace/libraries/LibC/pthread.cpp +++ b/userspace/libraries/LibC/pthread.cpp @@ -1,6 +1,6 @@ #include -#include #include +#include #include #include @@ -349,6 +349,180 @@ int pthread_spin_unlock(pthread_spinlock_t* lock) return 0; } +int pthread_mutexattr_destroy(pthread_mutexattr_t* attr) +{ + (void)attr; + return 0; +} + +int pthread_mutexattr_init(pthread_mutexattr_t* attr) +{ + *attr = { + .type = PTHREAD_MUTEX_DEFAULT, + .shared = false, + }; + return 0; +} + +int pthread_mutexattr_getpshared(const pthread_mutexattr_t* __restrict attr, int* __restrict pshared) +{ + *pshared = attr->shared ? PTHREAD_PROCESS_SHARED : PTHREAD_PROCESS_PRIVATE; + return 0; +} + +int pthread_mutexattr_setpshared(pthread_mutexattr_t* attr, int pshared) +{ + switch (pshared) + { + case PTHREAD_PROCESS_PRIVATE: + attr->shared = false; + return 0; + case PTHREAD_PROCESS_SHARED: + attr->shared = true; + return 0; + } + return EINVAL; +} + +int pthread_mutexattr_gettype(const pthread_mutexattr_t* __restrict attr, int* __restrict type) +{ + *type = attr->type; + return 0; +} + +int pthread_mutexattr_settype(pthread_mutexattr_t* attr, int type) +{ + switch (type) + { + case PTHREAD_MUTEX_DEFAULT: + case PTHREAD_MUTEX_ERRORCHECK: + case PTHREAD_MUTEX_NORMAL: + case PTHREAD_MUTEX_RECURSIVE: + attr->type = type; + return 0; + } + return EINVAL; +} + +int pthread_mutex_destroy(pthread_mutex_t* mutex) +{ + (void)mutex; + return 0; +} + +int pthread_mutex_init(pthread_mutex_t* __restrict mutex, const pthread_mutexattr_t* __restrict attr) +{ + const pthread_mutexattr_t default_attr = { + .type = PTHREAD_MUTEX_DEFAULT, + .shared = false, + }; + if (attr == nullptr) + attr = &default_attr; + *mutex = { + .attr = *attr, + .locker = 0, + .lock_depth = 0, + }; + return 0; +} + +int pthread_mutex_lock(pthread_mutex_t* mutex) +{ + // NOTE: current yielding implementation supports shared + + const auto tid = pthread_self(); + + switch (mutex->attr.type) + { + case PTHREAD_MUTEX_RECURSIVE: + if (mutex->locker != tid) + break; + mutex->lock_depth++; + return 0; + case PTHREAD_MUTEX_ERRORCHECK: + if (mutex->locker != tid) + break; + return EDEADLK; + } + + pthread_t expected = 0; + while (!BAN::atomic_compare_exchange(mutex->locker, expected, tid, BAN::MemoryOrder::memory_order_acquire)) + { + sched_yield(); + expected = 0; + } + + mutex->lock_depth = 1; + return 0; +} + +int pthread_mutex_timedlock(pthread_mutex_t* __restrict mutex, const struct timespec* __restrict abstime) +{ + if (pthread_mutex_trylock(mutex) == 0) + return 0; + + constexpr auto has_timed_out = + [](const struct timespec* abstime) -> bool + { + struct timespec curtime; + clock_gettime(CLOCK_REALTIME, &curtime); + if (curtime.tv_sec < abstime->tv_sec) + return false; + if (curtime.tv_sec > abstime->tv_sec) + return true; + return curtime.tv_nsec >= abstime->tv_nsec; + }; + + while (!has_timed_out(abstime)) + { + if (pthread_mutex_trylock(mutex) == 0) + return 0; + sched_yield(); + } + + return ETIMEDOUT; +} + +int pthread_mutex_trylock(pthread_mutex_t* mutex) +{ + // NOTE: current yielding implementation supports shared + + const auto tid = pthread_self(); + + switch (mutex->attr.type) + { + case PTHREAD_MUTEX_RECURSIVE: + if (mutex->locker != tid) + break; + mutex->lock_depth++; + return 0; + case PTHREAD_MUTEX_ERRORCHECK: + if (mutex->locker != tid) + break; + return EDEADLK; + } + + pthread_t expected = 0; + if (!BAN::atomic_compare_exchange(mutex->locker, expected, tid, BAN::MemoryOrder::memory_order_acquire)) + return EBUSY; + + mutex->lock_depth = 1; + return 0; +} + +int pthread_mutex_unlock(pthread_mutex_t* mutex) +{ + // NOTE: current yielding implementation supports shared + + ASSERT(mutex->locker == pthread_self()); + + mutex->lock_depth--; + if (mutex->lock_depth == 0) + BAN::atomic_store(mutex->locker, 0, BAN::MemoryOrder::memory_order_release); + + return 0; +} + #if not __disable_thread_local_storage struct tls_index {