| /**************************************************************************** |
| ** |
| ** Copyright (C) 2016 The Qt Company Ltd. |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** This file is part of the QtConcurrent module of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:LGPL$ |
| ** 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 Lesser General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU Lesser |
| ** General Public License version 3 as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
| ** packaging of this file. Please review the following information to |
| ** ensure the GNU Lesser General Public License version 3 requirements |
| ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU |
| ** General Public License version 2.0 or (at your option) the GNU General |
| ** Public license version 3 or 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.GPL2 and 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-2.0.html and |
| ** https://www.gnu.org/licenses/gpl-3.0.html. |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| #ifndef QTCONCURRENT_THREADENGINE_H |
| #define QTCONCURRENT_THREADENGINE_H |
| |
| #include <QtConcurrent/qtconcurrent_global.h> |
| |
| #if !defined(QT_NO_CONCURRENT) ||defined(Q_CLANG_QDOC) |
| |
| #include <QtCore/qthreadpool.h> |
| #include <QtCore/qfuture.h> |
| #include <QtCore/qdebug.h> |
| #include <QtCore/qexception.h> |
| #include <QtCore/qwaitcondition.h> |
| #include <QtCore/qatomic.h> |
| #include <QtCore/qsemaphore.h> |
| |
| QT_BEGIN_NAMESPACE |
| |
| |
| namespace QtConcurrent { |
| |
| // The ThreadEngineBarrier counts worker threads, and allows one |
| // thread to wait for all others to finish. Tested for its use in |
| // QtConcurrent, requires more testing for use as a general class. |
| class ThreadEngineBarrier |
| { |
| private: |
| // The thread count is maintained as an integer in the count atomic |
| // variable. The count can be either positive or negative - a negative |
| // count signals that a thread is waiting on the barrier. |
| |
| QAtomicInt count; |
| QSemaphore semaphore; |
| public: |
| ThreadEngineBarrier(); |
| void acquire(); |
| int release(); |
| void wait(); |
| int currentCount(); |
| bool releaseUnlessLast(); |
| }; |
| |
| enum ThreadFunctionResult { ThrottleThread, ThreadFinished }; |
| |
| // The ThreadEngine controls the threads used in the computation. |
| // Can be run in three modes: single threaded, multi-threaded blocking |
| // and multi-threaded asynchronous. |
| // The code for the single threaded mode is |
| class Q_CONCURRENT_EXPORT ThreadEngineBase: public QRunnable |
| { |
| public: |
| // Public API: |
| ThreadEngineBase(); |
| virtual ~ThreadEngineBase(); |
| void startSingleThreaded(); |
| void startBlocking(); |
| void startThread(); |
| bool isCanceled(); |
| void waitForResume(); |
| bool isProgressReportingEnabled(); |
| void setProgressValue(int progress); |
| void setProgressRange(int minimum, int maximum); |
| void acquireBarrierSemaphore(); |
| |
| protected: // The user overrides these: |
| virtual void start() {} |
| virtual void finish() {} |
| virtual ThreadFunctionResult threadFunction() { return ThreadFinished; } |
| virtual bool shouldStartThread() { return futureInterface ? !futureInterface->isPaused() : true; } |
| virtual bool shouldThrottleThread() { return futureInterface ? futureInterface->isPaused() : false; } |
| private: |
| bool startThreadInternal(); |
| void startThreads(); |
| void threadExit(); |
| bool threadThrottleExit(); |
| void run() override; |
| virtual void asynchronousFinish() = 0; |
| #ifndef QT_NO_EXCEPTIONS |
| void handleException(const QException &exception); |
| #endif |
| protected: |
| QFutureInterfaceBase *futureInterface; |
| QThreadPool *threadPool; |
| ThreadEngineBarrier barrier; |
| QtPrivate::ExceptionStore exceptionStore; |
| }; |
| |
| |
| template <typename T> |
| class ThreadEngine : public virtual ThreadEngineBase |
| { |
| public: |
| typedef T ResultType; |
| |
| virtual T *result() { return nullptr; } |
| |
| QFutureInterface<T> *futureInterfaceTyped() |
| { |
| return static_cast<QFutureInterface<T> *>(futureInterface); |
| } |
| |
| // Runs the user algorithm using a single thread. |
| T *startSingleThreaded() |
| { |
| ThreadEngineBase::startSingleThreaded(); |
| return result(); |
| } |
| |
| // Runs the user algorithm using multiple threads. |
| // This function blocks until the algorithm is finished, |
| // and then returns the result. |
| T *startBlocking() |
| { |
| ThreadEngineBase::startBlocking(); |
| return result(); |
| } |
| |
| // Runs the user algorithm using multiple threads. |
| // Does not block, returns a future. |
| QFuture<T> startAsynchronously() |
| { |
| futureInterface = new QFutureInterface<T>(); |
| |
| // reportStart() must be called before starting threads, otherwise the |
| // user algorithm might finish while reportStart() is running, which |
| // is very bad. |
| futureInterface->reportStarted(); |
| QFuture<T> future = QFuture<T>(futureInterfaceTyped()); |
| start(); |
| |
| acquireBarrierSemaphore(); |
| threadPool->start(this); |
| return future; |
| } |
| |
| void asynchronousFinish() override |
| { |
| finish(); |
| futureInterfaceTyped()->reportFinished(result()); |
| delete futureInterfaceTyped(); |
| delete this; |
| } |
| |
| |
| void reportResult(const T *_result, int index = -1) |
| { |
| if (futureInterface) |
| futureInterfaceTyped()->reportResult(_result, index); |
| } |
| |
| void reportResults(const QVector<T> &_result, int index = -1, int count = -1) |
| { |
| if (futureInterface) |
| futureInterfaceTyped()->reportResults(_result, index, count); |
| } |
| }; |
| |
| // The ThreadEngineStarter class ecapsulates the return type |
| // from the thread engine. |
| // Depending on how the it is used, it will run |
| // the engine in either blocking mode or asynchronous mode. |
| template <typename T> |
| class ThreadEngineStarterBase |
| { |
| public: |
| ThreadEngineStarterBase(ThreadEngine<T> *_threadEngine) |
| : threadEngine(_threadEngine) { } |
| |
| inline ThreadEngineStarterBase(const ThreadEngineStarterBase &other) |
| : threadEngine(other.threadEngine) { } |
| |
| QFuture<T> startAsynchronously() |
| { |
| return threadEngine->startAsynchronously(); |
| } |
| |
| operator QFuture<T>() |
| { |
| return startAsynchronously(); |
| } |
| |
| protected: |
| ThreadEngine<T> *threadEngine; |
| }; |
| |
| |
| // We need to factor out the code that dereferences the T pointer, |
| // with a specialization where T is void. (code that dereferences a void * |
| // won't compile) |
| template <typename T> |
| class ThreadEngineStarter : public ThreadEngineStarterBase<T> |
| { |
| typedef ThreadEngineStarterBase<T> Base; |
| typedef ThreadEngine<T> TypedThreadEngine; |
| public: |
| ThreadEngineStarter(TypedThreadEngine *eng) |
| : Base(eng) { } |
| |
| T startBlocking() |
| { |
| T t = *this->threadEngine->startBlocking(); |
| delete this->threadEngine; |
| return t; |
| } |
| }; |
| |
| // Full template specialization where T is void. |
| template <> |
| class ThreadEngineStarter<void> : public ThreadEngineStarterBase<void> |
| { |
| public: |
| ThreadEngineStarter<void>(ThreadEngine<void> *_threadEngine) |
| :ThreadEngineStarterBase<void>(_threadEngine) {} |
| |
| void startBlocking() |
| { |
| this->threadEngine->startBlocking(); |
| delete this->threadEngine; |
| } |
| }; |
| |
| //! [qtconcurrentthreadengine-1] |
| template <typename ThreadEngine> |
| inline ThreadEngineStarter<typename ThreadEngine::ResultType> startThreadEngine(ThreadEngine *threadEngine) |
| { |
| return ThreadEngineStarter<typename ThreadEngine::ResultType>(threadEngine); |
| } |
| |
| } // namespace QtConcurrent |
| |
| |
| QT_END_NAMESPACE |
| |
| #endif // QT_NO_CONCURRENT |
| |
| #endif |