| /**************************************************************************** | 
 | ** | 
 | ** Copyright (C) 2008-2012 NVIDIA Corporation. | 
 | ** Copyright (C) 2019 The Qt Company Ltd. | 
 | ** Contact: https://www.qt.io/licensing/ | 
 | ** | 
 | ** This file is part of Qt Quick 3D. | 
 | ** | 
 | ** $QT_BEGIN_LICENSE:GPL$ | 
 | ** Commercial License Usage | 
 | ** Licensees holding valid commercial Qt licenses may use this file in | 
 | ** accordance with the commercial license agreement provided with the | 
 | ** Software or, alternatively, in accordance with the terms contained in | 
 | ** a written agreement between you and The Qt Company. For licensing terms | 
 | ** and conditions see https://www.qt.io/terms-conditions. For further | 
 | ** information use the contact form at https://www.qt.io/contact-us. | 
 | ** | 
 | ** GNU General Public License Usage | 
 | ** Alternatively, this file may be used under the terms of the GNU | 
 | ** General Public License version 3 or (at your option) any later version | 
 | ** approved by the KDE Free Qt Foundation. The licenses are as published by | 
 | ** the Free Software Foundation and appearing in the file LICENSE.GPL3 | 
 | ** included in the packaging of this file. Please review the following | 
 | ** information to ensure the GNU General Public License requirements will | 
 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. | 
 | ** | 
 | ** $QT_END_LICENSE$ | 
 | ** | 
 | ****************************************************************************/ | 
 |  | 
 | #include "qssgrenderthreadpool_p.h" | 
 |  | 
 | #if QT_CONFIG(thread) | 
 | #include <QtCore/QThreadPool> | 
 | #endif // END THREAD | 
 | #include <QtCore/QRunnable> | 
 | #include <QtCore/QHash> | 
 | #include <QtCore/QMutex> | 
 | #include <QtCore/QMutexLocker> | 
 |  | 
 | QT_BEGIN_NAMESPACE | 
 |  | 
 | namespace { | 
 | #if QT_CONFIG(thread) | 
 | class QSSGThreadPool; | 
 | class QSSGTask : public QRunnable | 
 | { | 
 | public: | 
 |     QSSGTask(void *inUserData, QSSGTaskCallback inFunction, QSSGTaskCallback inCancelFunction, quint64 id, QSSGThreadPool *threadPool) | 
 |         : m_userData(inUserData) | 
 |         , m_function(inFunction) | 
 |         , m_cancelFunction(inCancelFunction) | 
 |         , m_id(id) | 
 |         , m_taskState(TaskStates::Queued) | 
 |         , m_threadPool(threadPool) | 
 |     { | 
 |         setAutoDelete(false); | 
 |     } | 
 |  | 
 |     TaskStates taskState() | 
 |     { | 
 |         QMutexLocker locker(&m_mutex); | 
 |         return m_taskState; | 
 |     } | 
 |  | 
 |     void run() override; | 
 |  | 
 |     void doFunction() | 
 |     { | 
 |         if (m_function) | 
 |             m_function(m_userData); | 
 |     } | 
 |  | 
 |     bool doCancel() | 
 |     { | 
 |         { | 
 |             QMutexLocker locker(&m_mutex); | 
 |             if (m_taskState == TaskStates::Running) | 
 |                 return false; | 
 |         } | 
 |  | 
 |         if (m_cancelFunction) | 
 |             m_cancelFunction(m_userData); | 
 |  | 
 |         return true; | 
 |     } | 
 |  | 
 | private: | 
 |     void *m_userData; | 
 |     QSSGTaskCallback m_function; | 
 |     QSSGTaskCallback m_cancelFunction; | 
 |     quint64 m_id; | 
 |     TaskStates m_taskState; | 
 |     QMutex m_mutex; | 
 |     QSSGThreadPool *m_threadPool; | 
 | }; | 
 |  | 
 | class QSSGThreadPool : public QSSGAbstractThreadPool | 
 | { | 
 | public: | 
 |     QSSGThreadPool(qint32 numThreads); | 
 |  | 
 |     ~QSSGThreadPool() override; | 
 |  | 
 |     quint64 addTask(void *inUserData, QSSGTaskCallback inFunction, QSSGTaskCallback inCancelFunction) override; | 
 |  | 
 |     TaskStates getTaskState(quint64 inTaskId) override; | 
 |  | 
 |     CancelReturnValues cancelTask(quint64 inTaskId) override; | 
 |  | 
 |     // Called from another thread! | 
 |     void taskFinished(quint64 inTaskId); | 
 |  | 
 | private: | 
 |     static quint64 generateTaskID() | 
 |     { | 
 |         static quint64 taskID = 0; | 
 |         return taskID++; | 
 |     } | 
 |  | 
 |     QThreadPool m_threadPool; | 
 |     QHash<quint64, QSSGTask *> m_taskMap; | 
 |     QMutex m_mutex; | 
 | }; | 
 |  | 
 | void QSSGTask::run() | 
 | { | 
 |     { | 
 |         QMutexLocker locker(&m_mutex); | 
 |         m_taskState = TaskStates::Running; | 
 |     } | 
 |  | 
 |     doFunction(); | 
 |  | 
 |     m_threadPool->taskFinished(m_id); | 
 | } | 
 |  | 
 | QSSGThreadPool::QSSGThreadPool(qint32 numThreads) | 
 | { | 
 |     m_threadPool.setMaxThreadCount(numThreads); | 
 | } | 
 |  | 
 | QSSGThreadPool::~QSSGThreadPool() | 
 | { | 
 |     QMutexLocker locker(&m_mutex); | 
 |     for (auto task : m_taskMap.values()) { | 
 |         if (m_threadPool.tryTake(task)) | 
 |             task->doCancel(); | 
 |         delete task; | 
 |     } | 
 | } | 
 |  | 
 | quint64 QSSGThreadPool::addTask(void *inUserData, QSSGTaskCallback inFunction, QSSGTaskCallback inCancelFunction) | 
 | { | 
 |     QMutexLocker locker(&m_mutex); | 
 |     const quint64 taskID = generateTaskID(); | 
 |     auto task = new QSSGTask(inUserData, inFunction, inCancelFunction, taskID, this); | 
 |     m_taskMap.insert(taskID, task); | 
 |     m_threadPool.start(task); | 
 |     return taskID; | 
 | } | 
 |  | 
 | TaskStates QSSGThreadPool::getTaskState(quint64 inTaskId) | 
 | { | 
 |     QMutexLocker locker(&m_mutex); | 
 |     auto task = m_taskMap.value(inTaskId, nullptr); | 
 |     if (!task) | 
 |         return TaskStates::UnknownTask; | 
 |     return task->taskState(); | 
 | } | 
 |  | 
 | CancelReturnValues QSSGThreadPool::cancelTask(quint64 inTaskId) | 
 | { | 
 |     QMutexLocker locker(&m_mutex); | 
 |     auto task = m_taskMap.value(inTaskId, nullptr); | 
 |     if (!task) | 
 |         return CancelReturnValues::TaskNotFound; | 
 |  | 
 |     if (m_threadPool.tryTake(task)) | 
 |         if (task->doCancel()) { | 
 |             auto task = m_taskMap.value(inTaskId); | 
 |             delete task; | 
 |             m_taskMap.remove(inTaskId); | 
 |             return CancelReturnValues::TaskCanceled; | 
 |         } | 
 |  | 
 |     return CancelReturnValues::TaskRunning; | 
 | } | 
 |  | 
 | void QSSGThreadPool::taskFinished(quint64 inTaskId) | 
 | { | 
 |     QMutexLocker locker(&m_mutex); | 
 |     auto task = m_taskMap.value(inTaskId); | 
 |     delete task; | 
 |     m_taskMap.remove(inTaskId); | 
 | } | 
 | #else | 
 | class QSSGThreadPool : public QSSGAbstractThreadPool | 
 | { | 
 | public: | 
 |     QSSGThreadPool(quint32) {} | 
 |  | 
 |     ~QSSGThreadPool() override {} | 
 |  | 
 |     quint64 addTask(void *inUserData, QSSGTaskCallback inFunction, QSSGTaskCallback inCancelFunction) override | 
 |     { | 
 |         Q_UNUSED(inCancelFunction); | 
 |         inFunction(inUserData); | 
 |         return 0; | 
 |     } | 
 |  | 
 |     TaskStates getTaskState(quint64) override { return TaskStates::UnknownTask; } | 
 |  | 
 |     CancelReturnValues cancelTask(quint64) override { return CancelReturnValues::TaskNotFound; } | 
 | }; | 
 | #endif // END NO THREAD | 
 | } | 
 |  | 
 |  | 
 | QSSGAbstractThreadPool::~QSSGAbstractThreadPool() = default; | 
 |  | 
 | QSSGRef<QSSGAbstractThreadPool> QSSGAbstractThreadPool::createThreadPool(qint32 inNumThreads) | 
 | { | 
 |     Q_ASSERT(inNumThreads > 0); | 
 |     return QSSGRef<QSSGAbstractThreadPool>(new QSSGThreadPool(inNumThreads)); | 
 | } | 
 |  | 
 | QT_END_NAMESPACE |