text: /* $Id: UIThreadPool.cpp 71526 2018-03-27 16:01:42Z vboxsync $ */
  /** @file
   * VBox Qt GUI - UIThreadPool and UITask classes implementation.
   */
  
  /*
   * Copyright (C) 2013-2018 Oracle Corporation
   *
   * This file is part of VirtualBox Open Source Edition (OSE), as
   * available from http://www.virtualbox.org. This file is free software;
   * you can redistribute it and/or modify it under the terms of the GNU
   * General Public License (GPL) as published by the Free Software
   * Foundation, in version 2 as it comes in the "COPYING" file of the
   * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
   * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
   */
  
  #ifdef VBOX_WITH_PRECOMPILED_HEADERS
  # include <precomp.h>
  #else  /* !VBOX_WITH_PRECOMPILED_HEADERS */
  
  /* Qt includes: */
  # include <QThread>
  
  /* GUI includes: */
  # include "COMDefs.h"
  # include "UIDefs.h"
  # include "UIThreadPool.h"
  
  /* Other VBox includes: */
  #include <iprt/assert.h>
  
  #endif /* !VBOX_WITH_PRECOMPILED_HEADERS */
  
  
  /** QThread extension used as worker-thread.
    * Capable of executing COM-related tasks. */
  class UIThreadWorker : public QThread
  {
      Q_OBJECT;
  
  signals:
  
      /** Notifies listeners about @a pWorker finished. */
      void sigFinished(UIThreadWorker *pWorker);
  
  public:
  
      /** Constructs worker-thread for parent worker-thread @a pPool.
        * @param  iIndex  Brings worker-thread index within the worker-thread pool registry. */
      UIThreadWorker(UIThreadPool *pPool, int iIndex);
  
      /** Returns worker-thread index within the worker-thread pool registry. */
      int index() const { return m_iIndex; }
  
      /** Disables sigFinished signal, for optimizing worker-thread pool termination. */
      void setNoFinishedSignal() { m_fNoFinishedSignal = true; }
  
  private:
  
      /** Contains the worker-thread body. */
      void run();
  
      /** Holds the worker-thread pool reference. */
      UIThreadPool *m_pPool;
  
      /** Holds the worker-thread index within the worker-thread pool registry. */
      int m_iIndex;
  
      /** Holds whether sigFinished signal should be emitted or not. */
      bool m_fNoFinishedSignal;
  };
  
  
  /*********************************************************************************************************************************
  *   Class UIThreadPool implementation.                                                                                           *
  *********************************************************************************************************************************/
  
  UIThreadPool::UIThreadPool(ulong cMaxWorkers /* = 3 */, ulong cMsWorkerIdleTimeout /* = 5000 */)
      : m_cMsIdleTimeout(cMsWorkerIdleTimeout)
      , m_workers(cMaxWorkers)
      , m_cWorkers(0)
      , m_cIdleWorkers(0)
      , m_fTerminating(false)
  {
  }
  
  UIThreadPool::~UIThreadPool()
  {
      /* Set termination status: */
      setTerminating();
  
      /* Lock initially: */
      m_everythingLocker.lock();
  
      /* Cleanup all the workers: */
      for (int idxWorker = 0; idxWorker < m_workers.size(); ++idxWorker)
      {
          /* Acquire the worker: */
          UIThreadWorker *pWorker = m_workers.at(idxWorker);
          /* Remove it from the registry: */
          m_workers[idxWorker] = 0;
  
          /* Clean up the worker, if there was one: */
          if (pWorker)
          {
              /* Decrease the number of workers: */
              --m_cWorkers;
              /* Unlock temporary to let the worker finish: */
              m_everythingLocker.unlock();
              /* Wait for the worker to finish: */
              pWorker->wait();
              /* Lock again: */
              m_everythingLocker.lock();
              /* Delete the worker finally: */
              delete pWorker;
          }
      }
  
      /* Cleanup all the tasks: */
      qDeleteAll(m_pendingTasks);
      qDeleteAll(m_executingTasks);
      m_pendingTasks.clear();
      m_executingTasks.clear();
  
      /* Unlock finally: */
      m_everythingLocker.unlock();
  }
  
  bool UIThreadPool::isTerminating() const
  {
      /* Lock initially: */
      m_everythingLocker.lock();
  
      /* Acquire termination-flag: */
      bool fTerminating = m_fTerminating;
  
      /* Unlock finally: */
      m_everythingLocker.unlock();
  
      /* Return termination-flag: */
      return fTerminating;
  }
  
  void UIThreadPool::setTerminating()
  {
      /* Lock initially: */
      m_everythingLocker.lock();
  
      /* Assign termination-flag: */
      m_fTerminating = true;
  
      /* Tell all threads to NOT queue any termination signals: */
      for (int idxWorker = 0; idxWorker < m_workers.size(); ++idxWorker)
      {
          UIThreadWorker *pWorker = m_workers.at(idxWorker);
          if (pWorker)
              pWorker->setNoFinishedSignal();
      }
  
      /* Wake up all idle worker threads: */
      m_taskCondition.wakeAll();
  
      /* Unlock finally: */
      m_everythingLocker.unlock();
  }
  
  void UIThreadPool::enqueueTask(UITask *pTask)
  {
      /* Do nothing if terminating: */
      AssertReturnVoid(!isTerminating());
  
      /* Prepare task: */
      connect(pTask, &UITask::sigComplete,
              this, &UIThreadPool::sltHandleTaskComplete, Qt::QueuedConnection);
  
      /* Lock initially: */
      m_everythingLocker.lock();
  
      /* Put the task into the queue: */
      m_pendingTasks.enqueue(pTask);
  
      /* Wake up an idle worker if we got one: */
      if (m_cIdleWorkers > 0)
      {
          m_taskCondition.wakeOne();
      }
      /* No idle worker threads, should we create a new one? */
      else if (m_cWorkers < m_workers.size())
      {
          /* Find free slot: */
          int idxFirstUnused = m_workers.size();
          while (idxFirstUnused-- > 0)
              if (m_workers.at(idxFirstUnused) == 0)
              {
                  /* Prepare the new worker: */
                  UIThreadWorker *pWorker = new UIThreadWorker(this, idxFirstUnused);
                  connect(pWorker, &UIThreadWorker::sigFinished,
                          this, &UIThreadPool::sltHandleWorkerFinished, Qt::QueuedConnection);
                  m_workers[idxFirstUnused] = pWorker;
                  ++m_cWorkers;
  
                  /* And start it: */
                  pWorker->start();
                  break;
              }
      }
      /* else: wait for some worker to complete
       * whatever it's busy with and jump to it. */
  
      /* Unlock finally: */
      m_everythingLocker.unlock();
  }
  
  UITask *UIThreadPool::dequeueTask(UIThreadWorker *pWorker)
  {
      /* Lock initially: */
      m_everythingLocker.lock();
  
      /* Dequeue a task, watching out for terminations.
       * For optimal efficiency in enqueueTask() we keep count of idle threads.
       * If the wait times out, we'll return 0 and terminate the thread. */
      bool fIdleTimedOut = false;
      while (!m_fTerminating)
      {
          /* Make sure that worker has proper index: */
          Assert(m_workers.at(pWorker->index()) == pWorker);
  
          /* Dequeue task if there is one: */
          if (!m_pendingTasks.isEmpty())
          {
              UITask *pTask = m_pendingTasks.dequeue();
              if (pTask)
              {
                  /* Put into the set of executing tasks: */
                  m_executingTasks << pTask;
  
                  /* Unlock finally: */
                  m_everythingLocker.unlock();
  
                  /* Return dequeued task: */
                  return pTask;
              }
          }
  
          /* If we timed out already, then quit the worker thread. To prevent a
           * race between enqueueTask and the queue removal of the thread from
           * the workers vector, we remove it here already. (This does not apply
           * to the termination scenario.) */
          if (fIdleTimedOut)
          {
              m_workers[pWorker->index()] = 0;
              --m_cWorkers;
              break;
          }
  
          /* Wait for a task or timeout: */
          ++m_cIdleWorkers;
          fIdleTimedOut = !m_taskCondition.wait(&m_everythingLocker, m_cMsIdleTimeout);
          --m_cIdleWorkers;
      }
  
      /* Unlock finally: */
      m_everythingLocker.unlock();
  
      /* Return 0 by default: */
      return 0;
  }
  
  void UIThreadPool::sltHandleTaskComplete(UITask *pTask)
  {
      /* Skip on termination: */
      if (isTerminating())
          return;
  
      /* Notify listeners: */
      emit sigTaskComplete(pTask);
  
      /* Lock initially: */
      m_everythingLocker.lock();
  
      /* Delete task finally: */
      if (   !m_executingTasks.contains(pTask)
          || !m_executingTasks.remove(pTask))
          AssertMsgFailed(("Unable to find or remove complete task!"));
      delete pTask;
  
      /* Unlock finally: */
      m_everythingLocker.unlock();
  }
  
  void UIThreadPool::sltHandleWorkerFinished(UIThreadWorker *pWorker)
  {
      /* Wait for the thread to finish completely, then delete the thread
       * object. We have already removed the thread from the workers vector.
       * Note! We don't want to use 'this' here, in case it's invalid. */
      pWorker->wait();
      delete pWorker;
  }
  
  void UITask::start()
  {
      /* Run task: */
      run();
      /* Notify listeners: */
      emit sigComplete(this);
  }
  
  
  /*********************************************************************************************************************************
  *   Class UIThreadWorker implementation.                                                                                         *
  *********************************************************************************************************************************/
  
  UIThreadWorker::UIThreadWorker(UIThreadPool *pPool, int iIndex)
      : m_pPool(pPool)
      , m_iIndex(iIndex)
      , m_fNoFinishedSignal(false)
  {
  }
  
  void UIThreadWorker::run()
  {
      /* Initialize COM: */
      COMBase::InitializeCOM(false);
  
      /* Try get a task from the pool queue: */
      while (UITask *pTask = m_pPool->dequeueTask(this))
      {
          /* Process the task if we are not terminating.
           * Please take into account tasks are cleared by the UIThreadPool
           * after all listeners notified about task is complete and handled it. */
          if (!m_pPool->isTerminating())
              pTask->start();
      }
  
      /* Cleanup COM: */
      COMBase::CleanupCOM();
  
      /* Queue a signal for the pool to do thread cleanup, unless the pool is
         already terminating and doesn't need the signal. */
      if (!m_fNoFinishedSignal)
          emit sigFinished(this);
  }
  
  
  #include "UIThreadPool.moc"
  
  