From 683484fedac098e0a2380e4bc9475d512ec3782f Mon Sep 17 00:00:00 2001 From: Adam Greloch Date: Mon, 20 Jul 2026 16:20:26 +0200 Subject: [PATCH 1/3] pthread: set key_data_list to NULL after freeing TASK: RTOS-1389 --- pthread/pthread.c | 1 + 1 file changed, 1 insertion(+) diff --git a/pthread/pthread.c b/pthread/pthread.c index 9fd464d7..114596aa 100644 --- a/pthread/pthread.c +++ b/pthread/pthread.c @@ -459,6 +459,7 @@ static void pthread_key_cleanup(pthread_ctx *ctx) key_data = key_data->next; free(curr); } + ctx->key_data_list = NULL; mutexUnlock(pthread_common.pthread_key_lock); } From 1423caac3c4189f638d21977982bb8f32cba376f Mon Sep 17 00:00:00 2001 From: Adam Greloch Date: Mon, 13 Jul 2026 12:22:56 +0200 Subject: [PATCH 2/3] pthread: implement pthread_testcancel TASK: RTOS-1389 --- include/pthread.h | 3 +++ pthread/pthread.c | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/include/pthread.h b/include/pthread.h index c55c1d2d..a0c2efb5 100644 --- a/include/pthread.h +++ b/include/pthread.h @@ -76,6 +76,9 @@ int pthread_setcancelstate(int state, int *oldstate); int pthread_cancel(pthread_t thread); +void pthread_testcancel(void); + + pthread_t pthread_self(void); diff --git a/pthread/pthread.c b/pthread/pthread.c index 114596aa..688ec74d 100644 --- a/pthread/pthread.c +++ b/pthread/pthread.c @@ -46,6 +46,7 @@ typedef struct pthread_ctx { struct pthread_ctx *prev; int is_detached; int cancelstate; + int cancelled; struct __errno_t e; int refcount; struct pthread_key_data_t *key_data_list; @@ -206,6 +207,7 @@ static int pthread_create_main(void) ctx->stacksize = 0; ctx->is_detached = (pthread_attr_default.detachstate == PTHREAD_CREATE_DETACHED) ? 1 : 0; ctx->cancelstate = PTHREAD_CANCEL_ENABLE; + ctx->cancelled = 0; ctx->refcount = 1; ctx->key_data_list = NULL; ctx->cleanup_list = NULL; @@ -277,6 +279,7 @@ int pthread_create(pthread_t *thread, const pthread_attr_t *attr, ctx->stacksize = stacksize; ctx->key_data_list = NULL; ctx->cancelstate = PTHREAD_CANCEL_ENABLE; + ctx->cancelled = 0; ctx->cleanup_list = NULL; *thread = (pthread_t)ctx; @@ -478,6 +481,7 @@ int pthread_cancel(pthread_t thread) self = pthread_self(); mutexLock(pthread_common.pthread_list_lock); _pthread_ctx_get(ctx); + ctx->cancelled = 1; if (thread == self) { if (ctx->cancelstate == PTHREAD_CANCEL_ENABLE) { _pthread_ctx_put(ctx); @@ -505,6 +509,20 @@ int pthread_cancel(pthread_t thread) } +void pthread_testcancel(void) +{ + pthread_ctx *ctx = (pthread_ctx *)pthread_self(); + mutexLock(pthread_common.pthread_list_lock); + _pthread_ctx_get(ctx); + if (ctx->cancelstate == PTHREAD_CANCEL_ENABLE && ctx->cancelled != 0) { + _pthread_ctx_put(ctx); + pthread_exit((void *)PTHREAD_CANCELED); + /* no return */ + } + _pthread_ctx_put(ctx); +} + + pthread_t pthread_self(void) { pthread_ctx *ctx = pthread_find(gettid()); From eca4cfd2a4801e82e0318b4b23e250d98df85fe9 Mon Sep 17 00:00:00 2001 From: Adam Greloch Date: Mon, 13 Jul 2026 12:22:45 +0200 Subject: [PATCH 3/3] pthread: implement rwlocks TASK: RTOS-1389 --- include/pthread.h | 30 ++++- include/sys/types.h | 16 ++- pthread/pthread.c | 307 +++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 327 insertions(+), 26 deletions(-) diff --git a/include/pthread.h b/include/pthread.h index a0c2efb5..f763c4ae 100644 --- a/include/pthread.h +++ b/include/pthread.h @@ -5,8 +5,8 @@ * * pthread * - * Copyright 2017-2018, 2019 Phoenix Systems - * Author: Pawel Pisarczyk, Michal Miroslaw, Marcin Baran + * Copyright 2017-2018, 2019, 2026 Phoenix Systems + * Author: Pawel Pisarczyk, Michal Miroslaw, Marcin Baran, Adam Greloch * * This file is part of Phoenix-RTOS. * @@ -45,8 +45,6 @@ extern "C" { #define PTHREAD_SCOPE_PROCESS 0 #define PTHREAD_SCOPE_SYSTEM 1 -#define PTHREAD_MUTEX_INITIALIZER { 0, 0 } - #define PTHREAD_MUTEX_NORMAL PH_LOCK_NORMAL #define PTHREAD_MUTEX_ERRORCHECK PH_LOCK_ERRORCHECK #define PTHREAD_MUTEX_RECURSIVE PH_LOCK_RECURSIVE @@ -58,7 +56,11 @@ extern "C" { #define PTHREAD_CANCEL_ENABLE 1 #define PTHREAD_CANCELED 2 -#define PTHREAD_COND_INITIALIZER { 0, 0 } +/* clang-format off */ +#define PTHREAD_MUTEX_INITIALIZER { 0, 0 } +#define PTHREAD_COND_INITIALIZER { 0, 0 } +#define PTHREAD_RWLOCK_INITIALIZER { 0 } +/* clang-format on */ int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg); @@ -279,9 +281,27 @@ int pthread_rwlock_unlock(pthread_rwlock_t *rwlock); int pthread_rwlock_destroy(pthread_rwlock_t *rwlock); +int pthread_rwlock_timedrdlock(pthread_rwlock_t *__restrict__ rwlock, const struct timespec *__restrict__ abs_timeout); + + +int pthread_rwlock_timedwrlock(pthread_rwlock_t *__restrict__ rwlock, const struct timespec *__restrict__ abs_timeout); + + int pthread_rwlock_init(pthread_rwlock_t *__restrict__ rwlock, const pthread_rwlockattr_t *__restrict__ attr); +int pthread_rwlockattr_destroy(pthread_rwlockattr_t *attr); + + +int pthread_rwlockattr_init(pthread_rwlockattr_t *attr); + + +int pthread_rwlockattr_getpshared(const pthread_rwlockattr_t *__restrict__ attr, int *__restrict__ pshared); + + +int pthread_rwlockattr_setpshared(pthread_rwlockattr_t *attr, int pshared); + + #ifdef __cplusplus } #endif diff --git a/include/sys/types.h b/include/sys/types.h index 89d17d86..081a08bf 100644 --- a/include/sys/types.h +++ b/include/sys/types.h @@ -54,10 +54,20 @@ typedef struct { volatile int initialized; } pthread_mutex_t; -/* TODO */ -typedef int pthread_rwlock_t; +typedef struct { + handle_t lock; + handle_t readCond; + handle_t writeCond; + size_t readActive; + size_t writeActive; + size_t writeWaiting; + volatile int initialized; +} pthread_rwlock_t; + -typedef int pthread_rwlockattr_t; +typedef struct { + int pshared; +} pthread_rwlockattr_t; typedef struct lockAttr pthread_mutexattr_t; diff --git a/pthread/pthread.c b/pthread/pthread.c index 688ec74d..abe79ce1 100644 --- a/pthread/pthread.c +++ b/pthread/pthread.c @@ -5,8 +5,8 @@ * * pthread * - * Copyright 2017, 2019, 2023 Phoenix Systems - * Author: Pawel Pisarczyk, Marcin Baran, Hubert Badocha + * Copyright 2017, 2019, 2023, 2026 Phoenix Systems + * Author: Pawel Pisarczyk, Marcin Baran, Hubert Badocha, Adam Greloch * * This file is part of Phoenix-RTOS. * @@ -110,6 +110,11 @@ static const pthread_attr_t pthread_attr_default = { }; +static const pthread_rwlockattr_t pthread_rwlockattr_default = { + .pshared = PTHREAD_PROCESS_PRIVATE, +}; + + static __attribute__((noreturn)) void pthread_do_exit(pthread_ctx *ctx, void *value_ptr, int cleanup); @@ -1096,6 +1101,12 @@ static time_t timespec_to_us(const struct timespec *__restrict time) } +static int timespec_is_valid(const struct timespec *__restrict time) +{ + return time != NULL && time->tv_nsec >= 0 && time->tv_nsec < 1000000000L; +} + + int pthread_cond_timedwait(pthread_cond_t *__restrict cond, pthread_mutex_t *__restrict mutex, const struct timespec *__restrict abstime) @@ -1417,54 +1428,314 @@ void pthread_cleanup_pop(int execute) } -/* FIXME: rwlocks are yet to be implemented, that breaks shared_mutex in cpp. */ +static int pthread_rwlock_lazy_init(pthread_rwlock_t *__restrict__ rwlock, const pthread_rwlockattr_t *__restrict__ attr) +{ + int err = EOK; + + (void)attr; + + if (rwlock->initialized != 0) { + return EOK; + } + + mutexLock(pthread_common.mutex_cond_init_lock); + + do { + if (rwlock->initialized != 0) { + break; + } + + err = mutexCreate(&rwlock->lock); + if (err < 0) { + break; + } + + struct condAttr cattr = { 0 }; + cattr.clock = PH_CLOCK_REALTIME; + + err = condCreateWithAttr(&rwlock->readCond, &cattr); + if (err < 0) { + resourceDestroy(rwlock->lock); + break; + } + + err = condCreateWithAttr(&rwlock->writeCond, &cattr); + if (err < 0) { + resourceDestroy(rwlock->readCond); + resourceDestroy(rwlock->lock); + break; + } + + rwlock->readActive = 0; + rwlock->writeActive = 0; + rwlock->writeWaiting = 0; + rwlock->initialized = 1; + } while (0); + + mutexUnlock(pthread_common.mutex_cond_init_lock); + + return -err; +} + + +static int pthread_rwlock_rdlock_ex(pthread_rwlock_t *rwlock, int block, int timeout) +{ + int err = pthread_rwlock_lazy_init(rwlock, NULL); + if (err != EOK) { + return err; + } + + /* TODO: can rwlocks be robust? */ + mutexLock(rwlock->lock); + + /* avoid starving the waiting writers by letting them through */ + while (rwlock->writeActive > 0 || rwlock->writeWaiting > 0) { + if (block == 0) { + err = EBUSY; + break; + } + + err = condWait(rwlock->readCond, rwlock->lock, timeout); + + if (err == -ETIME) { + err = ETIMEDOUT; + break; + } + else if (err == -EINTR) { + err = EOK; + } + else if (err < 0) { + err = -err; + break; + } + } + + if (err == EOK) { + rwlock->readActive++; + } + + mutexUnlock(rwlock->lock); + + return err; +} + + +static int pthread_rwlock_wrlock_ex(pthread_rwlock_t *rwlock, int block, int timeout) +{ + int err = pthread_rwlock_lazy_init(rwlock, NULL); + if (err != EOK) { + return err; + } + + mutexLock(rwlock->lock); + + if (block != 0) { + rwlock->writeWaiting++; + } + + while (rwlock->readActive > 0 || rwlock->writeActive > 0) { + if (block == 0) { + err = EBUSY; + break; + } + + err = condWait(rwlock->writeCond, rwlock->lock, timeout); + + if (err == -ETIME) { + err = ETIMEDOUT; + break; + } + else if (err == -EINTR) { + err = EOK; + } + else if (err < 0) { + err = -err; + break; + } + } + + if (block != 0) { + rwlock->writeWaiting--; + } + + if (err == EOK) { + rwlock->writeActive++; + } + + mutexUnlock(rwlock->lock); + + return err; +} + + int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock) { - (void)rwlock; - return ENOSYS; + return pthread_rwlock_rdlock_ex(rwlock, 1, 0); } int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock) { - (void)rwlock; - return ENOSYS; + return pthread_rwlock_rdlock_ex(rwlock, 0, 0); } int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock) { - (void)rwlock; - return ENOSYS; + return pthread_rwlock_wrlock_ex(rwlock, 0, 0); } int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock) { - (void)rwlock; - return ENOSYS; + return pthread_rwlock_wrlock_ex(rwlock, 1, 0); } int pthread_rwlock_unlock(pthread_rwlock_t *rwlock) { - (void)rwlock; - return ENOSYS; + int err = pthread_rwlock_lazy_init(rwlock, NULL); + if (err != EOK) { + return err; + } + + mutexLock(rwlock->lock); + + if (rwlock->writeActive > 0) { + /* caller is a writer, as there can be at most one */ + rwlock->writeActive--; + + /* avoid starving the writers by waking waiting writers first */ + if (rwlock->writeWaiting > 0) { + condSignal(rwlock->writeCond); + } + else { + condBroadcast(rwlock->readCond); + } + } + else if (rwlock->readActive > 0) { + rwlock->readActive--; + + if (rwlock->readActive == 0 && rwlock->writeWaiting > 0) { + condSignal(rwlock->writeCond); + } + } + else { + /* caller does not hold this lock */ + err = EPERM; + } + + mutexUnlock(rwlock->lock); + + return err; +} + + +int pthread_rwlock_timedrdlock(pthread_rwlock_t *restrict rwlock, const struct timespec *restrict abs_timeout) +{ + if (timespec_is_valid(abs_timeout) == 0) { + return EINVAL; + } + + const time_t abs_timeout_us = timespec_to_us(abs_timeout); + + if (abs_timeout_us == 0) { + return ETIMEDOUT; + } + + return pthread_rwlock_rdlock_ex(rwlock, 1, abs_timeout_us); +} + + +int pthread_rwlock_timedwrlock(pthread_rwlock_t *restrict rwlock, const struct timespec *restrict abs_timeout) +{ + if (timespec_is_valid(abs_timeout) == 0) { + return EINVAL; + } + + const time_t abs_timeout_us = timespec_to_us(abs_timeout); + + if (abs_timeout_us == 0) { + return ETIMEDOUT; + } + + return pthread_rwlock_wrlock_ex(rwlock, 1, abs_timeout_us); +} + + +int pthread_rwlockattr_destroy(pthread_rwlockattr_t *attr) +{ + return attr == NULL ? EINVAL : EOK; +} + + +int pthread_rwlockattr_init(pthread_rwlockattr_t *attr) +{ + if (attr == NULL) { + return EINVAL; + } + + *attr = pthread_rwlockattr_default; + + return EOK; +} + + +int pthread_rwlockattr_getpshared(const pthread_rwlockattr_t *restrict attr, int *restrict pshared) +{ + if (attr == NULL || pshared == NULL) { + return EINVAL; + } + + *pshared = attr->pshared; + + return EOK; +} + + +int pthread_rwlockattr_setpshared(pthread_rwlockattr_t *attr, int pshared) +{ + int err = EOK; + + if (attr == NULL || pshared != PTHREAD_PROCESS_PRIVATE) { + err = EINVAL; + } + else { + attr->pshared = pshared; + } + + return err; } int pthread_rwlock_destroy(pthread_rwlock_t *rwlock) { - (void)rwlock; - return ENOSYS; + if (rwlock == NULL) { + return EINVAL; + } + + mutexLock(pthread_common.mutex_cond_init_lock); + if (rwlock->initialized != 0) { + resourceDestroy(rwlock->writeCond); + resourceDestroy(rwlock->readCond); + resourceDestroy(rwlock->lock); + rwlock->initialized = 0; + } + mutexUnlock(pthread_common.mutex_cond_init_lock); + + return EOK; } int pthread_rwlock_init(pthread_rwlock_t *__restrict__ rwlock, const pthread_rwlockattr_t *__restrict__ attr) { - (void)rwlock; - (void)attr; - return ENOSYS; + if (rwlock == NULL) { + return EINVAL; + } + + rwlock->initialized = 0; + + return pthread_rwlock_lazy_init(rwlock, attr); }