Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-09-21 09:21:01

0001 // Copyright (C) 2016 The Qt Company Ltd.
0002 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
0003 // Qt-Security score:significant reason:default
0004 
0005 #ifndef QTCONCURRENT_ITERATEKERNEL_H
0006 #define QTCONCURRENT_ITERATEKERNEL_H
0007 
0008 #include <QtConcurrent/qtconcurrent_global.h>
0009 
0010 #if !defined(QT_NO_CONCURRENT) || defined(Q_QDOC)
0011 
0012 #include <QtCore/qatomic.h>
0013 #include <QtConcurrent/qtconcurrentmedian.h>
0014 #include <QtConcurrent/qtconcurrentthreadengine.h>
0015 
0016 #include <iterator>
0017 
0018 QT_BEGIN_NAMESPACE
0019 
0020 
0021 
0022 namespace QtConcurrent {
0023 
0024 /*
0025     The BlockSizeManager class manages how many iterations a thread should
0026     reserve and process at a time. This is done by measuring the time spent
0027     in the user code versus the control part code, and then increasing
0028     the block size if the ratio between them is to small. The block size
0029     management is done on the basis of the median of several timing measurements,
0030     and it is done individually for each thread.
0031 */
0032 class Q_CONCURRENT_EXPORT BlockSizeManager
0033 {
0034 public:
0035     explicit BlockSizeManager(QThreadPool *pool, int iterationCount);
0036 
0037     void timeBeforeUser();
0038     void timeAfterUser();
0039     int blockSize();
0040 
0041 private:
0042     inline bool blockSizeMaxed()
0043     {
0044         return (m_blockSize >= maxBlockSize);
0045     }
0046 
0047     const int maxBlockSize;
0048     qint64 beforeUser;
0049     qint64 afterUser;
0050     Median controlPartElapsed;
0051     Median userPartElapsed;
0052     int m_blockSize;
0053 
0054     Q_DISABLE_COPY(BlockSizeManager)
0055 };
0056 
0057 template <typename T>
0058 class ResultReporter
0059 {
0060 public:
0061     ResultReporter(ThreadEngine<T> *_threadEngine, T &_defaultValue)
0062         : threadEngine(_threadEngine), defaultValue(_defaultValue)
0063     {
0064     }
0065 
0066     void reserveSpace(int resultCount)
0067     {
0068         currentResultCount = resultCount;
0069         resizeList(qMax(resultCount, vector.size()));
0070     }
0071 
0072     void reportResults(int begin)
0073     {
0074         const int useVectorThreshold = 4; // Tunable parameter.
0075         if (currentResultCount > useVectorThreshold) {
0076             resizeList(currentResultCount);
0077             threadEngine->reportResults(vector, begin);
0078         } else {
0079             for (int i = 0; i < currentResultCount; ++i)
0080                 threadEngine->reportResult(&vector.at(i), begin + i);
0081         }
0082     }
0083 
0084     inline T * getPointer()
0085     {
0086         return vector.data();
0087     }
0088 
0089     int currentResultCount = 0;
0090     ThreadEngine<T> *threadEngine;
0091     QList<T> vector;
0092 
0093 private:
0094     void resizeList(qsizetype size)
0095     {
0096         if constexpr (std::is_default_constructible_v<T>)
0097             vector.resize(size);
0098         else
0099             vector.resize(size, defaultValue);
0100     }
0101 
0102     T &defaultValue;
0103 };
0104 
0105 template <>
0106 class ResultReporter<void>
0107 {
0108 public:
0109     inline ResultReporter(ThreadEngine<void> *) { }
0110     inline void reserveSpace(int) { }
0111     inline void reportResults(int) { }
0112     inline void * getPointer() { return nullptr; }
0113 };
0114 
0115 template<typename T>
0116 struct DefaultValueContainer
0117 {
0118     template<typename U = T>
0119     DefaultValueContainer(U &&_value) : value(std::forward<U>(_value))
0120     {
0121     }
0122 
0123     T value;
0124 };
0125 
0126 template<>
0127 struct DefaultValueContainer<void>
0128 {
0129 };
0130 
0131 inline bool selectIteration(std::bidirectional_iterator_tag)
0132 {
0133     return false; // while
0134 }
0135 
0136 inline bool selectIteration(std::forward_iterator_tag)
0137 {
0138     return false; // while
0139 }
0140 
0141 inline bool selectIteration(std::random_access_iterator_tag)
0142 {
0143     return true; // for
0144 }
0145 
0146 template <typename Iterator, typename T>
0147 class IterateKernel : public ThreadEngine<T>
0148 {
0149     using IteratorCategory = typename std::iterator_traits<Iterator>::iterator_category;
0150 
0151 public:
0152     typedef T ResultType;
0153 
0154     template<typename U = T, std::enable_if_t<std::is_same_v<U, void>, bool> = true>
0155     IterateKernel(QThreadPool *pool, Iterator _begin, Iterator _end)
0156         : ThreadEngine<U>(pool),
0157           begin(_begin),
0158           end(_end),
0159           current(_begin),
0160           iterationCount(selectIteration(IteratorCategory()) ? static_cast<int>(std::distance(_begin, _end)) : 0),
0161           forIteration(selectIteration(IteratorCategory())),
0162           progressReportingEnabled(true)
0163     {
0164     }
0165 
0166     template<typename U = T, std::enable_if_t<!std::is_same_v<U, void>, bool> = true>
0167     IterateKernel(QThreadPool *pool, Iterator _begin, Iterator _end)
0168         : ThreadEngine<U>(pool),
0169           begin(_begin),
0170           end(_end),
0171           current(_begin),
0172           iterationCount(selectIteration(IteratorCategory()) ? static_cast<int>(std::distance(_begin, _end)) : 0),
0173           forIteration(selectIteration(IteratorCategory())),
0174           progressReportingEnabled(true),
0175           defaultValue(U())
0176     {
0177     }
0178 
0179     template<typename U = T, std::enable_if_t<!std::is_same_v<U, void>, bool> = true>
0180     IterateKernel(QThreadPool *pool, Iterator _begin, Iterator _end, U &&_defaultValue)
0181         : ThreadEngine<U>(pool),
0182           begin(_begin),
0183           end(_end),
0184           current(_begin),
0185           iterationCount(selectIteration(IteratorCategory()) ? static_cast<int>(std::distance(_begin, _end)) : 0),
0186           forIteration(selectIteration(IteratorCategory())),
0187           progressReportingEnabled(true),
0188           defaultValue(std::forward<U>(_defaultValue))
0189     {
0190     }
0191 
0192     virtual ~IterateKernel() { }
0193 
0194     virtual bool runIteration(Iterator, int , T *) { return false; }
0195     virtual bool runIterations(Iterator, int, int, T *) { return false; }
0196 
0197     void start() override
0198     {
0199         progressReportingEnabled = this->isProgressReportingEnabled();
0200         if (progressReportingEnabled && iterationCount > 0)
0201             this->setProgressRange(0, iterationCount);
0202     }
0203 
0204     bool shouldStartThread() override
0205     {
0206         if (forIteration)
0207             return (currentIndex.loadRelaxed() < iterationCount) && !this->shouldThrottleThread();
0208         else // whileIteration
0209             return (iteratorThreads.loadRelaxed() == 0);
0210     }
0211 
0212     ThreadFunctionResult threadFunction() override
0213     {
0214         if (forIteration)
0215             return this->forThreadFunction();
0216         else // whileIteration
0217             return this->whileThreadFunction();
0218     }
0219 
0220     ThreadFunctionResult forThreadFunction()
0221     {
0222         BlockSizeManager blockSizeManager(ThreadEngineBase::threadPool, iterationCount);
0223         ResultReporter<T> resultReporter = createResultsReporter();
0224 
0225         for(;;) {
0226             if (this->isCanceled())
0227                 break;
0228 
0229             const int currentBlockSize = blockSizeManager.blockSize();
0230 
0231             if (currentIndex.loadRelaxed() >= iterationCount)
0232                 break;
0233 
0234             // Atomically reserve a block of iterationCount for this thread.
0235             const int beginIndex = currentIndex.fetchAndAddRelease(currentBlockSize);
0236             const int endIndex = qMin(beginIndex + currentBlockSize, iterationCount);
0237 
0238             if (beginIndex >= endIndex) {
0239                 // No more work
0240                 break;
0241             }
0242 
0243             this->waitForResume(); // (only waits if the qfuture is paused.)
0244 
0245             if (shouldStartThread())
0246                 this->startThread();
0247 
0248             const int finalBlockSize = endIndex - beginIndex; // block size adjusted for possible end-of-range
0249             resultReporter.reserveSpace(finalBlockSize);
0250 
0251             // Call user code with the current iteration range.
0252             blockSizeManager.timeBeforeUser();
0253             const bool resultsAvailable = this->runIterations(begin, beginIndex, endIndex, resultReporter.getPointer());
0254             blockSizeManager.timeAfterUser();
0255 
0256             if (resultsAvailable)
0257                 resultReporter.reportResults(beginIndex);
0258 
0259             // Report progress if progress reporting enabled.
0260             if (progressReportingEnabled) {
0261                 completed.fetchAndAddAcquire(finalBlockSize);
0262                 this->setProgressValue(this->completed.loadRelaxed());
0263             }
0264 
0265             if (this->shouldThrottleThread())
0266                 return ThrottleThread;
0267         }
0268         return ThreadFinished;
0269     }
0270 
0271     ThreadFunctionResult whileThreadFunction()
0272     {
0273         if (iteratorThreads.testAndSetAcquire(0, 1) == false)
0274             return ThreadFinished;
0275 
0276         ResultReporter<T> resultReporter = createResultsReporter();
0277         resultReporter.reserveSpace(1);
0278 
0279         while (current != end) {
0280             // The following two lines breaks support for input iterators according to
0281             // the sgi docs: dereferencing prev after calling ++current is not allowed
0282             // on input iterators. (prev is dereferenced inside user.runIteration())
0283             Iterator prev = current;
0284             ++current;
0285             int index = currentIndex.fetchAndAddRelaxed(1);
0286             iteratorThreads.testAndSetRelease(1, 0);
0287 
0288             this->waitForResume(); // (only waits if the qfuture is paused.)
0289 
0290             if (shouldStartThread())
0291                 this->startThread();
0292 
0293             const bool resultAavailable = this->runIteration(prev, index, resultReporter.getPointer());
0294             if (resultAavailable)
0295                 resultReporter.reportResults(index);
0296 
0297             if (this->shouldThrottleThread())
0298                 return ThrottleThread;
0299 
0300             if (iteratorThreads.testAndSetAcquire(0, 1) == false)
0301                 return ThreadFinished;
0302         }
0303 
0304         return ThreadFinished;
0305     }
0306 
0307 private:
0308     ResultReporter<T> createResultsReporter()
0309     {
0310         if constexpr (!std::is_same_v<T, void>)
0311             return ResultReporter<T>(this, defaultValue.value);
0312         else
0313             return ResultReporter<T>(this);
0314     }
0315 
0316 public:
0317     const Iterator begin;
0318     const Iterator end;
0319     Iterator current;
0320     QAtomicInt currentIndex;
0321     QAtomicInt iteratorThreads;
0322     QAtomicInt completed;
0323     const int iterationCount;
0324     const bool forIteration;
0325     bool progressReportingEnabled;
0326     DefaultValueContainer<ResultType> defaultValue;
0327 };
0328 
0329 } // namespace QtConcurrent
0330 
0331 
0332 QT_END_NAMESPACE
0333 
0334 #endif // QT_NO_CONCURRENT
0335 
0336 #endif