From e3d8fe4854a7ef7ff0637688cda9e65feb680122 Mon Sep 17 00:00:00 2001 From: Berke Güzel Date: Thu, 15 Oct 2026 12:37:10 +0300 Subject: 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 --- src/screen.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'src/screen.c') diff --git a/src/screen.c b/src/screen.c index 87d6f2c..82cd6d3 100644 --- a/src/screen.c +++ b/src/screen.c @@ -856,7 +856,10 @@ struct term *term_new(const struct term_options *opt) struct term *t = calloc(1, sizeof(*t)); if (!t) return NULL; - atomic_init(&t->seq, 0); + if (!term_mutex_init(&t->write_lock)) { + free(t); + return NULL; + } atomic_init(&t->writer_tid, 0); t->write_depth = 0; t->serial = atomic_fetch_add_explicit(&next_serial, 1, memory_order_relaxed); @@ -871,11 +874,13 @@ struct term *term_new(const struct term_options *opt) t->next_row_uid = 1; if (!screen_setup(&t->scr, cols, rows, &t->next_row_uid)) { + term_mutex_destroy(&t->write_lock); free(t); return NULL; } if (!term_hist_init(&t->hist, t->sb_capacity, 0)) { screen_free(&t->scr); + term_mutex_destroy(&t->write_lock); free(t); return NULL; } @@ -932,6 +937,7 @@ void term_free(struct term *t) free(t->tabs); term_osc_cleanup(t); term_gstore_free(t->gstore); + term_mutex_destroy(&t->write_lock); free(t); } -- cgit v1.2.3