From 14060e15e5144137b915a787659b28626f5539ad Mon Sep 17 00:00:00 2001 From: Jan-Peter von Hunnius Date: Fri, 27 Feb 2026 07:51:37 +0100 Subject: [PATCH] fix: use try_lock in data accessors to prevent component mutex deadlock --- src/system_config.rs | 121 +++++++++++++++++++++++++++---------------- 1 file changed, 77 insertions(+), 44 deletions(-) diff --git a/src/system_config.rs b/src/system_config.rs index 9eefc3b..47c150c 100644 --- a/src/system_config.rs +++ b/src/system_config.rs @@ -700,24 +700,35 @@ impl ConfigurableSystem { /// Get CPU register data from Intel 4004 CPU /// Returns: CpuRegisterData if Intel 4004 CPU is found, None otherwise + /// Uses try_lock() to avoid blocking when component is busy pub fn get_cpu_register_data(&self) -> Option { // Look for Intel 4004 CPU components for (_name, component) in &self.components { - if let Ok(comp) = component.lock() { - // Try to downcast to Intel4004 - if let Some(cpu) = comp.as_any().downcast_ref::() { - let mut index_registers = [0u8; 16]; - for i in 0..16 { - index_registers[i] = cpu.get_register(i as u8).unwrap_or(0); - } + match component.try_lock() { + Ok(comp) => { + // Try to downcast to Intel4004 + if let Some(cpu) = comp.as_any().downcast_ref::() { + let mut index_registers = [0u8; 16]; + for i in 0..16 { + index_registers[i] = cpu.get_register(i as u8).unwrap_or(0); + } - return Some(CpuRegisterData { - program_counter: cpu.get_program_counter(), - accumulator: cpu.get_accumulator(), - carry: cpu.get_carry(), - stack_pointer: cpu.get_stack_pointer(), - index_registers, - }); + return Some(CpuRegisterData { + program_counter: cpu.get_program_counter(), + accumulator: cpu.get_accumulator(), + carry: cpu.get_carry(), + stack_pointer: cpu.get_stack_pointer(), + index_registers, + }); + } + } + Err(std::sync::TryLockError::WouldBlock) => { + // Component is busy, continue to next component + continue; + } + Err(std::sync::TryLockError::Poisoned(_)) => { + // Handle poisoned mutex - continue to next component + continue; } } } @@ -727,33 +738,44 @@ impl ConfigurableSystem { /// Get RAM data from Intel 4002 RAM chips /// Parameters: max_rows - Maximum number of rows to return per bank /// Returns: Vector of (chip_name, banks) where each bank is a vector of nibbles + /// Uses try_lock() to avoid blocking when component is busy pub fn get_ram_data(&self, max_rows: usize) -> RamData { let mut ram_data = Vec::new(); for (name, component) in &self.components { - if let Ok(comp) = component.lock() { - // Try to downcast to Intel4002 - if let Some(ram) = comp.as_any().downcast_ref::() { - let mut banks = Vec::new(); + match component.try_lock() { + Ok(comp) => { + // Try to downcast to Intel4002 + if let Some(ram) = comp.as_any().downcast_ref::() { + let mut banks = Vec::new(); - // Intel 4002 has 4 banks of 16 4-bit words each (64 nibbles total per chip) - // But actually it has 4 main memory registers, each with 16 4-bit characters - // and 4 status characters = 4 x 20 = 80 nibbles - for bank in 0..4 { - let mut bank_data = Vec::new(); - // Read up to max_rows * 4 nibbles per bank - // The read_ram_direct takes a single address parameter - for addr in 0..(max_rows * 4).min(20) { - // Calculate the combined address for this bank and offset - let full_addr = (bank * 20 + addr) as u8; - // Use read_ram_direct to get the nibble at this address - let value = ram.read_ram_direct(full_addr).unwrap_or(0); - bank_data.push(value); + // Intel 4002 has 4 banks of 16 4-bit words each (64 nibbles total per chip) + // But actually it has 4 main memory registers, each with 16 4-bit characters + // and 4 status characters = 4 x 20 = 80 nibbles + for bank in 0..4 { + let mut bank_data = Vec::new(); + // Read up to max_rows * 4 nibbles per bank + // The read_ram_direct takes a single address parameter + for addr in 0..(max_rows * 4).min(20) { + // Calculate the combined address for this bank and offset + let full_addr = (bank * 20 + addr) as u8; + // Use read_ram_direct to get the nibble at this address + let value = ram.read_ram_direct(full_addr).unwrap_or(0); + bank_data.push(value); + } + banks.push(bank_data); } - banks.push(bank_data); - } - ram_data.push((name.clone(), banks)); + ram_data.push((name.clone(), banks)); + } + } + Err(std::sync::TryLockError::WouldBlock) => { + // Component is busy, skip to next component + continue; + } + Err(std::sync::TryLockError::Poisoned(_)) => { + // Handle poisoned mutex - skip to next component + continue; } } } @@ -764,22 +786,33 @@ impl ConfigurableSystem { /// Get ROM data from Intel 4001 ROM chips /// Parameters: max_rows - Maximum number of bytes to return per chip /// Returns: Vector of (chip_name, bytes) + /// Uses try_lock() to avoid blocking when component is busy pub fn get_rom_data(&self, max_rows: usize) -> RomData { let mut rom_data = Vec::new(); for (name, component) in &self.components { - if let Ok(comp) = component.lock() { - // Try to downcast to Intel4001 - if let Some(rom) = comp.as_any().downcast_ref::() { - let mut data = Vec::new(); + match component.try_lock() { + Ok(comp) => { + // Try to downcast to Intel4001 + if let Some(rom) = comp.as_any().downcast_ref::() { + let mut data = Vec::new(); - // Intel 4001 has 256 bytes of ROM - for addr in 0..max_rows.min(256) { - let value = rom.read_rom(addr as u8).unwrap_or(0); - data.push(value); + // Intel 4001 has 256 bytes of ROM + for addr in 0..max_rows.min(256) { + let value = rom.read_rom(addr as u8).unwrap_or(0); + data.push(value); + } + + rom_data.push((name.clone(), data)); } - - rom_data.push((name.clone(), data)); + } + Err(std::sync::TryLockError::WouldBlock) => { + // Component is busy, skip to next component + continue; + } + Err(std::sync::TryLockError::Poisoned(_)) => { + // Handle poisoned mutex - skip to next component + continue; } } }