fix: resolve mutex deadlock in console/GUI mode
This commit is contained in:
parent
9d328ee87b
commit
d1cc176cc7
3 changed files with 132 additions and 50 deletions
|
|
@ -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<String, Arc<Mutex<Box<dyn Component>>>>,
|
||||
is_running: bool,
|
||||
is_running: Arc<Mutex<bool>>,
|
||||
component_handles: Arc<Mutex<Option<ComponentHandles>>>,
|
||||
}
|
||||
|
||||
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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue