summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--threads.c142
-rw-r--r--threads.h68
2 files changed, 210 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);
+}
diff --git a/threads.h b/threads.h
new file mode 100644
index 0000000..a0b6952
--- /dev/null
+++ b/threads.h
@@ -0,0 +1,68 @@
+#ifndef __SAFEC_THREADS_H
+#define __SAFEC_THREADS_H
+#include "types.h"
+#include <time.h>
+
+#ifdef __STDC_NO_THREADS__
+
+#include <pthread.h>
+
+typedef pthread_t thrd_t;
+typedef pthread_mutex_t mtx_t;
+typedef pthread_cond_t cnd_t;
+typedef void* thrd_ret_type;
+typedef thrd_ret_type (*thrd_start_t)(void*);
+
+#define thrd_success 0
+#define thrd_timedout ETIMEDOUT
+#define mtx_plain NULL
+
+#define mtx_lock(m) pthread_mutex_lock(m)
+#define mtx_timedlock(m, t) pthread_mutex_timedlock(m, t)
+#define mtx_trylock(m) pthread_mutex_trylock(m)
+#define mtx_unlock(m) pthread_mutex_unlock(m)
+#define mtx_init(m, t) pthread_mutex_init(m, t)
+#define mtx_destroy(m) pthread_mutex_destroy(m)
+
+#define cnd_init(c) pthread_cond_init(c, NULL)
+#define cnd_signal(c) pthread_cond_signal(c)
+#define cnd_broadcast(c) pthread_cond_broadcast(c)
+#define cnd_wait(c, m) pthread_cond_wait(c, m)
+#define cnd_timedwait(c, m, t) pthread_cond_timedwait(c, m, t)
+#define cnd_destroy(c) pthread_cond_destroy(c)
+
+#define thrd_create(t, w, a) pthread_create(t, NULL, w, a)
+#define thrd_equal(t1, t2) pthread_equal(t1, t2)
+#define thrd_current() pthread_self()
+#define thrd_yield() pthread_yield()
+#define thrd_exit(rv) pthread_exit(rv)
+#define thrd_detach(t) pthread_detach(t)
+#define thrd_join(t, rv) pthread_join(t, rv)
+
+#else
+
+#include <threads.h>
+typedef int thrd_ret_type;
+
+#endif
+
+typedef struct {
+ mtx_t __lock;
+ cnd_t __cond;
+ ssize_t __val;
+} sema_t;
+
+/* these return thrd_success on success and something else on failure */
+int sema_init(sema_t* sema, ssize_t value);
+int sema_get_value(sema_t* sema, ssize_t* p_value);
+int sema_up(sema_t* sema);
+int sema_up_many(sema_t* sema, ssize_t n);
+int sema_down(sema_t* sema);
+int sema_try_down(sema_t* sema);
+int sema_down_timed(sema_t* sema, const struct timespec* time);
+int sema_down_many(sema_t* sema, ssize_t n);
+int sema_try_down_many(sema_t* sema, ssize_t n);
+int sema_down_many_timed(sema_t* sema, const struct timespec* time, ssize_t n);
+void sema_destroy(sema_t* sema);
+
+#endif