summaryrefslogtreecommitdiff
path: root/threads.c
diff options
context:
space:
mode:
authorCarson Fleming <[email protected]>2026-02-20 00:15:28 -0500
committerCarson Fleming <[email protected]>2026-02-20 00:15:28 -0500
commit3aa9f72a8b84eb9e8149e0edfb744c9acea303ad (patch)
tree6a386c927fc0c1a2a7639a99e44db215f32b282a /threads.c
parent9ef0abf7ab5266f6b58c0e55bbe9dd9038bc7f6e (diff)
downloadsafec-3aa9f72a8b84eb9e8149e0edfb744c9acea303ad.tar.gz
threads libary
Diffstat (limited to 'threads.c')
-rw-r--r--threads.c142
1 files changed, 142 insertions, 0 deletions
diff --git a/threads.c b/threads.c
new file mode 100644
index 0000000..383c8ab
--- /dev/null
+++ b/threads.c
@@ -0,0 +1,142 @@
+#include "threads.h"
+#include <time.h>
+#include <errno.h>
+#define sema_err (thrd_success - 1)
+#define NSEC_PER_SEC ((time_t)1000000000L)
+
+int sema_init(sema_t* sema, ssize_t value) {
+ int rv;
+
+ rv = mtx_init(&sema->__lock, mtx_plain);
+ if (rv != thrd_success) return rv;
+
+ rv = cnd_init(&sema->__cond);
+ if (rv != thrd_success) {
+ mtx_destroy(&sema->__lock);
+ return rv;
+ }
+
+ sema->__val = value;
+ return thrd_success;
+}
+
+int sema_get_value(sema_t* sema, ssize_t* p_value) {
+ int rv;
+
+ rv = mtx_lock(&sema->__lock);
+ if (rv != thrd_success) return rv;
+
+ *p_value = sema->__val;
+
+ return mtx_unlock(&sema->__lock);
+}
+
+int sema_up(sema_t* sema) {
+ return sema_up_many(sema, 1);
+}
+
+int sema_up_many(sema_t* sema, ssize_t n) {
+ int rv;
+
+ rv = mtx_lock(&sema->__lock);
+ if (rv != thrd_success) return rv;
+
+ sema->__val += n;
+
+ rv = mtx_unlock(&sema->__lock);
+ if (rv != thrd_success) return rv;
+
+ for (ssize_t i = 0; i < n; i++) {
+ rv = cnd_signal(&sema->__cond);
+ if (rv != thrd_success) return rv;
+ }
+ return thrd_success;
+}
+
+int sema_down(sema_t* sema) {
+ return sema_down_many(sema, 1);
+}
+
+int sema_down_many(sema_t* sema, ssize_t n) {
+ int rv;
+
+ rv = mtx_lock(&sema->__lock);
+ if (rv != thrd_success) return rv;
+
+ while (sema->__val < n) {
+ rv = cnd_wait(&sema->__cond, &sema->__lock);
+ if (rv != thrd_success) return rv;
+ }
+
+ sema->__val -= n;
+ return mtx_unlock(&sema->__lock);
+}
+
+int sema_try_down(sema_t* sema) {
+ return sema_try_down_many(sema, 1);
+}
+
+int sema_try_down_many(sema_t* sema, ssize_t n) {
+ int rv;
+
+ rv = mtx_lock(&sema->__lock);
+ if (rv != thrd_success) return rv;
+
+ if (sema->__val < n) {
+ rv = mtx_unlock(&sema->__lock);
+ if (rv != thrd_success) return rv;
+ errno = EAGAIN;
+ return sema_err;
+ }
+
+ sema->__val -= n;
+ return mtx_unlock(&sema->__lock);
+}
+
+int sema_down_timed(sema_t* sema, const struct timespec* time) {
+ return sema_down_many_timed(sema, time, 1);
+}
+
+static bool diff_timespec(
+ struct timespec* dst,
+ const struct timespec* src
+) {
+ *dst = (struct timespec) {
+ .tv_sec = dst->tv_sec - src->tv_sec,
+ .tv_nsec = dst->tv_nsec - src->tv_nsec
+ };
+ if (dst->tv_nsec < 0) {
+ dst->tv_sec--;
+ dst->tv_nsec += NSEC_PER_SEC;
+ }
+ return dst->tv_sec > 0 || (dst->tv_sec == 0 && dst->tv_nsec > 0);
+}
+
+int sema_down_many_timed(sema_t* sema, const struct timespec* time, ssize_t n) {
+ int rv;
+ struct timespec start, end, remaining = *time;
+
+ timespec_get(&start, TIME_UTC);
+ rv = mtx_timedlock(&sema->__lock, &remaining);
+ if (rv != thrd_success) return rv;
+ timespec_get(&end, TIME_UTC);
+ diff_timespec(&end, &start);
+ if (!diff_timespec(&remaining, &end)) return thrd_timedout;
+
+ while (sema->__val < n) {
+ timespec_get(&start, TIME_UTC);
+ rv = cnd_timedwait(&sema->__cond, &sema->__lock, &remaining);
+ if (rv != thrd_success) return rv;
+ timespec_get(&end, TIME_UTC);
+ diff_timespec(&end, &start);
+ if (!diff_timespec(&remaining, &end)) return thrd_timedout;
+ }
+
+ sema->__val -= n;
+ return mtx_unlock(&sema->__lock);
+}
+
+void sema_destroy(sema_t* sema) {
+ cnd_destroy(&sema->__cond);
+ mtx_destroy(&sema->__lock);
+}