summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-04-08 10:12:37 +0300
committerPaul Buetow <paul@buetow.org>2026-04-08 10:12:37 +0300
commit14995ca7a76e2a72a2169def7f55f3b9e2433ee2 (patch)
treea4009a0dcc0473ec7cabf41f56312d8e8eeb1cc9
parentd04a2b91fac52d5ff170b96f4f11846ddf7e01b8 (diff)
fix(gui): make GetActiveJobs concurrency-safe without channel access under lock
GetActiveJobs previously drained and refilled q.jobs while holding RLock, racing with AddWordWithPrompt and ProcessNextJob on the same channel. Derive queued jobs from q.results (StatusQueued) and processing jobs from q.processing, matching the authoritative state already updated under the mutex elsewhere. Made-with: Cursor
-rw-r--r--internal/gui/queue.go25
1 files changed, 8 insertions, 17 deletions
diff --git a/internal/gui/queue.go b/internal/gui/queue.go
index 1646708..37b4b72 100644
--- a/internal/gui/queue.go
+++ b/internal/gui/queue.go
@@ -153,33 +153,24 @@ func (q *WordQueue) GetQueueStatus() (queued, processing, completed, failed int)
return
}
-// GetActiveJobs returns all jobs that are currently queued or processing
+// GetActiveJobs returns all jobs that are currently queued or processing.
+// It uses only q.processing and q.results under the read lock; it does not
+// touch q.jobs, because channel operations must not run while holding the
+// mutex (other goroutines send/receive on q.jobs without the lock).
func (q *WordQueue) GetActiveJobs() []*WordJob {
q.mu.RLock()
defer q.mu.RUnlock()
var jobs []*WordJob
-
- // Add processing jobs
for _, job := range q.processing {
jobs = append(jobs, job)
}
-
- // Add queued jobs from channel (non-blocking)
- queuedJobs := make([]*WordJob, 0)
- for {
- select {
- case job := <-q.jobs:
- queuedJobs = append(queuedJobs, job)
- default:
- // Re-add jobs back to queue
- for _, job := range queuedJobs {
- q.jobs <- job
- }
- jobs = append(jobs, queuedJobs...)
- return jobs
+ for _, job := range q.results {
+ if job.Status == StatusQueued {
+ jobs = append(jobs, job)
}
}
+ return jobs
}
// GetCompletedJobs returns all completed jobs