diff --git a/src/gui/console.rs b/src/gui/console.rs index 1521d7e..32dcb47 100644 --- a/src/gui/console.rs +++ b/src/gui/console.rs @@ -142,8 +142,8 @@ impl ConsoleApp { while self.running { let now = Instant::now(); - // Handle input - if let Ok(true) = event::poll(Duration::from_millis(10)) { + // Handle input with longer poll interval to reduce CPU usage + if let Ok(true) = event::poll(Duration::from_millis(50)) { if let Ok(Event::Key(key)) = event::read() { self.handle_key_event(key.code, key.modifiers); } @@ -158,20 +158,30 @@ impl ConsoleApp { last_draw = now; } - // Check if system should still be running - if let Ok(system) = self.system.lock() { - if !system.is_running() && !self.show_help { - // System stopped, show final state briefly - if let Err(e) = terminal.draw(|f| self.draw_ui(f)) { - eprintln!("DEBUG: Failed to draw final UI: {}", e); + // Check if system should still be running using try_lock to avoid blocking + match self.system.try_lock() { + Ok(system) => { + if !system.is_running() && !self.show_help { + // System stopped, show final state briefly + if let Err(e) = terminal.draw(|f| self.draw_ui(f)) { + eprintln!("DEBUG: Failed to draw final UI: {}", e); + } + thread::sleep(Duration::from_millis(500)); + break; } - thread::sleep(Duration::from_millis(500)); + } + Err(std::sync::TryLockError::WouldBlock) => { + // System is busy, continue running - this is expected + } + Err(std::sync::TryLockError::Poisoned(_)) => { + // System mutex is poisoned, exit gracefully + eprintln!("DEBUG: System mutex poisoned, exiting"); break; } } // Small delay to prevent busy waiting - thread::sleep(Duration::from_millis(1)); + thread::sleep(Duration::from_millis(10)); } // Restore terminal @@ -464,10 +474,11 @@ impl ConsoleApp { ]) .split(area); - // System information - let system_info = match self.system.lock() { - Ok(system) => match system.get_system_info() { - info => vec![ + // System information - use try_lock to avoid blocking + let system_info = match self.system.try_lock() { + Ok(system) => { + let info = system.get_system_info(); + vec![ Line::from(vec![Span::raw(format!("System: {}", info.name))]), Line::from(vec![Span::raw(format!( "Description: {}", @@ -487,8 +498,13 @@ impl ConsoleApp { info.ram_size ))]), Line::from(vec![Span::raw(format!("Running: {}", system.is_running()))]), - ], - }, + ] + } + Err(std::sync::TryLockError::WouldBlock) => { + vec![Line::from(vec![Span::raw( + "System busy (emulation running)...", + )])] + } Err(_) => { vec![Line::from(vec![Span::raw( "System information unavailable", @@ -526,7 +542,7 @@ impl ConsoleApp { fn get_cpu_register_info(&self) -> Vec> { let mut register_info = vec![Line::from(vec![Span::raw("CPU Registers:")])]; - match self.system.lock() { + match self.system.try_lock() { Ok(system) => { // Try to find Intel 4004 CPU and get register data let cpu_data = system.get_cpu_register_data(); @@ -567,6 +583,11 @@ impl ConsoleApp { register_info.push(Line::from(vec![Span::raw("No Intel 4004 CPU found")])); } } + Err(std::sync::TryLockError::WouldBlock) => { + register_info.push(Line::from(vec![Span::raw( + "CPU data temporarily unavailable (system busy)...", + )])); + } Err(_) => { register_info.push(Line::from(vec![Span::raw("Unable to access CPU data")])); } @@ -578,9 +599,9 @@ impl ConsoleApp { fn draw_ram_contents(&self, f: &mut Frame, area: Rect) { let mut ram_info = vec![Line::from(vec![Span::raw("RAM Contents:")])]; - // Try to get actual RAM data from the system + // Try to get actual RAM data from the system using try_lock if self.config.show_ram { - match self.system.lock() { + match self.system.try_lock() { Ok(system) => { let ram_data = system.get_ram_data(self.config.max_ram_rows); @@ -611,6 +632,11 @@ impl ConsoleApp { } } } + Err(std::sync::TryLockError::WouldBlock) => { + ram_info.push(Line::from(vec![Span::raw( + "RAM data temporarily unavailable (system busy)...", + )])); + } Err(_) => { ram_info.push(Line::from(vec![Span::raw("Unable to access RAM data")])); } diff --git a/src/main.rs b/src/main.rs index 5e17392..b281fbc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -206,15 +206,12 @@ fn main() { "Console features: Real-time monitoring, formatted output, non-blocking operation" ); - // Start the emulator system in a separate thread - // This allows the console interface to run while emulation continues - let system_runner = system_arc.clone(); - thread::spawn(move || { - if let Ok(mut system) = system_runner.lock() { - println!("DEBUG: Starting system for console mode"); - system.run(); - } - }); + // Start the emulator system using start() which doesn't block + // This allows the console interface to access the system while emulation runs + if let Ok(mut system) = system_arc.lock() { + println!("DEBUG: Starting system for console mode (non-blocking)"); + system.start(); + } // Launch console interface (blocks until console is closed) if let Err(e) = run_console(system_arc, console_config) { @@ -229,15 +226,12 @@ fn main() { println!("Starting graphical user interface..."); println!("GUI features: Visual monitoring, interactive controls, real-time updates"); - // Start the emulator system in a separate thread + // Start the emulator system using start() which doesn't block // This allows the GUI to remain responsive while emulation runs - let system_runner = system_arc.clone(); - thread::spawn(move || { - if let Ok(mut system) = system_runner.lock() { - println!("DEBUG: Starting system for GUI mode"); - system.run(); - } - }); + if let Ok(mut system) = system_arc.lock() { + println!("DEBUG: Starting system for GUI mode (non-blocking)"); + system.start(); + } // Launch GUI application (blocks until GUI window is closed) // The GUI will handle all user interactions and system monitoring diff --git a/src/system_config.rs b/src/system_config.rs index b132cae..9eefc3b 100644 --- a/src/system_config.rs +++ b/src/system_config.rs @@ -377,11 +377,15 @@ impl SystemFactory { } } +/// Thread handles for running components +type ComponentHandles = Vec<(String, std::thread::JoinHandle<()>)>; + /// A configurable system created from JSON configuration pub struct ConfigurableSystem { config: SystemConfig, components: HashMap>>>, - is_running: bool, + is_running: Arc>, + component_handles: Arc>>, } impl ConfigurableSystem { @@ -392,20 +396,35 @@ impl ConfigurableSystem { ConfigurableSystem { config, components, - is_running: false, + is_running: Arc::new(Mutex::new(false)), + component_handles: Arc::new(Mutex::new(None)), } } - pub fn run(&mut self) { - self.is_running = true; - let mut handles = vec![]; + /// Start the system without blocking. + /// + /// This method spawns component threads and returns immediately, + /// allowing the caller to retain access to the system via the mutex. + /// The system will continue running in the background until `stop()` is called. + pub fn start(&mut self) { + let mut is_running = self.is_running.lock().unwrap(); + if *is_running { + println!("System is already running"); + return; + } + *is_running = true; + drop(is_running); // Release lock before spawning threads println!("Starting configurable system: {}", self.config.name); println!("Description: {}", self.config.description); + let mut handles = vec![]; + let is_running_clone = Arc::clone(&self.is_running); + for (name, component) in &self.components { let comp_clone = Arc::clone(component); let name_clone = name.clone(); + let running_clone = Arc::clone(&is_running_clone); let handle = std::thread::spawn(move || { println!("DEBUG: Starting component thread: {}", name_clone); @@ -414,17 +433,46 @@ impl ConfigurableSystem { comp.run(); } println!("DEBUG: Component {} stopped", name_clone); + + // Signal that system should stop when any component stops + if let Ok(mut running) = running_clone.lock() { + *running = false; + } }); handles.push((name.clone(), handle)); } - println!("All components started. System running..."); + // Store handles for later cleanup + if let Ok(mut stored_handles) = self.component_handles.lock() { + *stored_handles = Some(handles); + } + println!("All components started. System running in background..."); + } + + /// Run the system (blocking version for backward compatibility). + /// + /// This method starts the system and then monitors it until stopped. + /// Note: This method holds the mutex lock for the entire duration, + /// which can cause issues with console/GUI access. Use `start()` instead + /// for non-blocking operation. + pub fn run(&mut self) { + self.start(); + // Monitor system state during execution self.monitor_system_state(); - self.is_running = false; + // Stop and cleanup + self.cleanup(); + } + + /// Clean up component threads and stop all components. + fn cleanup(&mut self) { + // Signal stop + if let Ok(mut is_running) = self.is_running.lock() { + *is_running = false; + } // Stop all components println!("\nStopping system components..."); @@ -436,10 +484,14 @@ impl ConfigurableSystem { } // Wait for threads - for (name, handle) in handles { - match handle.join() { - Ok(_) => println!("Component {} thread finished", name), - Err(_) => eprintln!("Component {} thread panicked", name), + if let Ok(mut handles_guard) = self.component_handles.lock() { + if let Some(handles) = handles_guard.take() { + for (name, handle) in handles { + match handle.join() { + Ok(_) => println!("Component {} thread finished", name), + Err(_) => eprintln!("Component {} thread panicked", name), + } + } } } @@ -459,7 +511,11 @@ impl ConfigurableSystem { std::thread::sleep(std::time::Duration::from_millis(500)); // Check if system should stop - if !self.is_running { + let should_continue = self.is_running.lock() + .map(|guard| *guard) + .unwrap_or(false); + + if !should_continue { println!("\nSystem stop requested - terminating monitoring"); break; } @@ -555,11 +611,17 @@ impl ConfigurableSystem { } pub fn stop(&mut self) { - self.is_running = false; + if let Ok(mut is_running) = self.is_running.lock() { + *is_running = false; + } + // Also cleanup threads + self.cleanup(); } pub fn is_running(&self) -> bool { - self.is_running + self.is_running.lock() + .map(|guard| *guard) + .unwrap_or(false) } /// Get access to components for monitoring purposes