Commit 3aa0ce9bc for llama.cpp
commit 3aa0ce9bca8a21cf3e367be389a2d9a2718c4667
Author: Max Krasnyansky <maxk@qti.qualcomm.com>
Date: Wed Sep 30 22:35:52 2026 -0700
hex-workqueue: fix race condition in seqn getting out of sync with idx_read/write (#29785)
diff --git a/ggml/src/ggml-hexagon/htp/work-queue.c b/ggml/src/ggml-hexagon/htp/work-queue.c
index bb73e205a..6c74445f7 100644
--- a/ggml/src/ggml-hexagon/htp/work-queue.c
+++ b/ggml/src/ggml-hexagon/htp/work-queue.c
@@ -31,7 +31,7 @@ struct work_queue_task_s {
struct work_queue_s {
atomic_uint seqn; // seqno used to detect new jobs
atomic_uint idx_read; // Updated by producer (pop/reclaim)
- unsigned int idx_write; // Updated by producer (push)
+ atomic_uint idx_write; // Updated by producer (push)
uint32_t idx_mask;
uint32_t capacity;
@@ -41,7 +41,7 @@ struct work_queue_s {
unsigned int n_threads; // total threads (workers + main)
unsigned int n_workers; // number of active threads (just workers)
- atomic_bool active; // workers are polling/active
+ atomic_uint active; // workers are polling/active
atomic_bool killed; // threads need to exit
bool external_mem; // memory owned externally
@@ -62,7 +62,7 @@ static void work_queue_thread(void * context) {
if (atomic_load_explicit(&q->active, memory_order_relaxed)) {
hex_pause();
} else {
- qurt_futex_wait(&q->seqn, prev_seqn);
+ qurt_futex_wait(&q->active, 0);
}
continue;
}
@@ -71,7 +71,7 @@ static void work_queue_thread(void * context) {
// Process all active tasks in the queue
unsigned int ir = atomic_load_explicit(&q->idx_read, memory_order_relaxed);
- unsigned int iw = q->idx_write;
+ unsigned int iw = atomic_load_explicit(&q->idx_write, memory_order_relaxed);
while (ir != iw) {
struct work_queue_task_s * task = &q->queue[ir];
@@ -101,8 +101,8 @@ bool work_queue_run_async(work_queue_t q, work_queue_func_t func, void * data, u
return false;
}
- unsigned int ir = atomic_load_explicit(&q->idx_read, memory_order_relaxed);
- unsigned int iw = q->idx_write;
+ unsigned int ir = atomic_load_explicit(&q->idx_read, memory_order_relaxed);
+ unsigned int iw = atomic_load_explicit(&q->idx_write, memory_order_relaxed);
if (((iw + 1) & q->idx_mask) == ir) {
FARF(ERROR, "work-queue-push: queue is full\n");
@@ -115,7 +115,7 @@ bool work_queue_run_async(work_queue_t q, work_queue_func_t func, void * data, u
task->n_threads = n;
atomic_store_explicit(&task->barrier, n, memory_order_relaxed);
- q->idx_write = (iw + 1) & q->idx_mask;
+ atomic_store_explicit(&q->idx_write, (iw + 1) & q->idx_mask, memory_order_relaxed);
// publish job to workers (already awake and polling)
atomic_fetch_add_explicit(&q->seqn, 1, memory_order_release);
@@ -168,10 +168,10 @@ work_queue_t work_queue_init(void * ptr, uint32_t n_threads, uint32_t capacity,
q->context[i].queue = q;
}
- atomic_init(&q->idx_read, 0);
- atomic_init(&q->seqn, 0);
- atomic_init(&q->active, false);
- q->idx_write = 0;
+ atomic_init(&q->idx_read, 0);
+ atomic_init(&q->idx_write, 0);
+ atomic_init(&q->seqn, 0);
+ atomic_init(&q->active, 0);
q->idx_mask = capacity - 1;
q->killed = 0;
for (int i = 0; i < (int) capacity; i++) {
@@ -219,8 +219,9 @@ void work_queue_free(work_queue_t q) {
if (!q) { return; }
atomic_store_explicit(&q->killed, 1, memory_order_relaxed);
+ atomic_store_explicit(&q->active, 1, memory_order_release);
atomic_fetch_add_explicit(&q->seqn, 1, memory_order_release);
- qurt_futex_wake(&q->seqn, q->n_workers);
+ qurt_futex_wake(&q->active, q->n_workers);
for (unsigned int i = 0; i < q->n_workers; i++) {
if (q->thread[i]) {
@@ -232,13 +233,11 @@ void work_queue_free(work_queue_t q) {
void work_queue_wakeup(work_queue_t q) {
if (!atomic_load_explicit(&q->active, memory_order_relaxed)) {
- atomic_store_explicit(&q->active, true, memory_order_release);
- // Increment seqn and wake workers to transition them out of sleep
- atomic_fetch_add_explicit(&q->seqn, 1, memory_order_release);
- qurt_futex_wake(&q->seqn, q->n_workers);
+ atomic_store_explicit(&q->active, 1, memory_order_release);
+ qurt_futex_wake(&q->active, q->n_workers);
}
}
void work_queue_suspend(work_queue_t q) {
- atomic_store_explicit(&q->active, false, memory_order_release);
+ atomic_store_explicit(&q->active, 0, memory_order_release);
}