use std::sync::LazyLock;
use std::sync::atomic::{AtomicBool, Ordering};
use tokio::sync::Notify;
use vtcode_core::utils::terminal_color_probe::probe_and_cache_terminal_palette_harmony;
static PROBE_NOTIFY: LazyLock<Notify> = LazyLock::new(Notify::new);
static PROBE_STARTED: AtomicBool = AtomicBool::new(false);
static PROBE_DONE: AtomicBool = AtomicBool::new(false);
pub(crate) fn start_terminal_palette_probe(handle: &tokio::runtime::Handle) {
if PROBE_STARTED.swap(true, Ordering::SeqCst) {
return;
}
handle.spawn_blocking(|| {
struct CompletionGuard;
impl Drop for CompletionGuard {
fn drop(&mut self) {
PROBE_DONE.store(true, Ordering::Release);
PROBE_NOTIFY.notify_one();
}
}
let _guard = CompletionGuard;
probe_and_cache_terminal_palette_harmony();
});
}
pub(crate) async fn await_terminal_palette_probe() {
if !PROBE_STARTED.load(Ordering::SeqCst) {
probe_and_cache_terminal_palette_harmony();
return;
}
if PROBE_DONE.load(Ordering::Acquire) {
return;
}
loop {
PROBE_NOTIFY.notified().await;
if PROBE_DONE.load(Ordering::Acquire) {
return;
}
}
}
#[cfg(test)]
mod tests {
use super::{PROBE_DONE, PROBE_STARTED, await_terminal_palette_probe, start_terminal_palette_probe};
fn reset_globals() {
PROBE_STARTED.store(false, std::sync::atomic::Ordering::SeqCst);
PROBE_DONE.store(false, std::sync::atomic::Ordering::SeqCst);
}
#[tokio::test]
async fn await_without_start_runs_probe_synchronously() {
reset_globals();
tokio::time::timeout(std::time::Duration::from_secs(5), await_terminal_palette_probe())
.await
.expect("fallback probe should not hang");
}
#[tokio::test]
async fn await_returns_after_start_completes() {
reset_globals();
start_terminal_palette_probe(&tokio::runtime::Handle::current());
tokio::time::timeout(std::time::Duration::from_secs(5), await_terminal_palette_probe())
.await
.expect("probe should complete and release the awaiter");
assert!(PROBE_DONE.load(std::sync::atomic::Ordering::Acquire));
}
}