use std::{ sync::{ Arc, atomic::{AtomicBool, Ordering}, }, thread, }; use tokio::{select, sync::mpsc}; use crate::emulator::{EmulatorClient, EmulatorCommand, ProfileEvent, SimId}; pub struct Profiler { sim_id: SimId, client: EmulatorClient, running: Arc, killer: Option>, } impl Profiler { pub fn new(sim_id: SimId, client: EmulatorClient) -> Self { Self { sim_id, client, running: Arc::new(AtomicBool::new(false)), killer: None, } } pub fn started(&self) -> bool { self.running.load(Ordering::Relaxed) } pub fn start(&mut self) { let sim_id = self.sim_id; let client = self.client.clone(); let running = self.running.clone(); let (tx, rx) = oneshot::channel(); self.killer = Some(tx); thread::spawn(move || { tokio::runtime::Builder::new_current_thread() .enable_all() .build() .unwrap() .block_on(async move { select! { _ = run_profile(sim_id, client, running.clone()) => {} _ = rx => { running.store(false, Ordering::Relaxed); } } }) }); } pub fn stop(&mut self) { if let Some(killer) = self.killer.take() { let _ = killer.send(()); } } } async fn run_profile(sim_id: SimId, client: EmulatorClient, running: Arc) { let (profile_sync, mut profile_source) = mpsc::unbounded_channel(); client.send_command(EmulatorCommand::StartProfiling(sim_id, profile_sync)); running.store(true, Ordering::Relaxed); while let Some(event) = profile_source.recv().await { match event { ProfileEvent::Start { file_path } => { println!("profiling {}", file_path.display()); } ProfileEvent::Update { cycles, event } => { println!("update {cycles} {event:#x?}"); } } } running.store(false, Ordering::Release); }