123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288 | // 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#include"qtconcurrentthreadengine.h"#if !defined(QT_NO_CONCURRENT) || defined(Q_QDOC) QT_BEGIN_NAMESPACE namespace QtConcurrent {/*! \class QtConcurrent::ThreadEngineBarrier \inmodule QtConcurrent \internal*//*! \enum QtConcurrent::ThreadFunctionResult \internal*//*! \class QtConcurrent::ThreadEngineBase \inmodule QtConcurrent \internal*//*! \class QtConcurrent::ThreadEngine \inmodule QtConcurrent \internal*//*! \class QtConcurrent::ThreadEngineStarterBase \inmodule QtConcurrent \internal*//*! \class QtConcurrent::ThreadEngineStarter \inmodule QtConcurrent \internal*//*! \fn [qtconcurrentthreadengine-1] template <typename ThreadEngine> ThreadEngineStarter<typename ThreadEngine::ResultType> QtConcurrent::startThreadEngine(ThreadEngine *threadEngine) \internal*/ThreadEngineBarrier::ThreadEngineBarrier():count(0) { }voidThreadEngineBarrier::acquire(){ forever {int localCount = count.loadRelaxed();if(localCount <0) {if(count.testAndSetOrdered(localCount, localCount -1))return;}else{if(count.testAndSetOrdered(localCount, localCount +1))return;}qYieldCpu();}}intThreadEngineBarrier::release(){ forever {int localCount = count.loadRelaxed();if(localCount == -1) {if(count.testAndSetOrdered(-1,0)) { semaphore.release();return0;}}else if(localCount <0) {if(count.testAndSetOrdered(localCount, localCount +1))returnqAbs(localCount +1);}else{if(count.testAndSetOrdered(localCount, localCount -1))return localCount -1;}qYieldCpu();}}// Wait until all threads have been releasedvoidThreadEngineBarrier::wait(){ forever {int localCount = count.loadRelaxed();if(localCount ==0)return;Q_ASSERT(localCount >0);// multiple waiters are not allowed.if(count.testAndSetOrdered(localCount, -localCount)) { semaphore.acquire();return;}qYieldCpu();}}intThreadEngineBarrier::currentCount(){return count.loadRelaxed();}// releases a thread, unless this is the last thread.// returns true if the thread was released.boolThreadEngineBarrier::releaseUnlessLast(){ forever {int localCount = count.loadRelaxed();if(qAbs(localCount) ==1) {return false;}else if(localCount <0) {if(count.testAndSetOrdered(localCount, localCount +1))return true;}else{if(count.testAndSetOrdered(localCount, localCount -1))return true;}qYieldCpu();}}ThreadEngineBase::ThreadEngineBase(QThreadPool *pool):futureInterface(nullptr),threadPool(pool){setAutoDelete(false);}ThreadEngineBase::~ThreadEngineBase() {}voidThreadEngineBase::startSingleThreaded(){start();while(threadFunction() != ThreadFinished);finish();}voidThreadEngineBase::startThread(){startThreadInternal();}voidThreadEngineBase::acquireBarrierSemaphore(){ barrier.acquire();}voidThreadEngineBase::reportIfSuspensionDone()const{if(futureInterface && futureInterface->isSuspending()) futureInterface->reportSuspended();}boolThreadEngineBase::isCanceled(){if(futureInterface)return futureInterface->isCanceled();elsereturn false;}voidThreadEngineBase::waitForResume(){if(futureInterface) futureInterface->waitForResume();}boolThreadEngineBase::isProgressReportingEnabled(){// If we don't have a QFuture, there is no-one to report the progress to.return(futureInterface !=nullptr);}voidThreadEngineBase::setProgressValue(int progress){if(futureInterface) futureInterface->setProgressValue(progress);}voidThreadEngineBase::setProgressRange(int minimum,int maximum){if(futureInterface) futureInterface->setProgressRange(minimum, maximum);}boolThreadEngineBase::startThreadInternal(){if(this->isCanceled())return false; barrier.acquire();if(!threadPool->tryStart(this)) { barrier.release();return false;}return true;}voidThreadEngineBase::startThreads(){while(shouldStartThread() &&startThreadInternal());}voidThreadEngineBase::threadExit(){const bool asynchronous = (futureInterface !=nullptr);const int lastThread = (barrier.release() ==0);if(lastThread && asynchronous)this->asynchronousFinish();}// Called by a worker thread that wants to be throttled. If the current number// of running threads is larger than one the thread is allowed to exit and// this function returns one.boolThreadEngineBase::threadThrottleExit(){return barrier.releaseUnlessLast();}voidThreadEngineBase::run()// implements QRunnable.{if(this->isCanceled()) {threadExit();return;}startThreads();#ifndef QT_NO_EXCEPTIONStry{#endifwhile(threadFunction() == ThrottleThread) {// threadFunction returning ThrottleThread means it that the user// struct wants to be throttled by making a worker thread exit.// Respect that request unless this is the only worker thread left// running, in which case it has to keep going.if(threadThrottleExit()) {return;}else{// If the last worker thread is throttled and the state is "suspending",// it means that suspension has been requested, and it is already// in effect (because all previous threads have already exited).// Report the "Suspended" state.reportIfSuspensionDone();}}#ifndef QT_NO_EXCEPTIONS}catch(QException &e) {handleException(e);}catch(...) {handleException(QUnhandledException(std::current_exception()));}#endifthreadExit();}#ifndef QT_NO_EXCEPTIONSvoidThreadEngineBase::handleException(const QException &exception){if(futureInterface) { futureInterface->reportException(exception);}else{ QMutexLocker lock(&mutex);if(!exceptionStore.hasException()) exceptionStore.setException(exception);}}#endif}// namespace QtConcurrent QT_END_NAMESPACE #endif// QT_NO_CONCURRENT
|