summaryrefslogtreecommitdiff
path: root/src/compat.h
diff options
context:
space:
mode:
authorBerke Güzel <wenekar1@gmail.com>2026-10-15 12:37:10 +0300
committerBerke Güzel <wenekar1@gmail.com>2026-10-15 12:37:10 +0300
commite3d8fe4854a7ef7ff0637688cda9e65feb680122 (patch)
tree56bc226a49b7a706e078923434cb9fb05bab6e21 /src/compat.h
parent5f03033b0e211f9ed13e7a1351f9a8f9011ab665 (diff)
Replace the writer spin lock with a blocking mutex
term_write_begin spun on an atomic sequence word. A waiter burned CPU, and with no priority inheritance a SCHED_FIFO waiter could starve a preempted holder on the same CPU. The writer section is now a pthread mutex with PTHREAD_PRIO_INHERIT where the platform has it, and an SRWLOCK on Windows. Re-entry on the owning thread still only counts the depth. The platform wrapper (init, lock, unlock, destroy) is in compat.h. term_new initializes the mutex, and term_free destroys it. Meson links the thread library into the core, term_dep, and term_internal_dep. The README, term.h, and the amalgamation note say that POSIX needs -pthread. Tests: the feed and snapshot thread test now checks the final screen and history against a serial reference. A new test checks that term_lock nests and that another thread waits for the last term_unlock. Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Diffstat (limited to 'src/compat.h')
-rw-r--r--src/compat.h75
1 files changed, 75 insertions, 0 deletions
diff --git a/src/compat.h b/src/compat.h
index cb62679..6c3302f 100644
--- a/src/compat.h
+++ b/src/compat.h
@@ -14,6 +14,19 @@
#include <stdlib.h>
#include <string.h>
+#if defined(_WIN32)
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+#ifndef NOMINMAX
+#define NOMINMAX
+#endif
+#include <windows.h>
+#else
+#include <pthread.h>
+#include <unistd.h>
+#endif
+
#if defined(__GNUC__) || defined(__clang__)
#define TERM_COMPILER_GNU 1
#elif defined(_MSC_VER)
@@ -184,6 +197,68 @@ static inline void term_cpu_relax(void)
#endif
}
+/* The writer lock. POSIX uses a mutex with priority inheritance where the
+ * platform has it, so a waiter lifts the thread that holds the lock. Windows
+ * uses an SRWLOCK. Neither nests, so term_write_begin() counts re-entry. */
+#if defined(_WIN32)
+typedef SRWLOCK term_mutex;
+#else
+typedef pthread_mutex_t term_mutex;
+/* Strict C11 can hide PTHREAD_PRIO_INHERIT in pthread.h, as zig's aarch64
+ * glibc headers do. Every POSIX system that has the option uses 1 for it. */
+#if !defined(PTHREAD_PRIO_INHERIT)
+#define PTHREAD_PRIO_INHERIT 1
+#endif
+#endif
+
+/* Returns false when the lock cannot be made. */
+static inline bool term_mutex_init(term_mutex *m)
+{
+#if defined(_WIN32)
+ InitializeSRWLock(m);
+ return true;
+#elif defined(_POSIX_THREAD_PRIO_INHERIT) && _POSIX_THREAD_PRIO_INHERIT > 0
+ pthread_mutexattr_t attr;
+ if (pthread_mutexattr_init(&attr) != 0)
+ return false;
+ bool ok = pthread_mutexattr_setprotocol(&attr, PTHREAD_PRIO_INHERIT) == 0 &&
+ pthread_mutex_init(m, &attr) == 0;
+ pthread_mutexattr_destroy(&attr);
+ return ok;
+#else
+ return pthread_mutex_init(m, NULL) == 0;
+#endif
+}
+
+/* A normal mutex fails only when it is broken, so the result is ignored. */
+static inline void term_mutex_lock(term_mutex *m)
+{
+#if defined(_WIN32)
+ AcquireSRWLockExclusive(m);
+#else
+ (void)pthread_mutex_lock(m);
+#endif
+}
+
+static inline void term_mutex_unlock(term_mutex *m)
+{
+#if defined(_WIN32)
+ ReleaseSRWLockExclusive(m);
+#else
+ (void)pthread_mutex_unlock(m);
+#endif
+}
+
+/* An SRWLOCK has no destroy. */
+static inline void term_mutex_destroy(term_mutex *m)
+{
+#if defined(_WIN32)
+ (void)m;
+#else
+ (void)pthread_mutex_destroy(m);
+#endif
+}
+
/* strdup is POSIX, so the core copies strings itself. */
static inline char *term_strdup(const char *s)
{