Fix SWQOS worker pool busy-spin causing 300%+ CPU at idle

The 32 worker tasks in swqos_worker_loop use yield_now().await when
the queue is empty, which busy-spins across all tokio threads. Replace
with tokio::sync::Notify so workers sleep until jobs are enqueued.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
vibes
2026-03-13 00:37:14 +00:00
co-authored by Claude Opus 4.6
parent d7b0985844
commit 6607d276db
+11 -3
View File
@@ -16,6 +16,7 @@ use std::collections::HashMap;
use std::hash::BuildHasherDefault;
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use std::{str::FromStr, sync::Arc, time::Instant};
use tokio::sync::Notify;
use fnv::FnvHasher;
@@ -133,25 +134,27 @@ async fn run_one_swqos_job(job: SwqosJob) {
});
}
async fn swqos_worker_loop(queue: Arc<ArrayQueue<SwqosJob>>) {
async fn swqos_worker_loop(queue: Arc<ArrayQueue<SwqosJob>>, notify: Arc<Notify>) {
loop {
if let Some(job) = queue.pop() {
run_one_swqos_job(job).await;
} else {
tokio::task::yield_now().await;
notify.notified().await;
}
}
}
static SWQOS_QUEUE: OnceCell<Arc<ArrayQueue<SwqosJob>>> = OnceCell::new();
static SWQOS_NOTIFY: OnceCell<Arc<Notify>> = OnceCell::new();
static SWQOS_WORKERS_STARTED: AtomicBool = AtomicBool::new(false);
fn ensure_swqos_pool(queue: Arc<ArrayQueue<SwqosJob>>) {
if SWQOS_WORKERS_STARTED.swap(true, Ordering::AcqRel) {
return;
}
let notify = SWQOS_NOTIFY.get_or_init(|| Arc::new(Notify::new())).clone();
for _ in 0..SWQOS_POOL_WORKERS {
tokio::spawn(swqos_worker_loop(queue.clone()));
tokio::spawn(swqos_worker_loop(queue.clone(), notify.clone()));
}
}
@@ -477,6 +480,11 @@ pub async fn execute_parallel(
}
}
// Wake all workers to process enqueued jobs
if let Some(notify) = SWQOS_NOTIFY.get() {
notify.notify_waiters();
}
// All jobs enqueued (no spawn on hot path)
if !wait_transaction_confirmed {