From 54f29b951bd046705c68d936d8f2e3ce98d1b24d Mon Sep 17 00:00:00 2001
From: Miikka Heikkinen <miikka.heikkinen@theqtcompany.com>
Date: Tue, 6 Oct 2015 13:44:54 +0300
Subject: [PATCH] Fix deadlock when trying to schedule a sync job for hidden
 window
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Change-Id: I0685e6b7c1b0eb89ab5332db8746cf85639c9db4
Reviewed-by: Pasi Keränen <pasi.keranen@digia.com>
---
 src/imports/qtcanvas3d/context3d.cpp | 12 +++++++++---
 src/imports/qtcanvas3d/renderjob.cpp | 18 +++++++++++++++---
 src/imports/qtcanvas3d/renderjob_p.h |  6 +++++-
 3 files changed, 29 insertions(+), 7 deletions(-)

diff --git a/src/imports/qtcanvas3d/context3d.cpp b/src/imports/qtcanvas3d/context3d.cpp
index 1592dc3..829d5a9 100644
--- a/src/imports/qtcanvas3d/context3d.cpp
+++ b/src/imports/qtcanvas3d/context3d.cpp
@@ -5754,19 +5754,25 @@ void CanvasContext::scheduleSyncCommand(GlSyncCommand *command)
     if (m_canvas->window() && m_canvas->renderer()) {
         QOpenGLContext *ctx = m_canvas->window()->openglContext();
         if (ctx) {
+            bool jobDeleted = false;
             if (ctx->thread() != QThread::currentThread()) {
                 // In case of threaded renderer, we block the main thread until the job is done
                 CanvasRenderJob *syncJob = new CanvasRenderJob(command, &m_renderJobMutex,
                                                                &m_renderJobCondition,
-                                                               m_canvas->renderer());
+                                                               m_canvas->renderer(),
+                                                               &jobDeleted);
                 m_renderJobMutex.lock();
                 m_canvas->window()->scheduleRenderJob(syncJob, QQuickWindow::NoStage);
-                m_renderJobCondition.wait(&m_renderJobMutex);
+                // scheduleRenderJob will delete the job if the window is not exposed
+                if (!jobDeleted)
+                    m_renderJobCondition.wait(&m_renderJobMutex);
                 m_renderJobMutex.unlock();
+
             } else {
                 // In case of non-threaded renderer, scheduling a job executes it immediately,
                 // so we don't need synchronization.
-                CanvasRenderJob *syncJob = new CanvasRenderJob(command, 0, 0, m_canvas->renderer());
+                CanvasRenderJob *syncJob = new CanvasRenderJob(command, 0, 0, m_canvas->renderer(),
+                                                               &jobDeleted);
                 m_canvas->window()->scheduleRenderJob(syncJob, QQuickWindow::NoStage);
             }
         }
diff --git a/src/imports/qtcanvas3d/renderjob.cpp b/src/imports/qtcanvas3d/renderjob.cpp
index dfb891b..754aacb 100644
--- a/src/imports/qtcanvas3d/renderjob.cpp
+++ b/src/imports/qtcanvas3d/renderjob.cpp
@@ -49,14 +49,26 @@ QT_CANVAS3D_BEGIN_NAMESPACE
  */
 CanvasRenderJob::CanvasRenderJob(GlSyncCommand *command,
                                  QMutex *mutex, QWaitCondition *condition,
-                                 CanvasRenderer *renderer)
-    : m_command(command), m_mutex(mutex), m_condition(condition), m_renderer(renderer)
+                                 CanvasRenderer *renderer, bool *deleted)
+    : m_command(command),
+      m_mutex(mutex),
+      m_condition(condition),
+      m_renderer(renderer),
+      m_deleted(deleted),
+      m_creationThread(QThread::currentThread())
 {
 }
 
 CanvasRenderJob::~CanvasRenderJob()
 {
-    notifyGuiThread();
+    if (m_creationThread != QThread::currentThread()) {
+        notifyGuiThread();
+    } else {
+        // If the job is deleted in the thread it was created in, we can assume the scheduling
+        // failed and notify the context that we got deleted via the m_deleted flag,
+        // so it won't start waiting for job to complete.
+        *m_deleted = true;
+    }
 }
 
 void CanvasRenderJob::run()
diff --git a/src/imports/qtcanvas3d/renderjob_p.h b/src/imports/qtcanvas3d/renderjob_p.h
index 7d0e50b..e4c0ff1 100644
--- a/src/imports/qtcanvas3d/renderjob_p.h
+++ b/src/imports/qtcanvas3d/renderjob_p.h
@@ -53,6 +53,7 @@
 #include <QtCore/QWaitCondition>
 #include <QtCore/QMutex>
 #include <QtCore/QRunnable>
+#include <QtCore/QThread>
 
 QT_BEGIN_NAMESPACE
 QT_CANVAS3D_BEGIN_NAMESPACE
@@ -63,7 +64,8 @@ class CanvasRenderJob : public QRunnable
 {
 public:
     CanvasRenderJob(GlSyncCommand *command, QMutex *mutex,
-                    QWaitCondition *condition, QtCanvas3D::CanvasRenderer *renderer);
+                    QWaitCondition *condition, QtCanvas3D::CanvasRenderer *renderer,
+                    bool *deleted);
     ~CanvasRenderJob();
 
     void run();
@@ -76,6 +78,8 @@ private:
     QMutex *m_mutex;
     QWaitCondition *m_condition;
     CanvasRenderer *m_renderer;
+    bool *m_deleted;
+    QThread *m_creationThread;
 };
 
 QT_CANVAS3D_END_NAMESPACE
-- 
GitLab