From 6d8adc9453117807412109dcab5db158cde6e18c Mon Sep 17 00:00:00 2001
From: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com>
Date: Tue, 30 May 2023 15:46:27 -0400
Subject: [PATCH] avmodel: avoid write-locking the mutex when deleting the
 renderer

During a resize event the the addRenderer function will regenerate a Renderer of the new size. The Renderer destructor will eventually trigger a write-lock in VideoProvider which may deadlock with frame updates to other renderers.

Gitlab: #1161
Change-Id: I3f3b99f6f78ac94218213130d42efcf0884b38fa
---
 src/libclient/avmodel.cpp | 24 +++++++++++-------------
 1 file changed, 11 insertions(+), 13 deletions(-)

diff --git a/src/libclient/avmodel.cpp b/src/libclient/avmodel.cpp
index b6712b73d..aa477673f 100644
--- a/src/libclient/avmodel.cpp
+++ b/src/libclient/avmodel.cpp
@@ -103,10 +103,12 @@ public:
     void addRenderer(const QString& id, const QSize& res, const QString& shmPath = {});
 
     /**
-     * Remove renderer from renderers_
+     * Remove renderer from renderers_. If the returned renderer is ignored, it
+     * will be deleted.
      * @param id
+     * @return the renderer
      */
-    void removeRenderer(const QString& id);
+    std::unique_ptr<Renderer> removeRenderer(const QString& id);
 
     bool hasRenderer(const QString& id);
     QSize getRendererSize(const QString& id);
@@ -947,7 +949,7 @@ void
 AVModelPimpl::addRenderer(const QString& id, const QSize& res, const QString& shmPath)
 {
     // Remove the renderer if it already exists.
-    removeRenderer(id); // Will write-lock renderersMutex_.
+    std::ignore = removeRenderer(id);
 
     {
         QWriteLocker lk(&renderersMutex_);
@@ -955,13 +957,7 @@ AVModelPimpl::addRenderer(const QString& id, const QSize& res, const QString& sh
     }
 
     QReadLocker lk(&renderersMutex_);
-    auto it = renderers_.find(id);
-    if (it == renderers_.end()) {
-        qWarning() << Q_FUNC_INFO << "Renderer not found for id:" << id;
-        return;
-    }
-
-    if (auto* renderer = it->second.get()) {
+    if (auto* renderer = renderers_.find(id)->second.get()) {
         connect(
             renderer,
             &Renderer::fpsChanged,
@@ -999,16 +995,18 @@ AVModelPimpl::addRenderer(const QString& id, const QSize& res, const QString& sh
     }
 }
 
-void
+std::unique_ptr<Renderer>
 AVModelPimpl::removeRenderer(const QString& id)
 {
     QWriteLocker lk(&renderersMutex_);
     auto it = renderers_.find(id);
     if (it == renderers_.end()) {
         qWarning() << "Cannot remove renderer. " << id << "not found";
-        return;
+        return {};
     }
-    renderers_.erase(id);
+    auto removed = std::move(it->second);
+    renderers_.erase(it);
+    return removed;
 }
 
 bool
-- 
GitLab