From 30798435d185f73543a4439f4beed12ad141134d Mon Sep 17 00:00:00 2001
From: David Capello <david@igarastudio.com>
Date: Tue, 26 Mar 2019 15:19:52 -0300
Subject: [PATCH] Fix an edge case where m_remainingItems has elements but we
 don't have more workers to consume it

---
 src/app/thumbnail_generator.cpp | 49 +++++++++++++++++++++++----------
 src/app/thumbnail_generator.h   |  4 +++
 2 files changed, 38 insertions(+), 15 deletions(-)

diff --git a/src/app/thumbnail_generator.cpp b/src/app/thumbnail_generator.cpp
index da8cdc1ef..f41ab00a7 100644
--- a/src/app/thumbnail_generator.cpp
+++ b/src/app/thumbnail_generator.cpp
@@ -203,6 +203,13 @@ ThumbnailGenerator* ThumbnailGenerator::instance()
   return singleton;
 }
 
+ThumbnailGenerator::ThumbnailGenerator()
+{
+  int n = std::thread::hardware_concurrency()-1;
+  if (n < 1) n = 1;
+  m_maxWorkers = n;
+}
+
 bool ThumbnailGenerator::checkWorkers()
 {
   base::scoped_lock hold(m_workersAccess);
@@ -231,17 +238,31 @@ void ThumbnailGenerator::generateThumbnail(IFileItem* fileitem)
     return;
 
   if (fileitem->getThumbnailProgress() > 0.0) {
-    if (fileitem->getThumbnailProgress() < 0.0002) {
+    if (fileitem->getThumbnailProgress() == 0.00001) {
       m_remainingItems.prioritize(
         [fileitem](const Item& item) {
           return (item.fileitem == fileitem);
         });
+
+      // If there is no more workers running, we have to start a new
+      // one to process the m_remainingItems queue. How is it possible
+      // that a IFileItem has a thumbnail progress == 0.00001 but
+      // there is no workers?  This is an edge case where:
+      // 1. The Worker::loadBgThread() asks for the queue of remaining items
+      //    and it's empty, so the thread is going to be closed
+      // 2. We've just created a FOP for this IFileItem and ask for
+      //    available workers and we've already launch the max quantity
+      //    of possible workers (m_maxWorkers)
+      // 3. All worker threads are just closed so there is no more
+      //    worker for the remaining item in the queue.
+      if (m_workers.empty())
+        startWorker();
     }
     return;
   }
 
   // Set a starting progress so we don't enqueue the same item two times.
-  fileitem->setThumbnailProgress(0.0001);
+  fileitem->setThumbnailProgress(0.00001);
 
   THUMB_TRACE("Queue FOP thumbnail for %s\n",
               fileitem->fileName().c_str());
@@ -261,19 +282,7 @@ void ThumbnailGenerator::generateThumbnail(IFileItem* fileitem)
   m_remainingItems.push(Item(fileitem, fop.get()));
   fop.release();
 
-  int n = std::thread::hardware_concurrency()-1;
-  if (n < 1) n = 1;
-  if (m_workers.size() < n) {
-    Worker* worker = new Worker(m_remainingItems);
-    try {
-      base::scoped_lock hold(m_workersAccess);
-      m_workers.push_back(worker);
-    }
-    catch (...) {
-      delete worker;
-      throw;
-    }
-  }
+  startWorker();
 }
 
 void ThumbnailGenerator::stopAllWorkers()
@@ -296,4 +305,14 @@ void ThumbnailGenerator::stopAllWorkers()
     worker->stop();
 }
 
+void ThumbnailGenerator::startWorker()
+{
+  base::scoped_lock hold(m_workersAccess);
+  if (m_workers.size() < m_maxWorkers) {
+    std::unique_ptr<Worker> worker(new Worker(m_remainingItems));
+    m_workers.push_back(worker.get());
+    worker.release();
+  }
+}
+
 } // namespace app
diff --git a/src/app/thumbnail_generator.h b/src/app/thumbnail_generator.h
index 6622c44d0..cd6911dd5 100644
--- a/src/app/thumbnail_generator.h
+++ b/src/app/thumbnail_generator.h
@@ -24,6 +24,7 @@ namespace app {
   class IFileItem;
 
   class ThumbnailGenerator {
+    ThumbnailGenerator();
   public:
     static ThumbnailGenerator* instance();
 
@@ -43,6 +44,8 @@ namespace app {
     void stopAllWorkers();
 
   private:
+    void startWorker();
+
     class Worker;
     typedef std::vector<Worker*> WorkerList;
 
@@ -56,6 +59,7 @@ namespace app {
       }
     };
 
+    int m_maxWorkers;
     WorkerList m_workers;
     base::mutex m_workersAccess;
     std::unique_ptr<base::thread> m_stopThread;