use std::sync::atomic::Ordering;
use std::sync::Arc;
use std::time::{Duration, Instant};
use superfluid_daemon::{qos, Daemon, DaemonOptions, EngineHost, GenParams, MockCodec, SessionStore};
use superfluid_executor::fake::{FakeConfig, FakePrimitives};
use superfluid_executor::{Executor, ExecutorConfig};
fn daemon(max_lanes: usize) -> Arc<Daemon> {
static N: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
let dir = std::env::temp_dir().join(format!("superfluid-tick-yield-{}-{}", std::process::id(), N.fetch_add(1, Ordering::Relaxed)));
std::fs::create_dir_all(&dir).unwrap();
let store = SessionStore::open(&dir.join("wal.log")).unwrap();
let cfg = FakeConfig {
prefill_step_tokens: 512,
step_delay: Duration::from_millis(200),
max_seq_len: 16384,
..FakeConfig::default()
};
let host = EngineHost::spawn(move || (Executor::new(FakePrimitives::new(cfg), ExecutorConfig::default()), None)).expect("spawn");
let opts = DaemonOptions { max_lanes, prefill_budget: 4096, ..Default::default() };
Arc::new(Daemon::with_options(store, host, Box::new(MockCodec), opts).unwrap())
}
#[test]
fn a_cancel_ends_a_long_prefill_tick_within_a_step() {
let d = daemon(1);
let stats = d.sched_stats();
let s = d.create(None, GenParams::default()).unwrap();
d.set_qos(s, qos::INTERACTIVE_CHAT, false).unwrap();
d.append(s, None, (0..4000).map(|t| t % 900 + 50).collect()).unwrap();
let dd = Arc::clone(&d);
let running = std::thread::spawn(move || dd.generate(s, 4));
while stats.lanes_active.load(Ordering::Relaxed) < 1 {
assert!(!running.is_finished(), "never admitted: {:?}", running.join());
std::thread::sleep(Duration::from_millis(1));
}
std::thread::sleep(Duration::from_millis(450));
let t0 = Instant::now();
d.cancel_registry().cancel(s);
let _ = running.join().unwrap();
let took = t0.elapsed();
assert!(took < Duration::from_millis(800), "stopped within a step or two, not the rest of a 1.6 s tick: {took:?}");
}