summaryrefslogtreecommitdiff
path: root/kit/shared_mutex.h
diff options
context:
space:
mode:
Diffstat (limited to 'kit/shared_mutex.h')
-rw-r--r--kit/shared_mutex.h176
1 files changed, 176 insertions, 0 deletions
diff --git a/kit/shared_mutex.h b/kit/shared_mutex.h
new file mode 100644
index 0000000..ad83418
--- /dev/null
+++ b/kit/shared_mutex.h
@@ -0,0 +1,176 @@
+// Shared mutex can be used in shared memory for interprocess
+// synchronization.
+//
+
+#ifndef KIT_SHARED_MUTEX_H
+#define KIT_SHARED_MUTEX_H
+
+#include "atomic.h"
+#include "threads.h"
+
+#include <assert.h>
+#include <string.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if defined(__GNUC__) || defined(__clang__)
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wunused-function"
+# pragma GCC diagnostic ignored "-Wunknown-pragmas"
+# pragma GCC push_options
+# pragma GCC optimize("O3")
+#endif
+
+enum {
+ KIT_SHARED_MUTEX_READY = 1,
+ KIT_SHARED_MUTEX_LOCKED,
+ KIT_SHARED_MUTEX_WRITER
+};
+
+typedef union {
+ struct {
+ i8 _Atomic state;
+ i32 readers;
+ };
+ struct {
+ u8 _pad[16];
+ };
+} kit_shared_mutex_t;
+
+static void kit_shared_mutex_init(kit_shared_mutex_t *m) {
+ assert(m != NULL);
+ memset(m, 0, sizeof *m);
+
+ atomic_store_explicit(&m->state, KIT_SHARED_MUTEX_READY,
+ memory_order_relaxed);
+}
+
+static b8 kit_shared_try_lock(kit_shared_mutex_t *m) {
+ assert(m != NULL);
+
+ for (;;) {
+ i8 prev_state = KIT_SHARED_MUTEX_READY;
+ if (atomic_compare_exchange_strong_explicit(
+ &m->state, &prev_state, KIT_SHARED_MUTEX_LOCKED,
+ memory_order_seq_cst, memory_order_seq_cst))
+ break;
+ if (prev_state == KIT_SHARED_MUTEX_WRITER)
+ return 0;
+ // FIXME
+ // Check performance
+ thrd_yield();
+ }
+
+ assert(m->readers >= 0);
+ ++m->readers;
+
+ i8 prev_state = KIT_SHARED_MUTEX_LOCKED;
+ if (!atomic_compare_exchange_strong_explicit(
+ &m->state, &prev_state, KIT_SHARED_MUTEX_READY,
+ memory_order_seq_cst, memory_order_seq_cst))
+ assert(0);
+
+ return 1;
+}
+
+static void kit_shared_lock(kit_shared_mutex_t *m) {
+ assert(m != NULL);
+
+ while (!kit_shared_try_lock(m))
+ // FIXME
+ // Check performance
+ thrd_yield();
+}
+
+static void kit_shared_unlock(kit_shared_mutex_t *m) {
+ assert(m != NULL);
+
+ for (;;) {
+ i8 prev_state = KIT_SHARED_MUTEX_READY;
+ if (atomic_compare_exchange_strong_explicit(
+ &m->state, &prev_state, KIT_SHARED_MUTEX_LOCKED,
+ memory_order_seq_cst, memory_order_seq_cst))
+ break;
+ // FIXME
+ // Check performance
+ thrd_yield();
+ }
+
+ assert(m->readers > 0);
+ m->readers--;
+
+ i8 prev_state = KIT_SHARED_MUTEX_LOCKED;
+ if (!atomic_compare_exchange_strong_explicit(
+ &m->state, &prev_state, KIT_SHARED_MUTEX_READY,
+ memory_order_seq_cst, memory_order_seq_cst))
+ assert(0);
+}
+
+static b8 kit_unique_try_lock(kit_shared_mutex_t *m) {
+ assert(m != NULL);
+
+ i8 prev_state = KIT_SHARED_MUTEX_READY;
+ if (!atomic_compare_exchange_strong_explicit(
+ &m->state, &prev_state, KIT_SHARED_MUTEX_WRITER,
+ memory_order_seq_cst, memory_order_seq_cst))
+ return 0;
+
+ i8 is_locked = (m->readers == 0);
+
+ prev_state = KIT_SHARED_MUTEX_WRITER;
+ if (!is_locked &&
+ !atomic_compare_exchange_strong_explicit(
+ &m->state, &prev_state, KIT_SHARED_MUTEX_READY,
+ memory_order_seq_cst, memory_order_seq_cst))
+ assert(0);
+
+ return is_locked;
+}
+
+static void kit_unique_lock(kit_shared_mutex_t *m) {
+ assert(m != NULL);
+
+ while (!kit_unique_try_lock(m))
+ // FIXME
+ // Check performance
+ thrd_yield();
+}
+
+static void kit_unique_unlock(kit_shared_mutex_t *m) {
+ assert(m != NULL);
+
+ i8 prev_state = KIT_SHARED_MUTEX_WRITER;
+ if (!atomic_compare_exchange_strong_explicit(
+ &m->state, &prev_state, KIT_SHARED_MUTEX_READY,
+ memory_order_seq_cst, memory_order_seq_cst))
+ assert(0);
+}
+
+#if defined(__GNUC__) || defined(__clang__)
+# pragma GCC pop_options
+# pragma GCC diagnostic pop
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#define shared_mutex_t kit_shared_mutex_t
+#define shared_mutex_init kit_shared_mutex_init
+#define shared_try_lock kit_shared_try_lock
+#define shared_lock kit_shared_lock
+#define shared_unlock kit_shared_unlock
+#define unique_try_lock kit_unique_try_lock
+#define unique_lock kit_unique_lock
+#define unique_unlock kit_unique_unlock
+#define shared_mutex_init kit_shared_mutex_init
+#define shared_try_lock kit_shared_try_lock
+#define shared_lock kit_shared_lock
+#define shared_unlock kit_shared_unlock
+#define unique_try_lock kit_unique_try_lock
+#define unique_lock kit_unique_lock
+#define unique_unlock kit_unique_unlock
+
+#endif