Architecture remediation A/B/F: poison-tolerant locks, graceful backend init, doc fixes
Workstream A — poison-tolerant locking: - Add utils/lock.rs with MutexSafe/RwLockSafe extension traits that recover a poisoned std::sync lock instead of panicking, plus unit tests. - Replace all 153 .lock().unwrap() and 4 .read()/.write().unwrap() production sites with _safe variants across 14 files, eliminating the player crash-cascade class. Tokio async mutexes are unchanged. Workstream B — graceful backend init: - create_player_backend no longer panics when MPV/ExoPlayer fail to initialize; it falls back to NullBackend and emits a backend-init-failed event so the UI can show "playback unavailable" instead of the app crashing. Fatal DB-setup panics are kept. Workstream F — doc reconciliation: - Rewrite software-architecture.md's inaccurate "thin UI / ~800 lines" claims to reflect reality (~20.5k non-test frontend) and document the events+polling hybrid plus the new locking/backend-init behavior.
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
use crate::utils::lock::{MutexSafe, RwLockSafe};
|
||||
use log::{debug, error, info};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::sync::{
|
||||
@@ -43,13 +44,13 @@ impl PlaybackModeManager {
|
||||
|
||||
/// Get current playback mode
|
||||
pub fn get_mode(&self) -> PlaybackMode {
|
||||
self.current_mode.read().unwrap().clone()
|
||||
self.current_mode.read_safe().clone()
|
||||
}
|
||||
|
||||
/// Set playback mode (internal use)
|
||||
pub fn set_mode(&self, mode: PlaybackMode) {
|
||||
log::info!("[PlaybackMode] Setting mode to: {:?}", mode);
|
||||
let mut current = self.current_mode.write().unwrap();
|
||||
let mut current = self.current_mode.write_safe();
|
||||
*current = mode;
|
||||
}
|
||||
|
||||
@@ -193,7 +194,7 @@ impl PlaybackModeManager {
|
||||
debug!("[PlaybackMode] Player controller lock acquired");
|
||||
|
||||
let queue_arc = player.queue();
|
||||
let queue = queue_arc.lock().unwrap();
|
||||
let queue = queue_arc.lock_safe();
|
||||
let state = player.state();
|
||||
|
||||
let original_index = queue.current_index().unwrap_or(0);
|
||||
@@ -399,7 +400,7 @@ impl PlaybackModeManager {
|
||||
// Log queue state BEFORE stop
|
||||
{
|
||||
let queue_arc = player.queue();
|
||||
let queue = queue_arc.lock().unwrap();
|
||||
let queue = queue_arc.lock_safe();
|
||||
info!(
|
||||
"[PlaybackMode] BEFORE STOP: Queue has {} items, current_index={:?}",
|
||||
queue.items().len(),
|
||||
@@ -412,7 +413,7 @@ impl PlaybackModeManager {
|
||||
// Log queue state AFTER stop (should be unchanged)
|
||||
{
|
||||
let queue_arc = player.queue();
|
||||
let queue = queue_arc.lock().unwrap();
|
||||
let queue = queue_arc.lock_safe();
|
||||
info!(
|
||||
"[PlaybackMode] AFTER STOP: Queue has {} items, current_index={:?}",
|
||||
queue.items().len(),
|
||||
|
||||
Reference in New Issue
Block a user