mirror of
https://github.com/euzu/tuliprox.git
synced 2026-10-05 07:22:17 +02:00
Feature/provider linup manager deadlock fix (#545)
Identified a critical deadlock path in reconcile_connections where a DashMap iterator held internal shard locks while awaiting an asynchronous
This commit is contained in:
@@ -78,6 +78,7 @@
|
||||
- Added **TMDB** settings (Rate Limit, Cache Duration, Language) and **Metadata Formats** (NFO support) to Library configuration UI.
|
||||
|
||||
## 🚀 Performance & Stability
|
||||
- **Deadlock Resolution**: Fixed a potential deadlock in `ProviderLineupManager::reconcile_connections` by refactoring `DashMap` iterations to use snapshots, preventing internal shard locks from being held during async lock acquisition.
|
||||
- **Connection Reconciliation & GC**: Resolved a critical issue where provider connection counters could leak or become stale during hot reloads. Added automatic garbage collection for unused provider records to prevent logical memory buildup.
|
||||
- **Full Async Runtime**: Transitioned to `#[tokio::main]` and async I/O throughout the entire application.
|
||||
- **Non-Blocking Operations**: Cache persistence, playlist exports, and config saves moved to async tasks to prevent runtime stalls.
|
||||
|
||||
@@ -631,11 +631,15 @@ impl ProviderLineupManager {
|
||||
|
||||
pub async fn reconcile_connections(&self, mut counts: HashMap<Arc<str>, usize>) {
|
||||
// 1. Synchronize known providers from actual counts.
|
||||
// This avoids a transient "zero state" by updating each provider in one step.
|
||||
for entry in &self.provider_connections {
|
||||
let name = entry.key();
|
||||
let count = counts.remove(name).unwrap_or(0);
|
||||
let mut conn = entry.value().write().await;
|
||||
// We take a snapshot of the keys and locks to avoid holding the DashMap's internal
|
||||
// shard locks while awaiting the RwLock of each provider. Holding both can lead to deadlocks.
|
||||
let snapshot: Vec<_> = self.provider_connections.iter()
|
||||
.map(|e| (e.key().clone(), Arc::clone(e.value())))
|
||||
.collect();
|
||||
|
||||
for (name, conn_lock) in snapshot {
|
||||
let count = counts.remove(&name).unwrap_or(0);
|
||||
let mut conn = conn_lock.write().await;
|
||||
conn.current_connections = count;
|
||||
if count == 0 {
|
||||
conn.granted_grace = false;
|
||||
|
||||
Reference in New Issue
Block a user