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,5 +1,7 @@
|
||||
//! Smart caching engine for predictive downloads
|
||||
|
||||
#[cfg(test)]
|
||||
use crate::utils::lock::MutexSafe;
|
||||
use log::{debug, info};
|
||||
use std::collections::HashMap;
|
||||
use std::sync::{Arc, Mutex};
|
||||
@@ -309,7 +311,7 @@ mod tests {
|
||||
|
||||
// Add some downloads
|
||||
{
|
||||
let conn_guard = conn_arc.lock().unwrap();
|
||||
let conn_guard = conn_arc.lock_safe();
|
||||
conn_guard.execute(
|
||||
"INSERT INTO downloads (user_id, status, file_size) VALUES ('user1', 'completed', 600)",
|
||||
[],
|
||||
|
||||
@@ -10,6 +10,7 @@ pub mod cache;
|
||||
pub mod events;
|
||||
pub mod worker;
|
||||
|
||||
use crate::utils::lock::MutexSafe;
|
||||
use std::path::PathBuf;
|
||||
use std::collections::HashSet;
|
||||
use std::sync::{Arc, Mutex};
|
||||
@@ -45,18 +46,18 @@ impl DownloadManager {
|
||||
|
||||
/// Check if a new download can be started based on concurrent limit
|
||||
pub fn can_start_download(&self) -> bool {
|
||||
let active = self.active_downloads.lock().unwrap();
|
||||
let active = self.active_downloads.lock_safe();
|
||||
active.len() < self.max_concurrent
|
||||
}
|
||||
|
||||
/// Get the number of currently active downloads
|
||||
pub fn active_count(&self) -> usize {
|
||||
self.active_downloads.lock().unwrap().len()
|
||||
self.active_downloads.lock_safe().len()
|
||||
}
|
||||
|
||||
/// Register a download as active
|
||||
pub fn register_download(&self, download_id: i64) -> bool {
|
||||
let mut active = self.active_downloads.lock().unwrap();
|
||||
let mut active = self.active_downloads.lock_safe();
|
||||
if active.len() >= self.max_concurrent {
|
||||
return false;
|
||||
}
|
||||
@@ -65,7 +66,7 @@ impl DownloadManager {
|
||||
|
||||
/// Unregister a download when it completes or fails
|
||||
pub fn unregister_download(&self, download_id: i64) {
|
||||
let mut active = self.active_downloads.lock().unwrap();
|
||||
let mut active = self.active_downloads.lock_safe();
|
||||
active.remove(&download_id);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user