From bb5961f15844f054e9b6354ecdbdf7e1a67003e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Lach?= Date: Mon, 19 Jan 2026 18:40:35 +0100 Subject: [PATCH 1/4] semaphores: add semaphoreTryDown and semaphoreCount functions JIRA: RTOS-1088 --- include/sys/threads.h | 6 +++++ sys/semaphore.c | 54 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 59 insertions(+), 1 deletion(-) diff --git a/include/sys/threads.h b/include/sys/threads.h index d5113427..3d6cf394 100644 --- a/include/sys/threads.h +++ b/include/sys/threads.h @@ -113,12 +113,18 @@ extern int semaphoreCreate(semaphore_t *s, unsigned int v); extern int semaphoreDown(semaphore_t *s, time_t timeout); +extern int semaphoreTryDown(semaphore_t *s); + + extern int semaphoreUp(semaphore_t *s); extern int semaphoreDone(semaphore_t *s); +extern int semaphoreCount(semaphore_t *s); + + extern int phCondCreate(handle_t *h, const struct condAttr *attr); diff --git a/sys/semaphore.c b/sys/semaphore.c index 48109d1e..f2aa86d0 100644 --- a/sys/semaphore.c +++ b/sys/semaphore.c @@ -5,7 +5,7 @@ * * Copyright 2012, 2017, 2018, 2026 Phoenix Systems * Copyright 2006 Pawel Pisarczyk - * Author: Pawel Pisarczyk, Aleksander Kaminski + * Author: Pawel Pisarczyk, Aleksander Kaminski, Michał Lach * * This file is part of Phoenix-RTOS. * @@ -13,17 +13,44 @@ */ #include +#include #include #include #include +#include #include +int semaphoreCount(semaphore_t *s) +{ + int ret = 0; + + if (s == NULL) { + return -EINVAL; + } + + ret = mutexTry(s->mutex); + if (ret != EOK) { + ret = 0; + } + else { + ret = s->v; + assert(ret >= 0); /* s->v is unsigned, we don't want to overflow */ + mutexUnlock(s->mutex); + } + + return ret; +} + + int semaphoreCreate(semaphore_t *s, unsigned int v) { static const struct condAttr cAttr = { .clock = PH_CLOCK_MONOTONIC }; int err; + if (s == NULL || v > SEM_VALUE_MAX) + return -EINVAL; + err = mutexCreate(&s->mutex); if (err < 0) { return err; @@ -68,6 +95,31 @@ int semaphoreDown(semaphore_t *s, time_t timeout) } +int semaphoreTryDown(semaphore_t *s) +{ + int ret = EOK; + + if (s != NULL) { + ret = mutexTry(s->mutex); + if (ret != EOK) { + return ret; + } + else if (s->v <= 0) { + ret = -EAGAIN; + } + else { + --s->v; + } + } + else { + ret = -EINVAL; + } + + mutexUnlock(s->mutex); + return ret; +} + + int semaphoreUp(semaphore_t *s) { mutexLock(s->mutex); From 8880205ebf70ec3de42479d1c9e1c9941c0f674d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Lach?= Date: Wed, 7 Jan 2026 17:44:50 +0100 Subject: [PATCH 2/4] posix: implement sem_* family functions JIRA: RTOS-1088 --- include/semaphore.h | 61 ++++++ include/sys/semaphore.h | 28 +++ posix/Makefile | 2 +- posix/sem.c | 424 ++++++++++++++++++++++++++++++++++++++++ sys/semaphore.c | 2 +- 5 files changed, 515 insertions(+), 2 deletions(-) create mode 100644 include/semaphore.h create mode 100644 include/sys/semaphore.h create mode 100644 posix/sem.c diff --git a/include/semaphore.h b/include/semaphore.h new file mode 100644 index 00000000..332acccd --- /dev/null +++ b/include/semaphore.h @@ -0,0 +1,61 @@ +/* + * Phoenix-RTOS + * + * libphoenix + * + * POSIX implementation - semaphores + * + * Copyright 2026 Phoenix Systems + * Author: Michał Lach + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#include +#include +#include +#include + +#define SEM_FAILED ((sem_t *)0xDAAB0000) + +typedef struct _sem_t { + /* clang-format off */ + enum { smNamed, smUnnamed } type; + /* clang-format on */ + + union { + semaphore_t unnamed; + oid_t named; + }; +} sem_t; + +extern int sem_wait(sem_t *sem); + + +extern int sem_trywait(sem_t *sem); + + +extern int sem_timedwait(sem_t *restrict sem, const struct timespec *restrict abs_timeout); + + +extern int sem_getvalue(sem_t *restrict sem, int *restrict value); + + +extern int sem_post(sem_t *sem); + + +extern int sem_close(sem_t *sem); + + +extern sem_t *sem_open(const char *name, int oflag, ... /* mode_t mode, unsigned int value */); + + +extern int sem_unlink(const char *name); + + +extern int sem_destroy(sem_t *sem); + + +extern int sem_init(sem_t *sem, int pshared, unsigned int value); diff --git a/include/sys/semaphore.h b/include/sys/semaphore.h new file mode 100644 index 00000000..7ce38e8f --- /dev/null +++ b/include/sys/semaphore.h @@ -0,0 +1,28 @@ +/* + * Phoenix-RTOS + * + * libphoenix + * + * POSIX implementation - semaphores + * + * Copyright 2026 Phoenix Systems + * Author: Michał Lach + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#include +#include + +#define SEMAPHORE_PATH "/dev/posix/sem/" +#define SEMCTL_PATH "/dev/posix/semctl" + +STATIC_ASSERT(SEM_VALUE_MAX >= _POSIX_SEM_VALUE_MAX, "SEM_VALUE_MAX shall be greater or equal to _POSIX_SEM_VALUE_MAX"); +STATIC_ASSERT(SEM_NSEMS_MAX >= _POSIX_SEM_NSEMS_MAX, "SEM_NSEMS_MAX shall be greater or equal to _POSIX_SEM_NSEMS_MAX"); + +#define SEM_UP _IO('s', 0x1) +#define SEM_DOWN _IO('s', 0x2) +#define SEM_DOWN_TRY _IO('s', 0x3) +#define SEM_DOWN_TIMEOUT _IOW('s', 0x4, time_t) diff --git a/posix/Makefile b/posix/Makefile index e4d46467..722f2876 100644 --- a/posix/Makefile +++ b/posix/Makefile @@ -5,4 +5,4 @@ # Author: Pawel Pisarczyk # -OBJS += $(addprefix $(PREFIX_O)posix/, stubs.o utils.o idtree.o) +OBJS += $(addprefix $(PREFIX_O)posix/, stubs.o utils.o idtree.o sem.o) diff --git a/posix/sem.c b/posix/sem.c new file mode 100644 index 00000000..22fff493 --- /dev/null +++ b/posix/sem.c @@ -0,0 +1,424 @@ +/* + * Phoenix-RTOS + * + * libphoenix + * + * POSIX implementation - semaphores + * + * Copyright 2026 Phoenix Systems + * Author: Michal Lach + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int _sem_open(sem_t *sem) +{ + int ret = EOK; + msg_t msg = { + .type = mtOpen, + .oid = sem->named, + }; + + ret = msgSend(sem->named.port, &msg); + if (ret != EOK) { + return ret; + } + + return msg.o.err; +} + + +static int _sem_down(sem_t *sem, bool try, time_t timeout) +{ + int ret = EOK; + ioctl_in_t *data; + msg_t msg = { + .type = mtDevCtl, + .oid = sem->named, + }; + + data = (ioctl_in_t *)&msg.i.raw; + if (try) { + data->request = SEM_DOWN_TRY; + } + else if (timeout > 0) { + data->request = SEM_DOWN_TIMEOUT; + *((time_t *)data->data) = timeout; + } + else { + data->request = SEM_DOWN; + } + + ret = msgSend(sem->named.port, &msg); + if (ret != EOK) { + return ret; + } + + return msg.o.err; +} + + +static int _sem_create(const char *name, int value, oid_t *oid) +{ + int ret = EOK; + oid_t semctl; + msg_t msg = { 0 }; + + ret = lookup(SEMCTL_PATH, NULL, &semctl); + if (ret != EOK) { + return ret; + } + + msg.type = mtCreate; + msg.oid = semctl; + msg.i.create.type = value; + msg.i.create.mode = 0; + msg.i.data = name; + msg.i.size = strlen(name) + 1; + + ret = msgSend(semctl.port, &msg); + if (ret != EOK) { + return ret; + } + + *oid = msg.o.create.oid; + return msg.o.err; +} + + +static int _sem_lookup(const char *name, oid_t *sem) +{ + int ret = EOK; + DIR *dirp; + struct dirent *dent; + oid_t oid; + char *path; + + dirp = opendir(SEMAPHORE_PATH); + if (dirp == NULL) { + /* posixsrv has yet not initialized the semaphore subsystem */ + return -ENODEV; + } + + path = malloc(PATH_MAX + 1); + if (path == NULL) { + return -ENOMEM; + } + + memset(path, 0, PATH_MAX + 1); + + for (;;) { + errno = 0; + dent = readdir(dirp); + if (dent == NULL) { + closedir(dirp); + ret = -ENOENT; + break; + } + + if (strcmp(name, dent->d_name) == 0) { + strcpy(path, SEMAPHORE_PATH); + strcpy(path + strlen(SEMAPHORE_PATH), dent->d_name); + lookup(path, NULL, &oid); + *sem = oid; + closedir(dirp); + ret = EOK; + break; + } + } + + free(path); + + return ret; +} + + +sem_t *sem_open(const char *name, int oflag, ...) +{ + va_list list; + int err = EOK; + mode_t mode = 0; + unsigned int value = 0; + sem_t *sem; + + (void)mode; + + if (name == NULL) { + SET_ERRNO(-EINVAL); + return SEM_FAILED; + } + + sem = (sem_t *)malloc(sizeof(*sem)); + if (sem == NULL) { + SET_ERRNO(-ENOMEM); + return SEM_FAILED; + } + + sem->type = smNamed; + + if ((oflag & O_CREAT) > 0) { + va_start(list, oflag); + mode = va_arg(list, mode_t); + value = va_arg(list, unsigned int); + va_end(list); + } + + if ((oflag & O_CREAT) > 0 && (oflag & O_EXCL) > 0) { + err = _sem_create(name, value, &sem->named); + } + else { + err = _sem_lookup(name, &sem->named); + if ((oflag & O_CREAT) > 0 && err < 0) { + err = _sem_create(name, value, &sem->named); + } + } + + if (err != EOK) { + SET_ERRNO(err); + free(sem); + return SEM_FAILED; + } + else { + err = _sem_open(sem); + } + + if (err != EOK) { + SET_ERRNO(err); + free(sem); + return SEM_FAILED; + } + + return sem; +} + + +int sem_wait(sem_t *sem) +{ + int ret = EOK; + + if (sem == NULL) { + return SET_ERRNO(-EINVAL); + } + + if (sem->type == smNamed) { + ret = _sem_down(sem, false, 0); + } + else if (sem->type == smUnnamed) { + ret = semaphoreDown(&sem->unnamed, 0); + } + else { + ret = -EINVAL; + } + + return SET_ERRNO(ret); +} + + +int sem_trywait(sem_t *sem) +{ + int ret = EOK; + + if (sem == NULL) { + return SET_ERRNO(-EINVAL); + } + + if (sem->type == smNamed) { + ret = _sem_down(sem, true, 0); + } + else if (sem->type == smUnnamed) { + ret = semaphoreTryDown(&sem->unnamed); + } + else { + ret = -EINVAL; + } + + return SET_ERRNO(ret); +} + + +int sem_timedwait(sem_t *restrict sem, const struct timespec *restrict abs_timeout) +{ + int ret = EOK; + time_t timeout = 0, now = 0, offs = 0; + + if (sem == NULL) { + return SET_ERRNO(-EINVAL); + } + + gettime(&now, &offs); + now += offs; + + timeout += abs_timeout->tv_sec * 1000000 + abs_timeout->tv_nsec / 1000; + timeout -= now; + + if (sem->type == smNamed) { + ret = _sem_down(sem, false, timeout); + } + else if (sem->type == smUnnamed) { + ret = semaphoreDown(&sem->unnamed, timeout); + if (ret == -ETIME) { + ret = -ETIMEDOUT; + } + } + else { + ret = -EINVAL; + } + + return SET_ERRNO(ret); +} + + +int sem_post(sem_t *sem) +{ + int ret = EOK; + msg_t msg = { 0 }; + ioctl_in_t *data; + + if (sem == NULL) { + return SET_ERRNO(-EINVAL); + } + + if (sem->type == smNamed) { + msg.type = mtDevCtl; + msg.oid = sem->named; + data = (ioctl_in_t *)msg.i.raw; + data->request = SEM_UP; + ret = msgSend(sem->named.port, &msg); + if (ret == EOK) { + ret = msg.o.err; + } + } + else if (sem->type == smUnnamed) { + ret = semaphoreUp(&sem->unnamed); + } + else { + ret = -EINVAL; + } + + return SET_ERRNO(ret); +} + + +int sem_getvalue(sem_t *restrict sem, int *restrict value) +{ + int ret = EOK; + msg_t msg = { 0 }; + + if (sem == NULL) { + return SET_ERRNO(-EINVAL); + } + + if (sem->type == smNamed) { + msg.type = mtGetAttr; + msg.oid = sem->named; + ret = msgSend(sem->named.port, &msg); + if (ret == EOK && value != NULL) { + *value = msg.o.attr.val; + } + } + else if (sem->type == smUnnamed) { + ret = semaphoreCount(&sem->unnamed); + if (ret > 0 && value != NULL) { + ret = EOK; + if (value != NULL) { + *value = ret; + } + } + } + else { + ret = -EINVAL; + } + + return SET_ERRNO(ret); +} + + +int sem_close(sem_t *sem) +{ + int ret = EOK; + msg_t msg = { 0 }; + + if (sem == NULL) { + return SET_ERRNO(-EINVAL); + } + + if (sem->type == smNamed) { + msg.type = mtClose; + msg.oid = sem->named; + ret = msgSend(sem->named.port, &msg); + } + else { + ret = -EINVAL; + } + + return SET_ERRNO(ret); +} + + +int sem_init(sem_t *sem, int pshared, unsigned int value) +{ + int ret = EOK; + sem_t new = { 0 }; + + if (pshared != 0) { + return SET_ERRNO(-ENOSYS); + } + + if (sem == NULL) { + return SET_ERRNO(-EINVAL); + } + + new.type = smUnnamed; + ret = semaphoreCreate(&new.unnamed, value); + *sem = new; + + return SET_ERRNO(ret); +} + + +int sem_destroy(sem_t *sem) +{ + int ret = EOK; + + if (sem == NULL || sem->type != smUnnamed) { + return SET_ERRNO(-EINVAL); + } + + ret = semaphoreDone(&sem->unnamed); + return SET_ERRNO(ret); +} + + +int sem_unlink(const char *name) +{ + int ret = EOK; + oid_t oid; + msg_t msg = { 0 }; + + ret = _sem_lookup(name, &oid); + if (ret == EOK) { + msg.type = mtDestroy; + msg.oid = oid; + ret = msgSend(oid.port, &msg); + } + + return SET_ERRNO(ret); +} diff --git a/sys/semaphore.c b/sys/semaphore.c index f2aa86d0..bdd2000c 100644 --- a/sys/semaphore.c +++ b/sys/semaphore.c @@ -5,7 +5,7 @@ * * Copyright 2012, 2017, 2018, 2026 Phoenix Systems * Copyright 2006 Pawel Pisarczyk - * Author: Pawel Pisarczyk, Aleksander Kaminski, Michał Lach + * Author: Pawel Pisarczyk, Aleksander Kaminski, Michal Lach * * This file is part of Phoenix-RTOS. * From 5085342dafb785a8628f06a0939edb858f2cdee3 Mon Sep 17 00:00:00 2001 From: Michal Lach Date: Fri, 19 Jun 2026 18:44:27 +0200 Subject: [PATCH 3/4] include/assert: add C++ compatible STATIC_ASSERT macro Up until C23, C used _Static_assert for compile time assertions. C23 changed it to static_assert, to match C++ keyword. Since we can include some of our headers in C++ source, this static assertions should be portable between C++ and C source. --- include/assert.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/include/assert.h b/include/assert.h index 9cb246d4..3c8583ac 100644 --- a/include/assert.h +++ b/include/assert.h @@ -25,6 +25,16 @@ extern "C" { #endif +/* + * We want to be able to include our headers in C++ source and it uses a different + * keyword for static assertions, the same goes for C from C23 onwards + */ +#if defined(__cplusplus) || __STDC_VERSION__ == 202311L +#define STATIC_ASSERT static_assert +#else +#define STATIC_ASSERT _Static_assert +#endif + #ifndef NDEBUG #define assert(__expr) \ From 67a05baddf49e8fb6b90e35bee6aa6d6e9421c14 Mon Sep 17 00:00:00 2001 From: Michal Lach Date: Fri, 19 Jun 2026 18:55:15 +0200 Subject: [PATCH 4/4] limits: add semaphore specific POSIX limits --- include/limits.h | 7 +++++-- include/semaphore.h | 2 +- include/sys/semaphore.h | 3 ++- include/sys/threads.h | 9 +++++++-- include/unistd.h | 14 ++++++++------ unistd/conf.c | 4 ++++ 6 files changed, 27 insertions(+), 12 deletions(-) diff --git a/include/limits.h b/include/limits.h index 6c420d15..f6877ee8 100644 --- a/include/limits.h +++ b/include/limits.h @@ -5,8 +5,8 @@ * * limits.h * - * Copyright 2019 Phoenix Systems - * Author: Andrzej Glowinski + * Copyright 2019, 2026 Phoenix Systems + * Author: Andrzej Glowinski, Michal Lach * * This file is part of Phoenix-RTOS. * @@ -36,4 +36,7 @@ #include +#define SEM_VALUE_MAX INT_MAX +#define SEM_NSEMS_MAX _POSIX_SEM_NSEMS_MAX + #endif diff --git a/include/semaphore.h b/include/semaphore.h index 332acccd..df53f690 100644 --- a/include/semaphore.h +++ b/include/semaphore.h @@ -6,7 +6,7 @@ * POSIX implementation - semaphores * * Copyright 2026 Phoenix Systems - * Author: Michał Lach + * Author: Michal Lach * * This file is part of Phoenix-RTOS. * diff --git a/include/sys/semaphore.h b/include/sys/semaphore.h index 7ce38e8f..1c7fa5e0 100644 --- a/include/sys/semaphore.h +++ b/include/sys/semaphore.h @@ -6,13 +6,14 @@ * POSIX implementation - semaphores * * Copyright 2026 Phoenix Systems - * Author: Michał Lach + * Author: Michal Lach * * This file is part of Phoenix-RTOS. * * %LICENSE% */ +#include #include #include diff --git a/include/sys/threads.h b/include/sys/threads.h index 3d6cf394..e8542274 100644 --- a/include/sys/threads.h +++ b/include/sys/threads.h @@ -5,8 +5,8 @@ * * sys/threads * - * Copyright 2017, 2018 Phoenix Systems - * Author: Pawel Pisarczyk, Aleksander Kaminski + * Copyright 2017, 2018, 2026 Phoenix Systems + * Author: Pawel Pisarczyk, Aleksander Kaminski, Michal Lach * * This file is part of Phoenix-RTOS. * @@ -16,6 +16,8 @@ #ifndef _LIBPHOENIX_SYS_THREADS_H_ #define _LIBPHOENIX_SYS_THREADS_H_ +#include +#include #include #include #include @@ -36,6 +38,9 @@ typedef struct { } semaphore_t; +STATIC_ASSERT(SEM_VALUE_MAX <= UINT_MAX, "The maximum semaphore value SEM_VALUE_MAX shall be less or equal to the maximum possible value of its container"); + + struct __errno_t { rbnode_t linkage; int tid; diff --git a/include/unistd.h b/include/unistd.h index 0912efeb..b3fe7cb7 100644 --- a/include/unistd.h +++ b/include/unistd.h @@ -44,12 +44,14 @@ extern "C" { #define X_OK (1 << 0) -#define _SC_OPEN_MAX 0 -#define _SC_IOV_MAX 1 -#define _SC_ATEXIT_MAX 2 -#define _SC_CLK_TCK 3 -#define _SC_PAGESIZE 4 -#define _SC_PAGE_SIZE _SC_PAGESIZE /* spec. 1170 compatibility */ +#define _SC_OPEN_MAX 0 +#define _SC_IOV_MAX 1 +#define _SC_ATEXIT_MAX 2 +#define _SC_CLK_TCK 3 +#define _SC_PAGESIZE 4 +#define _SC_SEM_VALUE_MAX 5 +#define _SC_SEM_NSEMS_MAX 6 +#define _SC_PAGE_SIZE _SC_PAGESIZE /* spec. 1170 compatibility */ #define _POSIX_NO_TRUNC 1 #define _POSIX_ASYNC_IO -1 /* Async IO not implemented. */ diff --git a/unistd/conf.c b/unistd/conf.c index 50e1b790..aeb0b815 100644 --- a/unistd/conf.c +++ b/unistd/conf.c @@ -35,6 +35,10 @@ long sysconf(int name) case _SC_PAGESIZE: /* _SC_PAGE_SIZE is synonym */ return _PAGE_SIZE; + case _SC_SEM_VALUE_MAX: + return SEM_VALUE_MAX; + case _SC_SEM_NSEMS_MAX: + return SEM_NSEMS_MAX; default: errno = EINVAL; return -1;