From bc005728ad7695f2ab76fde4ae1c3ac1a1ae34da Mon Sep 17 00:00:00 2001 From: ngqt Date: Mon, 1 Jun 2026 06:40:45 +0700 Subject: [PATCH 01/12] feat: add minimal implement of semaphore for tcc complication --- include/semaphore.h | 30 +++++++++++ src/semaphore.c | 118 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 148 insertions(+) create mode 100644 include/semaphore.h create mode 100644 src/semaphore.c diff --git a/include/semaphore.h b/include/semaphore.h new file mode 100644 index 0000000..163aca0 --- /dev/null +++ b/include/semaphore.h @@ -0,0 +1,30 @@ +#ifndef SEMAPHORE_H +#define SEMAPHORE_H + +#include "stdbool.h" +#include "stdint.h" + +typedef struct { + uint32_t value; + int32_t pshared; + uint32_t sem_init_magic; + uint32_t initial_value; + volatile char internal_spinlock; +} sem_t; + +#define SEM_FAILED ((sem_t *)0) +#define SEM_INIT_MAGIC 0xfef123 +#define SEM_VALUE_MAX 2147483647 + +int sem_close(sem_t *); +int sem_destroy(sem_t *); +// int sem_getvalue(sem_t *restrict, int); +int sem_init(sem_t *, int, unsigned); +// sem_t *sem_open(const char *, int, ...); +int sem_post(sem_t *); +// int sem_timedwait(sem_t *restrict, const struct timespec); +int sem_trywait(sem_t *); +// int sem_unlink(const char *); +int sem_wait(sem_t *); + +#endif \ No newline at end of file diff --git a/src/semaphore.c b/src/semaphore.c new file mode 100644 index 0000000..e2fdbf2 --- /dev/null +++ b/src/semaphore.c @@ -0,0 +1,118 @@ +#include "semaphore.h" +#include "errno.h" +#include "stdbool.h" + +static inline void cpu_yield(void) { + __asm__ __volatile__("pause" ::: "memory"); +} + +void internal_lock(sem_t *sem) { + // do a atomic set + while (__atomic_test_and_set(&sem->internal_spinlock, __ATOMIC_ACQUIRE)) { + cpu_yield(); + } + return; +} + +void internal_unlock(sem_t *sem) { + __atomic_clear(&sem->internal_spinlock, __ATOMIC_RELEASE); +} + +int sem_init(sem_t *sem, int pshared, unsigned int val) { + if (!sem) + return -1; + + // sem_t already init + if (sem->sem_init_magic == SEM_INIT_MAGIC) { + return -1; + } + + if (val > SEM_VALUE_MAX) { + errno = EINVAL; + return -1; + } + + sem->value = val; + sem->initial_value = val; + sem->pshared = pshared; + sem->sem_init_magic = SEM_INIT_MAGIC; + __atomic_clear(&sem->internal_spinlock, __ATOMIC_RELEASE); + + // I'll not implement this... + if (pshared == 1) + return -1; + + return 0; +} + +int sem_destroy(sem_t *sem) { + if (!sem) + return -1; + + // just set the init magic to zero + if (sem->sem_init_magic != SEM_INIT_MAGIC) + return -1; + + internal_lock(sem); + sem->sem_init_magic = 0; + sem->value = 0; + internal_unlock(sem); + + return 0; +} + +int sem_wait(sem_t *sem) { + if (!sem) + return -1; + + if (sem->sem_init_magic != SEM_INIT_MAGIC) + return -1; + + // wait for resource + while (true) { + internal_lock(sem); + if (sem->value != 0) { + sem->value--; + internal_unlock(sem); + return 0; + } + + internal_unlock(sem); + cpu_yield(); + } + + return 0; +} + +int sem_trywait(sem_t *sem) { + if (!sem) + return -1; + + if (sem->sem_init_magic != SEM_INIT_MAGIC) + return -1; + + internal_lock(sem); + if (sem->value == 0) { + errno = EAGAIN; + internal_unlock(sem); + return -1; + } + + sem->value--; + internal_unlock(sem); + return 0; +} + +int sem_post(sem_t *sem) { + if (!sem) + return -1; + + if (sem->sem_init_magic != SEM_INIT_MAGIC) + return -1; + + internal_lock(sem); + sem->value++; + internal_unlock(sem); + + return 0; +} From 1413fe4d7569a1da33a9e692597aaebc1cb1079d Mon Sep 17 00:00:00 2001 From: ngqt Date: Mon, 1 Jun 2026 10:38:06 +0700 Subject: [PATCH 02/12] fix: add static to prevent redeclaration symbol --- src/semaphore.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/semaphore.c b/src/semaphore.c index e2fdbf2..8f95cb3 100644 --- a/src/semaphore.c +++ b/src/semaphore.c @@ -6,7 +6,7 @@ static inline void cpu_yield(void) { __asm__ __volatile__("pause" ::: "memory"); } -void internal_lock(sem_t *sem) { +static void internal_lock(sem_t *sem) { // do a atomic set while (__atomic_test_and_set(&sem->internal_spinlock, __ATOMIC_ACQUIRE)) { cpu_yield(); @@ -14,7 +14,7 @@ void internal_lock(sem_t *sem) { return; } -void internal_unlock(sem_t *sem) { +static void internal_unlock(sem_t *sem) { __atomic_clear(&sem->internal_spinlock, __ATOMIC_RELEASE); } From 1c3d93f208e6e2dc3c5218ec055fd7d63a1a6b06 Mon Sep 17 00:00:00 2001 From: ngqt Date: Mon, 1 Jun 2026 22:28:47 +0700 Subject: [PATCH 03/12] feat: add EOVERFLOW to errno.h --- include/errno.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/errno.h b/include/errno.h index 396ca00..8d9e8ca 100644 --- a/include/errno.h +++ b/include/errno.h @@ -23,5 +23,6 @@ extern int errno; #define EBUSY 16 #define ESPIPE 29 #define ENOSYS 38 +#define EOVERFLOW 75 #endif From 5aee8854b9476c7aac1623be32154683ba08dc7f Mon Sep 17 00:00:00 2001 From: ngqt Date: Mon, 1 Jun 2026 22:29:59 +0700 Subject: [PATCH 04/12] feat: add int_max, uint_least, int_least, uint_fast, int_fast --- include/stdint.h | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/include/stdint.h b/include/stdint.h index d6b9125..5c85091 100644 --- a/include/stdint.h +++ b/include/stdint.h @@ -9,6 +9,36 @@ typedef unsigned int uint32_t; typedef long int64_t; typedef unsigned long uint64_t; +typedef __UINT_LEAST64_TYPE__ uint_least64_t; +typedef __INT_LEAST64_TYPE__ int_least64_t; +typedef __INT_FAST64_TYPE__ int_fast64_t; +typedef __UINT_FAST64_TYPE__ uint_fast64_t; +typedef __UINT_LEAST32_TYPE__ uint_least32_t; +typedef __INT_LEAST32_TYPE__ int_least32_t; +typedef __INT_FAST32_TYPE__ int_fast32_t; +typedef __UINT_FAST32_TYPE__ uint_fast32_t; +typedef __UINT_LEAST16_TYPE__ uint_least16_t; +typedef __INT_LEAST16_TYPE__ int_least16_t; +typedef __INT_FAST16_TYPE__ int_fast16_t; +typedef __UINT_FAST16_TYPE__ uint_fast16_t; +typedef __UINT_LEAST8_TYPE__ uint_least8_t; +typedef __INT_LEAST8_TYPE__ int_least8_t; +typedef __INT_FAST8_TYPE__ int_fast8_t; +typedef __UINT_FAST8_TYPE__ uint_fast8_t; + +#ifdef __INTMAX_TYPE__ +typedef __INTMAX_TYPE__ intmax_t; +typedef __UINTMAX_TYPE__ uintmax_t; +#else +#if defined(__x86_64__) || defined(__aarch64__) || defined(_M_X64) +typedef long long intmax_t; +typedef unsigned long long uintmax_t; +#else +typedef long intmax_t; +typedef unsigned long uintmax_t; +#endif +#endif + #ifndef _INTPTR_T #define _INTPTR_T typedef long intptr_t; From ca82433b01d4951966aea502d6f8d14d8275f5d2 Mon Sep 17 00:00:00 2001 From: ngqt Date: Mon, 1 Jun 2026 22:30:16 +0700 Subject: [PATCH 05/12] feat: add struct timespec --- include/time.h | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/include/time.h b/include/time.h index b3e7c3c..9dfd165 100644 --- a/include/time.h +++ b/include/time.h @@ -9,15 +9,20 @@ typedef unsigned long long clock_t; #define CLOCKS_PER_SEC 3000000000ULL struct tm { - int tm_sec; - int tm_min; - int tm_hour; - int tm_mday; - int tm_mon; - int tm_year; - int tm_wday; - int tm_yday; - int tm_isdst; + int tm_sec; + int tm_min; + int tm_hour; + int tm_mday; + int tm_mon; + int tm_year; + int tm_wday; + int tm_yday; + int tm_isdst; +}; + +struct timespec { + time_t tv_sec; + long tv_nsec; }; time_t time(time_t *out); From 24023d4d1a7ae2e9c542e5ce54af5782aa5d20a9 Mon Sep 17 00:00:00 2001 From: ngqt Date: Mon, 1 Jun 2026 22:34:57 +0700 Subject: [PATCH 06/12] chore: add errno on every failing return; let unsupported functions return ENOSYS; add more comment to explain the code --- include/semaphore.h | 21 +++++--- src/semaphore.c | 123 +++++++++++++++++++++++++++++++++++--------- 2 files changed, 113 insertions(+), 31 deletions(-) diff --git a/include/semaphore.h b/include/semaphore.h index 163aca0..4c56467 100644 --- a/include/semaphore.h +++ b/include/semaphore.h @@ -1,15 +1,22 @@ #ifndef SEMAPHORE_H #define SEMAPHORE_H -#include "stdbool.h" -#include "stdint.h" +#include +#include +#include +#include + +/* +* --- UNSUPPORTED POSIX SEMAPHORE FEATURES -- +* 1. Functions: sem_open(), sem_close(), sem_timedwait(), sem_unlink() +*/ typedef struct { uint32_t value; int32_t pshared; uint32_t sem_init_magic; uint32_t initial_value; - volatile char internal_spinlock; + atomic_flag internal_spinlock; } sem_t; #define SEM_FAILED ((sem_t *)0) @@ -18,13 +25,13 @@ typedef struct { int sem_close(sem_t *); int sem_destroy(sem_t *); -// int sem_getvalue(sem_t *restrict, int); +int sem_getvalue(sem_t *restrict, int *); int sem_init(sem_t *, int, unsigned); -// sem_t *sem_open(const char *, int, ...); +sem_t *sem_open(const char *, int, ...); int sem_post(sem_t *); -// int sem_timedwait(sem_t *restrict, const struct timespec); +int sem_timedwait(sem_t *restrict, const struct timespec); int sem_trywait(sem_t *); -// int sem_unlink(const char *); +int sem_unlink(const char *); int sem_wait(sem_t *); #endif \ No newline at end of file diff --git a/src/semaphore.c b/src/semaphore.c index 8f95cb3..0fd0513 100644 --- a/src/semaphore.c +++ b/src/semaphore.c @@ -1,29 +1,42 @@ -#include "semaphore.h" -#include "errno.h" -#include "stdbool.h" - -static inline void cpu_yield(void) { +#include +#include +#include +#include +#include + +static inline void do_a_yield(void) { +#if defined(__x86_64__) || defined(_M_X64) || defined(__i386__) || \ + defined(_M_IX86) __asm__ __volatile__("pause" ::: "memory"); +#elif defined(__aarch64__) || defined(_M_ARM64) + __asm__ __volatile__("yield" ::: "memory"); +#else + sleep(20); // just sleep a little bit on strange arch +#endif } static void internal_lock(sem_t *sem) { // do a atomic set - while (__atomic_test_and_set(&sem->internal_spinlock, __ATOMIC_ACQUIRE)) { - cpu_yield(); + while (atomic_flag_test_and_set_explicit(&sem->internal_spinlock, + memory_order_acquire)) { + do_a_yield(); } return; } static void internal_unlock(sem_t *sem) { - __atomic_clear(&sem->internal_spinlock, __ATOMIC_RELEASE); + atomic_flag_clear_explicit(&sem->internal_spinlock, memory_order_release); } int sem_init(sem_t *sem, int pshared, unsigned int val) { - if (!sem) + if (!sem) { + errno = EINVAL; return -1; + } // sem_t already init if (sem->sem_init_magic == SEM_INIT_MAGIC) { + errno = EBUSY; return -1; } @@ -35,24 +48,31 @@ int sem_init(sem_t *sem, int pshared, unsigned int val) { sem->value = val; sem->initial_value = val; sem->pshared = pshared; - sem->sem_init_magic = SEM_INIT_MAGIC; - __atomic_clear(&sem->internal_spinlock, __ATOMIC_RELEASE); + sem->sem_init_magic = SEM_INIT_MAGIC; // use this magic to avoid double init or doing some stuffs on a uninitialized semaphore + atomic_flag_clear_explicit(&sem->internal_spinlock, memory_order_release); // I'll not implement this... - if (pshared == 1) + if (pshared == 1) { + errno = ENOSYS; return -1; + } return 0; } int sem_destroy(sem_t *sem) { - if (!sem) + if (!sem) { + errno = EINVAL; return -1; + } // just set the init magic to zero - if (sem->sem_init_magic != SEM_INIT_MAGIC) + if (sem->sem_init_magic != SEM_INIT_MAGIC) { + errno = EINVAL; return -1; + } + // wait for permission to modify the semaphore internal_lock(sem); sem->sem_init_magic = 0; sem->value = 0; @@ -62,38 +82,47 @@ int sem_destroy(sem_t *sem) { } int sem_wait(sem_t *sem) { - if (!sem) + if (!sem) { + errno = EINVAL; return -1; + } - if (sem->sem_init_magic != SEM_INIT_MAGIC) + if (sem->sem_init_magic != SEM_INIT_MAGIC) { + errno = EINVAL; return -1; + } // wait for resource while (true) { internal_lock(sem); - if (sem->value != 0) { + if (sem->value != 0) { // find a free spot sem->value--; internal_unlock(sem); return 0; } internal_unlock(sem); - cpu_yield(); + do_a_yield(); // let cpu be relax a little bit } return 0; } int sem_trywait(sem_t *sem) { - if (!sem) + if (!sem) { + errno = EINVAL; return -1; + } - if (sem->sem_init_magic != SEM_INIT_MAGIC) + if (sem->sem_init_magic != SEM_INIT_MAGIC) { + errno = EINVAL; return -1; + } + // wait for permission to modify the semaphore internal_lock(sem); if (sem->value == 0) { - errno = EAGAIN; + errno = EAGAIN; // tell the caller that semaphore value is ran out, try again later internal_unlock(sem); return -1; } @@ -103,16 +132,62 @@ int sem_trywait(sem_t *sem) { return 0; } +// increment a semaphore int sem_post(sem_t *sem) { - if (!sem) + if (!sem) { + errno = EINVAL; return -1; + } - if (sem->sem_init_magic != SEM_INIT_MAGIC) + if (sem->sem_init_magic != SEM_INIT_MAGIC) { + errno = EINVAL; return -1; + } + // wait for permission to modify the semaphore internal_lock(sem); - sem->value++; + if (sem->value < SEM_VALUE_MAX) + sem->value++; + else { + errno = EOVERFLOW; + internal_unlock(sem); + return -1; + } internal_unlock(sem); return 0; } + +// get the sem_t's current value +int sem_getvalue(sem_t *restrict sem, int *v) { + if (!sem || !v || sem->sem_init_magic != SEM_INIT_MAGIC) { + errno = EINVAL; + return -1; + } + + *v = sem->value; + + return 0; +} + +// not supported functions + +sem_t *sem_open(const char *name, int oflag, ...) { + errno = ENOSYS; + return SEM_FAILED; +} + +int sem_timedwait(sem_t *restrict sem, const struct timespec ts) { + errno = ENOSYS; + return -1; +} + +int sem_unlink(const char * name) { + errno = ENOSYS; + return -1; +} + +int sem_close(sem_t * sem) { + errno = ENOSYS; + return -1; +} \ No newline at end of file From b80b7f6da1259001ad23dec7e32a18248e6f1177 Mon Sep 17 00:00:00 2001 From: ngqt Date: Tue, 2 Jun 2026 23:00:15 +0700 Subject: [PATCH 07/12] feat: reimplement using kernel futex instead of spinlock --- include/semaphore.h | 17 ++--- src/semaphore.c | 179 +++++++++++++++++++++----------------------- 2 files changed, 92 insertions(+), 104 deletions(-) diff --git a/include/semaphore.h b/include/semaphore.h index 4c56467..19408a9 100644 --- a/include/semaphore.h +++ b/include/semaphore.h @@ -7,20 +7,17 @@ #include /* -* --- UNSUPPORTED POSIX SEMAPHORE FEATURES -- -* 1. Functions: sem_open(), sem_close(), sem_timedwait(), sem_unlink() -*/ + * --- UNSUPPORTED POSIX SEMAPHORE FEATURES -- + * 1. Functions: sem_open(), sem_close(), sem_timedwait(), sem_unlink() + */ typedef struct { - uint32_t value; + atomic_uint_least32_t value; int32_t pshared; - uint32_t sem_init_magic; - uint32_t initial_value; - atomic_flag internal_spinlock; + atomic_uint_least32_t waiter; } sem_t; -#define SEM_FAILED ((sem_t *)0) -#define SEM_INIT_MAGIC 0xfef123 +#define SEM_FAILED ((sem_t *)-1) #define SEM_VALUE_MAX 2147483647 int sem_close(sem_t *); @@ -29,7 +26,7 @@ int sem_getvalue(sem_t *restrict, int *); int sem_init(sem_t *, int, unsigned); sem_t *sem_open(const char *, int, ...); int sem_post(sem_t *); -int sem_timedwait(sem_t *restrict, const struct timespec); +int sem_timedwait(sem_t *restrict, const struct timespec *restrict); int sem_trywait(sem_t *); int sem_unlink(const char *); int sem_wait(sem_t *); diff --git a/src/semaphore.c b/src/semaphore.c index 0fd0513..698ebc0 100644 --- a/src/semaphore.c +++ b/src/semaphore.c @@ -2,41 +2,25 @@ #include #include #include -#include - -static inline void do_a_yield(void) { -#if defined(__x86_64__) || defined(_M_X64) || defined(__i386__) || \ - defined(_M_IX86) - __asm__ __volatile__("pause" ::: "memory"); -#elif defined(__aarch64__) || defined(_M_ARM64) - __asm__ __volatile__("yield" ::: "memory"); -#else - sleep(20); // just sleep a little bit on strange arch -#endif -} +#include +#include -static void internal_lock(sem_t *sem) { - // do a atomic set - while (atomic_flag_test_and_set_explicit(&sem->internal_spinlock, - memory_order_acquire)) { - do_a_yield(); - } - return; -} - -static void internal_unlock(sem_t *sem) { - atomic_flag_clear_explicit(&sem->internal_spinlock, memory_order_release); -} +#ifdef __linux__ +#include +#include +#endif +// POSIX sempahore won't care if programmer make an double init or using after +// destroy, so I remove the SEM_INIT_MAGIC int sem_init(sem_t *sem, int pshared, unsigned int val) { if (!sem) { errno = EINVAL; return -1; } - // sem_t already init - if (sem->sem_init_magic == SEM_INIT_MAGIC) { - errno = EBUSY; + // I'll not implement this... + if (pshared == 1) { + errno = ENOSYS; return -1; } @@ -45,17 +29,9 @@ int sem_init(sem_t *sem, int pshared, unsigned int val) { return -1; } - sem->value = val; - sem->initial_value = val; + atomic_store(&sem->value, val); + sem->waiter = 0; sem->pshared = pshared; - sem->sem_init_magic = SEM_INIT_MAGIC; // use this magic to avoid double init or doing some stuffs on a uninitialized semaphore - atomic_flag_clear_explicit(&sem->internal_spinlock, memory_order_release); - - // I'll not implement this... - if (pshared == 1) { - errno = ENOSYS; - return -1; - } return 0; } @@ -66,17 +42,7 @@ int sem_destroy(sem_t *sem) { return -1; } - // just set the init magic to zero - if (sem->sem_init_magic != SEM_INIT_MAGIC) { - errno = EINVAL; - return -1; - } - - // wait for permission to modify the semaphore - internal_lock(sem); - sem->sem_init_magic = 0; - sem->value = 0; - internal_unlock(sem); + // POSIX implementation dont care if caller destroy a running semaphore return 0; } @@ -87,22 +53,45 @@ int sem_wait(sem_t *sem) { return -1; } - if (sem->sem_init_magic != SEM_INIT_MAGIC) { - errno = EINVAL; - return -1; - } - // wait for resource while (true) { - internal_lock(sem); - if (sem->value != 0) { // find a free spot - sem->value--; - internal_unlock(sem); - return 0; + uint32_t cur_sem_val = + atomic_load_explicit(&sem->value, memory_order_acquire); + + if (cur_sem_val > 0) { + if (atomic_compare_exchange_strong_explicit( + &sem->value, &cur_sem_val, cur_sem_val - 1, memory_order_acquire, + memory_order_relaxed)) // check if cur_sem_val is = sem->value and + // decrease it + + return 0; + else + continue; // if they aren't the same, someone stole it, just try get it + // again + } + + cur_sem_val = atomic_load_explicit(&sem->value, memory_order_acquire); + if (cur_sem_val > 0) { // double check to make sure that no sem_post() sneak + // in while registering + continue; } - internal_unlock(sem); - do_a_yield(); // let cpu be relax a little bit + // got some sleep + atomic_fetch_add_explicit(&sem->waiter, 1, memory_order_relaxed); + + int32_t result = sys_futex((uint32_t *)&sem->value, FUTEX_WAIT, 0); + if (result != 0) { + atomic_fetch_sub_explicit(&sem->waiter, 1, memory_order_relaxed); + if (errno == EINTR) { // got an interrupt from kernel, it could be Ctrl + + // C or somethings so just return + errno = EINTR; + return -1; + } + + continue; // if not EINTR, try again + } + + atomic_fetch_sub_explicit(&sem->waiter, 1, memory_order_relaxed); } return 0; @@ -114,58 +103,60 @@ int sem_trywait(sem_t *sem) { return -1; } - if (sem->sem_init_magic != SEM_INIT_MAGIC) { - errno = EINVAL; - return -1; - } + uint32_t cur_sem_val = + atomic_load_explicit(&sem->value, memory_order_acquire); + while (cur_sem_val > + 0) { // run a loop to make sure it won't give up when sem->value > 0 + if (atomic_compare_exchange_strong_explicit( + &sem->value, &cur_sem_val, cur_sem_val - 1, memory_order_acquire, + memory_order_relaxed)) { + return 0; + } - // wait for permission to modify the semaphore - internal_lock(sem); - if (sem->value == 0) { - errno = EAGAIN; // tell the caller that semaphore value is ran out, try again later - internal_unlock(sem); - return -1; + // atomic_compare_exchange_strong refreshed cur_sem_val for us } - sem->value--; - internal_unlock(sem); - return 0; + errno = EAGAIN; + return -1; } -// increment a semaphore int sem_post(sem_t *sem) { if (!sem) { errno = EINVAL; return -1; } - if (sem->sem_init_magic != SEM_INIT_MAGIC) { - errno = EINVAL; - return -1; - } + uint32_t cur_sem_val = + atomic_load_explicit(&sem->value, memory_order_relaxed); - // wait for permission to modify the semaphore - internal_lock(sem); - if (sem->value < SEM_VALUE_MAX) - sem->value++; - else { - errno = EOVERFLOW; - internal_unlock(sem); - return -1; + while (cur_sem_val < SEM_VALUE_MAX) { + if (atomic_compare_exchange_strong_explicit( + &sem->value, &cur_sem_val, cur_sem_val + 1, memory_order_release, + memory_order_relaxed)) { + + if (atomic_load_explicit(&sem->waiter, memory_order_relaxed) > + 0) // only wake when waiter > 0 + + sys_futex((uint32_t *)&sem->value, FUTEX_WAKE, 1); + + return 0; + } + + // cur_sem_val changed while we was comparing so try again } - internal_unlock(sem); - return 0; + errno = EOVERFLOW; + return -1; } // get the sem_t's current value int sem_getvalue(sem_t *restrict sem, int *v) { - if (!sem || !v || sem->sem_init_magic != SEM_INIT_MAGIC) { + if (!sem || !v) { errno = EINVAL; return -1; } - - *v = sem->value; + + *v = (int)atomic_load_explicit(&sem->value, memory_order_acquire); return 0; } @@ -177,17 +168,17 @@ sem_t *sem_open(const char *name, int oflag, ...) { return SEM_FAILED; } -int sem_timedwait(sem_t *restrict sem, const struct timespec ts) { +int sem_timedwait(sem_t *restrict sem, const struct timespec *ts) { errno = ENOSYS; return -1; } -int sem_unlink(const char * name) { +int sem_unlink(const char *name) { errno = ENOSYS; return -1; } -int sem_close(sem_t * sem) { +int sem_close(sem_t *sem) { errno = ENOSYS; return -1; } \ No newline at end of file From 542800a8e622b84b3b7880a6c884aa5b30ee03d9 Mon Sep 17 00:00:00 2001 From: ngqt Date: Tue, 2 Jun 2026 23:01:09 +0700 Subject: [PATCH 08/12] chore: remove temporary macro for testing --- src/semaphore.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/semaphore.c b/src/semaphore.c index 698ebc0..cd9d7f1 100644 --- a/src/semaphore.c +++ b/src/semaphore.c @@ -5,11 +5,6 @@ #include #include -#ifdef __linux__ -#include -#include -#endif - // POSIX sempahore won't care if programmer make an double init or using after // destroy, so I remove the SEM_INIT_MAGIC int sem_init(sem_t *sem, int pshared, unsigned int val) { From 7bc5ccba22ab06131a03ce41f28767f1f4e897f7 Mon Sep 17 00:00:00 2001 From: ngqt Date: Wed, 3 Jun 2026 06:56:32 +0700 Subject: [PATCH 09/12] chore: add copyright, fix some small bugs --- include/semaphore.h | 8 ++++++-- src/semaphore.c | 16 +++++++++------- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/include/semaphore.h b/include/semaphore.h index 19408a9..294a9a8 100644 --- a/include/semaphore.h +++ b/include/semaphore.h @@ -1,3 +1,7 @@ +// Copyright (c) 2026 Quoc Trung (https://github.com/thisisaname1928) +// This software is released under the GNU General Public License v3.0. See +// LICENSE file for details. This header needs to maintain in any file it is +// present in, as per the GPL license terms. #ifndef SEMAPHORE_H #define SEMAPHORE_H @@ -12,9 +16,9 @@ */ typedef struct { - atomic_uint_least32_t value; + _Atomic uint32_t value; int32_t pshared; - atomic_uint_least32_t waiter; + _Atomic uint32_t waiter; } sem_t; #define SEM_FAILED ((sem_t *)-1) diff --git a/src/semaphore.c b/src/semaphore.c index cd9d7f1..3d14f17 100644 --- a/src/semaphore.c +++ b/src/semaphore.c @@ -1,3 +1,7 @@ +// Copyright (c) 2026 Quoc Trung (https://github.com/thisisaname1928) +// This software is released under the GNU General Public License v3.0. See +// LICENSE file for details. This header needs to maintain in any file it is +// present in, as per the GPL license terms. #include #include #include @@ -74,16 +78,14 @@ int sem_wait(sem_t *sem) { // got some sleep atomic_fetch_add_explicit(&sem->waiter, 1, memory_order_relaxed); - int32_t result = sys_futex((uint32_t *)&sem->value, FUTEX_WAIT, 0); + int result = sys_futex((uint32_t *)&sem->value, FUTEX_WAIT, 0); if (result != 0) { atomic_fetch_sub_explicit(&sem->waiter, 1, memory_order_relaxed); - if (errno == EINTR) { // got an interrupt from kernel, it could be Ctrl + - // C or somethings so just return - errno = EINTR; - return -1; - } - continue; // if not EINTR, try again + if (errno == EAGAIN) // only retry if futex retrun EAGAIN + continue; + + return -1; // leave the same errno from kernel for caller } atomic_fetch_sub_explicit(&sem->waiter, 1, memory_order_relaxed); From 02c474bb6a6a2a6a093e0110e1e866e99d3005a3 Mon Sep 17 00:00:00 2001 From: ngqt Date: Fri, 5 Jun 2026 08:36:10 +0700 Subject: [PATCH 10/12] feat: add fully implementation of endian.h --- include/endian.h | 91 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 include/endian.h diff --git a/include/endian.h b/include/endian.h new file mode 100644 index 0000000..2867e4f --- /dev/null +++ b/include/endian.h @@ -0,0 +1,91 @@ +// Copyright (c) 2026 Quoc Trung (https://github.com/thisisaname1928) +// This software is released under the GNU General Public License v3.0. See +// LICENSE file for details. This header needs to maintain in any file it is +// present in, as per the GPL license terms. +#ifndef ENDIAN_H +#define ENDIAN_H + +#include + +// PDP_ENDIAN too old, no need to implement it +#define LITTLE_ENDIAN 1234 +#define BIG_ENDIAN 4321 + +#if defined(__BYTE_ORDER__) +#define __LIBC_BYTE_ORDER __BYTE_ORDER__ +#elif defined(__BYTE_ORDER) +#define __LIBC_BYTE_ORDER __BYTE_ORDER +#endif + +// manually detect byte order +#if !defined(__BYTE_ORDER__) && !defined(__BYTE_ORDER) +#if defined(__x86_64__) || defined(__i386__) || defined(_M_IX86) +#define __LIBC_BYTE_ORDER LITTLE_ENDIAN +#elif defined(__powerpc__) || defined(__m68k__) +#define __LIBC_BYTE_ORDER BIG_ENDIAN +#else +#error "BoredOS libc doesn't support this architecture!" +#endif +#endif + +#if defined(_MSC_VER) +#define __bswap16(x) _byteswap_ushort(x) +#define __bswap32(x) _byteswap_ulong(x) +#define __bswap64(x) _byteswap_uint64(x) +#elif defined(__GNUC__) || defined(__clang__) +#define __bswap16(x) __builtin_bswap16(x) +#define __bswap32(x) __builtin_bswap32(x) +#define __bswap64(x) __builtin_bswap64(x) +#else +#define __bswap16(x) (uint16_t)(((uint16_t)(x) << 8) | ((uint16_t)(x) >> 8)) +#define __bswap32(x) \ + (uint32_t)((((uint32_t)(x) >> 24) & 0xff) | \ + (((uint32_t)(x) >> 8) & 0xff00) | \ + (((uint32_t)(x) << 8) & 0xff0000) | \ + (((uint32_t)(x) << 24) & 0xff000000)) +#define __bswap64(x) \ + (uint64_t)((((uint64_t)(x) >> 56) & 0x00000000000000ffULL) | \ + (((uint64_t)(x) >> 40) & 0x000000000000ff00ULL) | \ + (((uint64_t)(x) >> 24) & 0x0000000000ff0000ULL) | \ + (((uint64_t)(x) >> 8) & 0x00000000ff000000ULL) | \ + (((uint64_t)(x) << 8) & 0x000000ff00000000ULL) | \ + (((uint64_t)(x) << 24) & 0x0000ff0000000000ULL) | \ + (((uint64_t)(x) << 40) & 0x00ff000000000000ULL) | \ + (((uint64_t)(x) << 56) & 0xff00000000000000ULL)) +#endif + +#if (__LIBC_BYTE_ORDER == LITTLE_ENDIAN) +#define be16toh(x) (__bswap16(x)) +#define be32toh(x) (__bswap32(x)) +#define be64toh(x) (__bswap64(x)) + +#define htobe16(x) (__bswap16(x)) +#define htobe32(x) (__bswap32(x)) +#define htobe64(x) (__bswap64(x)) + +#define htole16(x) ((uint16_t)(x)) +#define htole32(x) ((uint32_t)(x)) +#define htole64(x) ((uint64_t)(x)) + +#define le16toh(x) ((uint16_t)(x)) +#define le32toh(x) ((uint32_t)(x)) +#define le64toh(x) ((uint64_t)(x)) +#else +#define be16toh(x) ((uint16_t)(x)) +#define be32toh(x) ((uint32_t)(x)) +#define be64toh(x) ((uint64_t)(x)) + +#define htobe16(x) ((uint16_t)(x)) +#define htobe32(x) ((uint32_t)(x)) +#define htobe64(x) ((uint64_t)(x)) + +#define htole16(x) (__bswap16(x)) +#define htole32(x) (__bswap32(x)) +#define htole64(x) (__bswap64(x)) + +#define le16toh(x) (__bswap16(x)) +#define le32toh(x) (__bswap32(x)) +#define le64toh(x) (__bswap64(x)) +#endif + +#endif \ No newline at end of file From b900e20580ffa9e1fbd76245f579790480194054 Mon Sep 17 00:00:00 2001 From: ngqt Date: Sat, 6 Jun 2026 20:30:38 +0700 Subject: [PATCH 11/12] chore: rename LITTLE_ENDIAN and BIG_ENDIAN macros --- include/endian.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/include/endian.h b/include/endian.h index 2867e4f..40939ac 100644 --- a/include/endian.h +++ b/include/endian.h @@ -8,8 +8,8 @@ #include // PDP_ENDIAN too old, no need to implement it -#define LITTLE_ENDIAN 1234 -#define BIG_ENDIAN 4321 +#define __LITTLE_ENDIAN 1234 +#define __BIG_ENDIAN 4321 #if defined(__BYTE_ORDER__) #define __LIBC_BYTE_ORDER __BYTE_ORDER__ @@ -20,9 +20,9 @@ // manually detect byte order #if !defined(__BYTE_ORDER__) && !defined(__BYTE_ORDER) #if defined(__x86_64__) || defined(__i386__) || defined(_M_IX86) -#define __LIBC_BYTE_ORDER LITTLE_ENDIAN +#define __LIBC_BYTE_ORDER __LITTLE_ENDIAN #elif defined(__powerpc__) || defined(__m68k__) -#define __LIBC_BYTE_ORDER BIG_ENDIAN +#define __LIBC_BYTE_ORDER __BIG_ENDIAN #else #error "BoredOS libc doesn't support this architecture!" #endif @@ -54,7 +54,7 @@ (((uint64_t)(x) << 56) & 0xff00000000000000ULL)) #endif -#if (__LIBC_BYTE_ORDER == LITTLE_ENDIAN) +#if (__LIBC_BYTE_ORDER == __LITTLE_ENDIAN) #define be16toh(x) (__bswap16(x)) #define be32toh(x) (__bswap32(x)) #define be64toh(x) (__bswap64(x)) From 44c0489e7d1bbe5c085885f322c755f15d16192e Mon Sep 17 00:00:00 2001 From: ngqt Date: Sat, 6 Jun 2026 20:40:50 +0700 Subject: [PATCH 12/12] chore: fix my stupid renaming mistakes --- include/endian.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/include/endian.h b/include/endian.h index 40939ac..2867e4f 100644 --- a/include/endian.h +++ b/include/endian.h @@ -8,8 +8,8 @@ #include // PDP_ENDIAN too old, no need to implement it -#define __LITTLE_ENDIAN 1234 -#define __BIG_ENDIAN 4321 +#define LITTLE_ENDIAN 1234 +#define BIG_ENDIAN 4321 #if defined(__BYTE_ORDER__) #define __LIBC_BYTE_ORDER __BYTE_ORDER__ @@ -20,9 +20,9 @@ // manually detect byte order #if !defined(__BYTE_ORDER__) && !defined(__BYTE_ORDER) #if defined(__x86_64__) || defined(__i386__) || defined(_M_IX86) -#define __LIBC_BYTE_ORDER __LITTLE_ENDIAN +#define __LIBC_BYTE_ORDER LITTLE_ENDIAN #elif defined(__powerpc__) || defined(__m68k__) -#define __LIBC_BYTE_ORDER __BIG_ENDIAN +#define __LIBC_BYTE_ORDER BIG_ENDIAN #else #error "BoredOS libc doesn't support this architecture!" #endif @@ -54,7 +54,7 @@ (((uint64_t)(x) << 56) & 0xff00000000000000ULL)) #endif -#if (__LIBC_BYTE_ORDER == __LITTLE_ENDIAN) +#if (__LIBC_BYTE_ORDER == LITTLE_ENDIAN) #define be16toh(x) (__bswap16(x)) #define be32toh(x) (__bswap32(x)) #define be64toh(x) (__bswap64(x))