// Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QTCONCURRENT_THREADENGINE_H #define QTCONCURRENT_THREADENGINE_H #include #if !defined(QT_NO_CONCURRENT) ||defined(Q_QDOC) #include #include #include #include #include #include #include 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(QThreadPool *pool); virtual ~ThreadEngineBase(); void startSingleThreaded(); void startThread(); bool isCanceled(); void waitForResume(); bool isProgressReportingEnabled(); void setProgressValue(int progress); void setProgressRange(int minimum, int maximum); void acquireBarrierSemaphore(); void reportIfSuspensionDone() const; protected: // The user overrides these: virtual void start() {} virtual void finish() {} virtual ThreadFunctionResult threadFunction() { return ThreadFinished; } virtual bool shouldStartThread() { return !shouldThrottleThread(); } virtual bool shouldThrottleThread() { return futureInterface ? (futureInterface->isSuspending() || futureInterface->isSuspended()) : 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; QBasicMutex mutex; }; template class ThreadEngine : public ThreadEngineBase { public: typedef T ResultType; ThreadEngine(QThreadPool *pool) : ThreadEngineBase(pool) {} virtual T *result() { return nullptr; } QFutureInterface *futureInterfaceTyped() { return static_cast *>(futureInterface); } // Runs the user algorithm using a single thread. T *startSingleThreaded() { ThreadEngineBase::startSingleThreaded(); return result(); } // Runs the user algorithm using multiple threads. // Does not block, returns a future. QFuture startAsynchronously() { futureInterface = new QFutureInterface(); // reportStart() must be called before starting threads, otherwise the // user algorithm might finish while reportStart() is running, which // is very bad. futureInterface->reportStarted(); QFuture future = QFuture(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 QList &_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 class ThreadEngineStarterBase { public: ThreadEngineStarterBase(ThreadEngine *_threadEngine) : threadEngine(_threadEngine) { } inline ThreadEngineStarterBase(const ThreadEngineStarterBase &other) : threadEngine(other.threadEngine) { } QFuture startAsynchronously() { return threadEngine->startAsynchronously(); } operator QFuture() { return startAsynchronously(); } protected: ThreadEngine *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 class ThreadEngineStarter : public ThreadEngineStarterBase { typedef ThreadEngineStarterBase Base; typedef ThreadEngine TypedThreadEngine; public: ThreadEngineStarter(TypedThreadEngine *eng) : Base(eng) { } }; // Full template specialization where T is void. template <> class ThreadEngineStarter : public ThreadEngineStarterBase { public: ThreadEngineStarter(ThreadEngine *_threadEngine) : ThreadEngineStarterBase(_threadEngine) {} }; //! [qtconcurrentthreadengine-1] template inline ThreadEngineStarter startThreadEngine(ThreadEngine *threadEngine) { return ThreadEngineStarter(threadEngine); } } // namespace QtConcurrent QT_END_NAMESPACE #endif // QT_NO_CONCURRENT #endif