From 38be0d13830efd2d98281c645c3a60afe05ffece Mon Sep 17 00:00:00 2001 From: Qt by Nokia Date: Wed, 27 Apr 2011 12:05:43 +0200 Subject: Initial import from the monolithic Qt. This is the beginning of revision history for this module. If you want to look at revision history older than this, please refer to the Qt Git wiki for how to use Git history grafting. At the time of writing, this wiki is located here: http://qt.gitorious.org/qt/pages/GitIntroductionWithQt If you have already performed the grafting and you don't see any history beyond this commit, try running "git log" with the "--follow" argument. Branched from the monolithic repo, Qt master branch, at commit 896db169ea224deb96c59ce8af800d019de63f12 --- src/corelib/concurrent/concurrent.pri | 42 + src/corelib/concurrent/qfuture.cpp | 697 ++++++++++ src/corelib/concurrent/qfuture.h | 278 ++++ src/corelib/concurrent/qfutureinterface.cpp | 565 +++++++++ src/corelib/concurrent/qfutureinterface.h | 313 +++++ src/corelib/concurrent/qfutureinterface_p.h | 167 +++ src/corelib/concurrent/qfuturesynchronizer.cpp | 156 +++ src/corelib/concurrent/qfuturesynchronizer.h | 121 ++ src/corelib/concurrent/qfuturewatcher.cpp | 592 +++++++++ src/corelib/concurrent/qfuturewatcher.h | 222 ++++ src/corelib/concurrent/qfuturewatcher_p.h | 90 ++ src/corelib/concurrent/qrunnable.cpp | 107 ++ src/corelib/concurrent/qrunnable.h | 73 ++ src/corelib/concurrent/qtconcurrentcompilertest.h | 65 + src/corelib/concurrent/qtconcurrentexception.cpp | 223 ++++ src/corelib/concurrent/qtconcurrentexception.h | 128 ++ src/corelib/concurrent/qtconcurrentfilter.cpp | 330 +++++ src/corelib/concurrent/qtconcurrentfilter.h | 736 +++++++++++ src/corelib/concurrent/qtconcurrentfilterkernel.h | 351 ++++++ .../concurrent/qtconcurrentfunctionwrappers.h | 173 +++ .../concurrent/qtconcurrentiteratekernel.cpp | 199 +++ src/corelib/concurrent/qtconcurrentiteratekernel.h | 340 +++++ src/corelib/concurrent/qtconcurrentmap.cpp | 402 ++++++ src/corelib/concurrent/qtconcurrentmap.h | 780 ++++++++++++ src/corelib/concurrent/qtconcurrentmapkernel.h | 273 ++++ src/corelib/concurrent/qtconcurrentmedian.h | 130 ++ src/corelib/concurrent/qtconcurrentreducekernel.h | 255 ++++ src/corelib/concurrent/qtconcurrentresultstore.cpp | 256 ++++ src/corelib/concurrent/qtconcurrentresultstore.h | 239 ++++ src/corelib/concurrent/qtconcurrentrun.cpp | 152 +++ src/corelib/concurrent/qtconcurrentrun.h | 297 +++++ src/corelib/concurrent/qtconcurrentrunbase.h | 155 +++ .../concurrent/qtconcurrentstoredfunctioncall.h | 1328 ++++++++++++++++++++ .../concurrent/qtconcurrentthreadengine.cpp | 299 +++++ src/corelib/concurrent/qtconcurrentthreadengine.h | 285 +++++ src/corelib/concurrent/qthreadpool.cpp | 651 ++++++++++ src/corelib/concurrent/qthreadpool.h | 96 ++ src/corelib/concurrent/qthreadpool_p.h | 107 ++ 38 files changed, 11673 insertions(+) create mode 100644 src/corelib/concurrent/concurrent.pri create mode 100644 src/corelib/concurrent/qfuture.cpp create mode 100644 src/corelib/concurrent/qfuture.h create mode 100644 src/corelib/concurrent/qfutureinterface.cpp create mode 100644 src/corelib/concurrent/qfutureinterface.h create mode 100644 src/corelib/concurrent/qfutureinterface_p.h create mode 100644 src/corelib/concurrent/qfuturesynchronizer.cpp create mode 100644 src/corelib/concurrent/qfuturesynchronizer.h create mode 100644 src/corelib/concurrent/qfuturewatcher.cpp create mode 100644 src/corelib/concurrent/qfuturewatcher.h create mode 100644 src/corelib/concurrent/qfuturewatcher_p.h create mode 100644 src/corelib/concurrent/qrunnable.cpp create mode 100644 src/corelib/concurrent/qrunnable.h create mode 100644 src/corelib/concurrent/qtconcurrentcompilertest.h create mode 100644 src/corelib/concurrent/qtconcurrentexception.cpp create mode 100644 src/corelib/concurrent/qtconcurrentexception.h create mode 100644 src/corelib/concurrent/qtconcurrentfilter.cpp create mode 100644 src/corelib/concurrent/qtconcurrentfilter.h create mode 100644 src/corelib/concurrent/qtconcurrentfilterkernel.h create mode 100644 src/corelib/concurrent/qtconcurrentfunctionwrappers.h create mode 100644 src/corelib/concurrent/qtconcurrentiteratekernel.cpp create mode 100644 src/corelib/concurrent/qtconcurrentiteratekernel.h create mode 100644 src/corelib/concurrent/qtconcurrentmap.cpp create mode 100644 src/corelib/concurrent/qtconcurrentmap.h create mode 100644 src/corelib/concurrent/qtconcurrentmapkernel.h create mode 100644 src/corelib/concurrent/qtconcurrentmedian.h create mode 100644 src/corelib/concurrent/qtconcurrentreducekernel.h create mode 100644 src/corelib/concurrent/qtconcurrentresultstore.cpp create mode 100644 src/corelib/concurrent/qtconcurrentresultstore.h create mode 100644 src/corelib/concurrent/qtconcurrentrun.cpp create mode 100644 src/corelib/concurrent/qtconcurrentrun.h create mode 100644 src/corelib/concurrent/qtconcurrentrunbase.h create mode 100644 src/corelib/concurrent/qtconcurrentstoredfunctioncall.h create mode 100644 src/corelib/concurrent/qtconcurrentthreadengine.cpp create mode 100644 src/corelib/concurrent/qtconcurrentthreadengine.h create mode 100644 src/corelib/concurrent/qthreadpool.cpp create mode 100644 src/corelib/concurrent/qthreadpool.h create mode 100644 src/corelib/concurrent/qthreadpool_p.h (limited to 'src/corelib/concurrent') diff --git a/src/corelib/concurrent/concurrent.pri b/src/corelib/concurrent/concurrent.pri new file mode 100644 index 0000000000..940297139c --- /dev/null +++ b/src/corelib/concurrent/concurrent.pri @@ -0,0 +1,42 @@ +SOURCES += \ + concurrent/qfuture.cpp \ + concurrent/qfutureinterface.cpp \ + concurrent/qfuturesynchronizer.cpp \ + concurrent/qfuturewatcher.cpp \ + concurrent/qrunnable.cpp \ + concurrent/qtconcurrentfilter.cpp \ + concurrent/qtconcurrentmap.cpp \ + concurrent/qtconcurrentresultstore.cpp \ + concurrent/qtconcurrentthreadengine.cpp \ + concurrent/qtconcurrentiteratekernel.cpp \ + concurrent/qtconcurrentexception.cpp \ + concurrent/qthreadpool.cpp + +HEADERS += \ + concurrent/qfuture.h \ + concurrent/qfutureinterface.h \ + concurrent/qfuturesynchronizer.h \ + concurrent/qfuturewatcher.h \ + concurrent/qrunnable.h \ + concurrent/qtconcurrentcompilertest.h \ + concurrent/qtconcurrentexception.h \ + concurrent/qtconcurrentfilter.h \ + concurrent/qtconcurrentfilterkernel.h \ + concurrent/qtconcurrentfunctionwrappers.h \ + concurrent/qtconcurrentiteratekernel.h \ + concurrent/qtconcurrentmap.h \ + concurrent/qtconcurrentmapkernel.h \ + concurrent/qtconcurrentmedian.h \ + concurrent/qtconcurrentreducekernel.h \ + concurrent/qtconcurrentresultstore.h \ + concurrent/qtconcurrentrun.h \ + concurrent/qtconcurrentrunbase.h \ + concurrent/qtconcurrentstoredfunctioncall.h \ + concurrent/qtconcurrentthreadengine.h \ + concurrent/qthreadpool.h + +# private headers +HEADERS += \ + concurrent/qfutureinterface_p.h \ + concurrent/qfuturewatcher_p.h \ + concurrent/qthreadpool_p.h diff --git a/src/corelib/concurrent/qfuture.cpp b/src/corelib/concurrent/qfuture.cpp new file mode 100644 index 0000000000..dfae1de16e --- /dev/null +++ b/src/corelib/concurrent/qfuture.cpp @@ -0,0 +1,697 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! \class QFuture + \threadsafe + \brief The QFuture class represents the result of an asynchronous computation. + \since 4.4 + + \ingroup thread + + To start a computation, use one of the APIs in the + \l {Concurrent Programming}{Qt Concurrent} framework. + + QFuture allows threads to be synchronized against one or more results + which will be ready at a later point in time. The result can be of any type + that has a default constructor and a copy constructor. If a result is not + available at the time of calling the result(), resultAt(), or results() + functions, QFuture will wait until the result becomes available. You can + use the isResultReadyAt() function to determine if a result is ready or + not. For QFuture objects that report more than one result, the + resultCount() function returns the number of continuous results. This + means that it is always safe to iterate through the results from 0 to + resultCount(). + + QFuture provides a \l{Java-style iterators}{Java-style iterator} + (QFutureIterator) and an \l{STL-style iterators}{STL-style iterator} + (QFuture::const_iterator). Using these iterators is another way to access + results in the future. + + QFuture also offers ways to interact with a runnning computation. For + instance, the computation can be canceled with the cancel() function. To + pause the computation, use the setPaused() function or one of the pause(), + resume(), or togglePaused() convenience functions. Be aware that not all + asynchronous computations can be canceled or paused. For example, the + future returned by QtConcurrent::run() cannot be canceled; but the + future returned by QtConcurrent::mappedReduced() can. + + Progress information is provided by the progressValue(), + progressMinimum(), progressMaximum(), and progressText() functions. The + waitForFinished() function causes the calling thread to block and wait for + the computation to finish, ensuring that all results are available. + + The state of the computation represented by a QFuture can be queried using + the isCanceled(), isStarted(), isFinished(), isRunning(), or isPaused() + functions. + + QFuture is a lightweight reference counted class that can be passed by + value. + + QFuture is specialized to not contain any of the result fetching + functions. Any QFuture can be assigned or copied into a QFuture + as well. This is useful if only status or progress information is needed + - not the actual result data. + + To interact with running tasks using signals and slots, use QFutureWatcher. + + \sa QFutureWatcher, {Concurrent Programming}{Qt Concurrent} +*/ + +/*! \fn QFuture::QFuture() + + Constructs an empty future. +*/ + +/*! \fn QFuture::QFuture(const QFuture &other) + + Constructs a copy of \a other. + + \sa operator=() +*/ + +/*! \fn QFuture::QFuture(QFutureInterface *resultHolder) + \internal +*/ + +/*! \fn QFuture::~QFuture() + + Destroys the future. + + Note that this neither waits nor cancels the asynchronous computation. Use + waitForFinished() or QFutureSynchronizer when you need to ensure that the + computation is completed before the future is destroyed. +*/ + +/*! \fn QFuture &QFuture::operator=(const QFuture &other) + + Assigns \a other to this future and returns a reference to this future. +*/ + +/*! \fn bool QFuture::operator==(const QFuture &other) const + + Returns true if \a other is a copy of this future; otherwise returns false. +*/ + +/*! \fn bool QFuture::operator!=(const QFuture &other) const + + Returns true if \a other is \e not a copy of this future; otherwise returns + false. +*/ + +/*! \fn void QFuture::cancel() + + Cancels the asynchronous computation represented by this future. Note that + the cancelation is asynchronous. Use waitForFinished() after calling + cancel() when you need synchronous cancelation. + + Results currently available may still be accessed on a canceled future, + but new results will \e not become available after calling this function. + Any QFutureWatcher object that is watching this future will not deliver + progress and result ready signals on a canceled future. + + Be aware that not all asynchronous computations can be canceled. For + example, the future returned by QtConcurrent::run() cannot be canceled; + but the future returned by QtConcurrent::mappedReduced() can. +*/ + +/*! \fn bool QFuture::isCanceled() const + + Returns true if the asynchronous computation has been canceled with the + cancel() function; otherwise returns false. + + Be aware that the computation may still be running even though this + function returns true. See cancel() for more details. +*/ + +/*! \fn void QFuture::setPaused(bool paused) + + If \a paused is true, this function pauses the asynchronous computation + represented by the future. If the computation is already paused, this + function does nothing. Any QFutureWatcher object that is watching this + future will stop delivering progress and result ready signals while the + future is paused. Signal delivery will continue once the future is + resumed. + + If \a paused is false, this function resumes the asynchronous computation. + If the computation was not previously paused, this function does nothing. + + Be aware that not all computations can be paused. For example, the future + returned by QtConcurrent::run() cannot be paused; but the future returned + by QtConcurrent::mappedReduced() can. + + \sa pause(), resume(), togglePaused() +*/ + +/*! \fn bool QFuture::isPaused() const + + Returns true if the asynchronous computation has been paused with the + pause() function; otherwise returns false. + + Be aware that the computation may still be running even though this + function returns true. See setPaused() for more details. + + \sa setPaused(), togglePaused() +*/ + +/*! \fn void QFuture::pause() + + Pauses the asynchronous computation represented by this future. This is a + convenience method that simply calls setPaused(true). + + \sa resume() +*/ + +/*! \fn void QFuture::resume() + + Resumes the asynchronous computation represented by this future. This is a + convenience method that simply calls setPaused(false). + + \sa pause() +*/ + +/*! \fn void QFuture::togglePaused() + + Toggles the paused state of the asynchronous computation. In other words, + if the computation is currently paused, calling this function resumes it; + if the computation is running, it is paused. This is a convenience method + for calling setPaused(!isPaused()). + + \sa setPaused(), pause(), resume() +*/ + +/*! \fn bool QFuture::isStarted() const + + Returns true if the asynchronous computation represented by this future + has been started; otherwise returns false. +*/ + +/*! \fn bool QFuture::isFinished() const + + Returns true if the asynchronous computation represented by this future + has finished; otherwise returns false. +*/ + +/*! \fn bool QFuture::isRunning() const + + Returns true if the asynchronous computation represented by this future is + currently running; otherwise returns false. +*/ + +/*! \fn int QFuture::resultCount() const + + Returns the number of continuous results available in this future. The real + number of results stored might be different from this value, due to gaps + in the result set. It is always safe to iterate through the results from 0 + to resultCount(). + \sa result(), resultAt(), results() +*/ + +/*! \fn int QFuture::progressValue() const + + Returns the current progress value, which is between the progressMinimum() + and progressMaximum(). + + \sa progressMinimum(), progressMaximum() +*/ + +/*! \fn int QFuture::progressMinimum() const + + Returns the minimum progressValue(). + + \sa progressValue(), progressMaximum() +*/ + +/*! \fn int QFuture::progressMaximum() const + + Returns the maximum progressValue(). + + \sa progressValue(), progressMinimum() +*/ + +/*! \fn QString QFuture::progressText() const + + Returns the (optional) textual representation of the progress as reported + by the asynchronous computation. + + Be aware that not all computations provide a textual representation of the + progress, and as such, this function may return an empty string. +*/ + +/*! \fn void QFuture::waitForFinished() + + Waits for the asynchronous computation to finish (including cancel()ed + computations). +*/ + +/*! \fn T QFuture::result() const + + Returns the first result in the future. If the result is not immediately + available, this function will block and wait for the result to become + available. This is a convenience method for calling resultAt(0). + + \sa resultAt(), results() +*/ + +/*! \fn T QFuture::resultAt(int index) const + + Returns the result at \a index in the future. If the result is not + immediately available, this function will block and wait for the result to + become available. + + \sa result(), results(), resultCount() +*/ + +/*! \fn bool QFuture::isResultReadyAt(int index) const + + Returns true if the result at \a index is immediately available; otherwise + returns false. + + \sa resultAt(), resultCount() +*/ + +/*! \fn QFuture::operator T() const + + Returns the first result in the future. If the result is not immediately + available, this function will block and wait for the result to become + available. This is a convenience method for calling result() or + resultAt(0). + + \sa result(), resultAt(), results() +*/ + +/*! \fn QList QFuture::results() const + + Returns all results from the future. If the results are not immediately + available, this function will block and wait for them to become available. + + \sa result(), resultAt(), resultCount() +*/ + +/*! \fn QFuture::const_iterator QFuture::begin() const + + Returns a const \l{STL-style iterator} pointing to the first result in the + future. + + \sa constBegin(), end() +*/ + +/*! \fn QFuture::const_iterator QFuture::end() const + + Returns a const \l{STL-style iterator} pointing to the imaginary result + after the last result in the future. + + \sa begin(), constEnd() +*/ + +/*! \fn QFuture::const_iterator QFuture::constBegin() const + + Returns a const \l{STL-style iterator} pointing to the first result in the + future. + + \sa begin(), constEnd() +*/ + +/*! \fn QFuture::const_iterator QFuture::constEnd() const + + Returns a const \l{STL-style iterator} pointing to the imaginary result + after the last result in the future. + + \sa constBegin(), end() +*/ + +/*! \class QFuture::const_iterator + \reentrant + \since 4.4 + + \brief The QFuture::const_iterator class provides an STL-style const + iterator for QFuture. + + QFuture provides both \l{STL-style iterators} and \l{Java-style iterators}. + The STL-style iterators are more low-level and more cumbersome to use; on + the other hand, they are slightly faster and, for developers who already + know STL, have the advantage of familiarity. + + The default QFuture::const_iterator constructor creates an uninitialized + iterator. You must initialize it using a QFuture function like + QFuture::constBegin() or QFuture::constEnd() before you start iterating. + Here's a typical loop that prints all the results available in a future: + + \snippet doc/src/snippets/code/src_corelib_concurrent_qfuture.cpp 0 + + \sa QFutureIterator, QFuture +*/ + +/*! \typedef QFuture::const_iterator::iterator_category + + Typedef for std::bidirectional_iterator_tag. Provided for STL compatibility. +*/ + +/*! \typedef QFuture::const_iterator::difference_type + + Typedef for ptrdiff_t. Provided for STL compatibility. +*/ + +/*! \typedef QFuture::const_iterator::value_type + + Typedef for T. Provided for STL compatibility. +*/ + +/*! \typedef QFuture::const_iterator::pointer + + Typedef for const T *. Provided for STL compatibility. +*/ + +/*! \typedef QFuture::const_iterator::reference + + Typedef for const T &. Provided for STL compatibility. +*/ + +/*! \fn QFuture::const_iterator::const_iterator() + + Constructs an uninitialized iterator. + + Functions like operator*() and operator++() should not be called on an + uninitialized iterartor. Use operator=() to assign a value to it before + using it. + + \sa QFuture::constBegin() QFuture::constEnd() +*/ + +/*! \fn QFuture::const_iterator::const_iterator(QFuture const * const future, int index) + \internal +*/ + +/*! \fn QFuture::const_iterator::const_iterator(const const_iterator &other) + + Constructs a copy of \a other. +*/ + +/*! \fn QFuture::const_iterator &QFuture::const_iterator::operator=(const const_iterator &other) + + Assigns \a other to this iterator. +*/ + +/*! \fn const T &QFuture::const_iterator::operator*() const + + Returns the current result. +*/ + +/*! \fn const T *QFuture::const_iterator::operator->() const + + Returns a pointer to the current result. +*/ + +/*! \fn bool QFuture::const_iterator::operator!=(const const_iterator &other) const + + Returns true if \a other points to a different result than this iterator; + otherwise returns false. + + \sa operator==() +*/ + +/*! \fn bool QFuture::const_iterator::operator==(const const_iterator &other) const + + Returns true if \a other points to the same result as this iterator; + otherwise returns false. + + \sa operator!=() +*/ + +/*! \fn QFuture::const_iterator &QFuture::const_iterator::operator++() + + The prefix ++ operator (\c{++it}) advances the iterator to the next result + in the future and returns an iterator to the new current result. + + Calling this function on QFuture::constEnd() leads to undefined results. + + \sa operator--() +*/ + +/*! \fn QFuture::const_iterator QFuture::const_iterator::operator++(int) + + \overload + + The postfix ++ operator (\c{it++}) advances the iterator to the next + result in the future and returns an iterator to the previously current + result. +*/ + +/*! \fn QFuture::const_iterator &QFuture::const_iterator::operator--() + + The prefix -- operator (\c{--it}) makes the preceding result current and + returns an iterator to the new current result. + + Calling this function on QFuture::constBegin() leads to undefined results. + + \sa operator++() +*/ + +/*! \fn QFuture::const_iterator QFuture::const_iterator::operator--(int) + + \overload + + The postfix -- operator (\c{it--}) makes the preceding result current and + returns an iterator to the previously current result. +*/ + +/*! \fn QFuture::const_iterator &QFuture::const_iterator::operator+=(int j) + + Advances the iterator by \a j results. (If \a j is negative, the iterator + goes backward.) + + \sa operator-=(), operator+() +*/ + +/*! \fn QFuture::const_iterator &QFuture::const_iterator::operator-=(int j) + + Makes the iterator go back by \a j results. (If \a j is negative, the + iterator goes forward.) + + \sa operator+=(), operator-() +*/ + +/*! \fn QFuture::const_iterator QFuture::const_iterator::operator+(int j) const + + Returns an iterator to the results at \a j positions forward from this + iterator. (If \a j is negative, the iterator goes backward.) + + \sa operator-(), operator+=() +*/ + +/*! \fn QFuture::const_iterator QFuture::const_iterator::operator-(int j) const + + Returns an iterator to the result at \a j positions backward from this + iterator. (If \a j is negative, the iterator goes forward.) + + \sa operator+(), operator-=() +*/ + +/*! \typedef QFuture::ConstIterator + + Qt-style synonym for QFuture::const_iterator. +*/ + +/*! + \class QFutureIterator + \reentrant + \since 4.4 + \inmodule QtCore + + \brief The QFutureIterator class provides a Java-style const iterator for + QFuture. + + QFuture has both \l{Java-style iterators} and \l{STL-style iterators}. The + Java-style iterators are more high-level and easier to use than the + STL-style iterators; on the other hand, they are slightly less efficient. + + An alternative to using iterators is to use index positions. Some QFuture + member functions take an index as their first parameter, making it + possible to access results without using iterators. + + QFutureIterator\ allows you to iterate over a QFuture\. Note that + there is no mutable iterator for QFuture (unlike the other Java-style + iterators). + + The QFutureIterator constructor takes a QFuture as its argument. After + construction, the iterator is located at the very beginning of the result + list (i.e. before the first result). Here's how to iterate over all the + results sequentially: + + \snippet doc/src/snippets/code/src_corelib_concurrent_qfuture.cpp 1 + + The next() function returns the next result (waiting for it to become + available, if necessary) from the future and advances the iterator. Unlike + STL-style iterators, Java-style iterators point \e between results rather + than directly \e at results. The first call to next() advances the iterator + to the position between the first and second result, and returns the first + result; the second call to next() advances the iterator to the position + between the second and third result, and returns the second result; and + so on. + + \img javaiterators1.png + + Here's how to iterate over the elements in reverse order: + + \snippet doc/src/snippets/code/src_corelib_concurrent_qfuture.cpp 2 + + If you want to find all occurrences of a particular value, use findNext() + or findPrevious() in a loop. + + Multiple iterators can be used on the same future. If the future is + modified while a QFutureIterator is active, the QFutureIterator will + continue iterating over the original future, ignoring the modified copy. + + \sa QFuture::const_iterator, QFuture +*/ + +/*! + \fn QFutureIterator::QFutureIterator(const QFuture &future) + + Constructs an iterator for traversing \a future. The iterator is set to be + at the front of the result list (before the first result). + + \sa operator=() +*/ + +/*! \fn QFutureIterator &QFutureIterator::operator=(const QFuture &future) + + Makes the iterator operate on \a future. The iterator is set to be at the + front of the result list (before the first result). + + \sa toFront(), toBack() +*/ + +/*! \fn void QFutureIterator::toFront() + + Moves the iterator to the front of the result list (before the first + result). + + \sa toBack(), next() +*/ + +/*! \fn void QFutureIterator::toBack() + + Moves the iterator to the back of the result list (after the last result). + + \sa toFront(), previous() +*/ + +/*! \fn bool QFutureIterator::hasNext() const + + Returns true if there is at least one result ahead of the iterator, e.g., + the iterator is \e not at the back of the result list; otherwise returns + false. + + \sa hasPrevious(), next() +*/ + +/*! \fn const T &QFutureIterator::next() + + Returns the next result and advances the iterator by one position. + + Calling this function on an iterator located at the back of the result + list leads to undefined results. + + \sa hasNext(), peekNext(), previous() +*/ + +/*! \fn const T &QFutureIterator::peekNext() const + + Returns the next result without moving the iterator. + + Calling this function on an iterator located at the back of the result + list leads to undefined results. + + \sa hasNext(), next(), peekPrevious() +*/ + +/*! \fn bool QFutureIterator::hasPrevious() const + + Returns true if there is at least one result ahead of the iterator, e.g., + the iterator is \e not at the front of the result list; otherwise returns + false. + + \sa hasNext(), previous() +*/ + +/*! \fn const T &QFutureIterator::previous() + + Returns the previous result and moves the iterator back by one position. + + Calling this function on an iterator located at the front of the result + list leads to undefined results. + + \sa hasPrevious(), peekPrevious(), next() +*/ + +/*! \fn const T &QFutureIterator::peekPrevious() const + + Returns the previous result without moving the iterator. + + Calling this function on an iterator located at the front of the result + list leads to undefined results. + + \sa hasPrevious(), previous(), peekNext() +*/ + +/*! \fn bool QFutureIterator::findNext(const T &value) + + Searches for \a value starting from the current iterator position forward. + Returns true if \a value is found; otherwise returns false. + + After the call, if \a value was found, the iterator is positioned just + after the matching result; otherwise, the iterator is positioned at the + back of the result list. + + \sa findPrevious() +*/ + +/*! \fn bool QFutureIterator::findPrevious(const T &value) + + Searches for \a value starting from the current iterator position + backward. Returns true if \a value is found; otherwise returns false. + + After the call, if \a value was found, the iterator is positioned just + before the matching result; otherwise, the iterator is positioned at the + front of the result list. + + \sa findNext() +*/ diff --git a/src/corelib/concurrent/qfuture.h b/src/corelib/concurrent/qfuture.h new file mode 100644 index 0000000000..e8a6e264b3 --- /dev/null +++ b/src/corelib/concurrent/qfuture.h @@ -0,0 +1,278 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QFUTURE_H +#define QFUTURE_H + +#include + +#ifndef QT_NO_QFUTURE + +#include +#include +#include + +QT_BEGIN_HEADER +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +template +class QFutureWatcher; +template <> +class QFutureWatcher; + +template +class QFuture +{ +public: + QFuture() + : d(QFutureInterface::canceledResult()) + { } + explicit QFuture(QFutureInterface *p) // internal + : d(*p) + { } + QFuture(const QFuture &other) + : d(other.d) + { } + ~QFuture() + { } + + inline QFuture &operator=(const QFuture &other); + bool operator==(const QFuture &other) const { return (d == other.d); } + bool operator!=(const QFuture &other) const { return (d != other.d); } + + void cancel() { d.cancel(); } + bool isCanceled() const { return d.isCanceled(); } + + void setPaused(bool paused) { d.setPaused(paused); } + bool isPaused() const { return d.isPaused(); } + void pause() { setPaused(true); } + void resume() { setPaused(false); } + void togglePaused() { d.togglePaused(); } + + bool isStarted() const { return d.isStarted(); } + bool isFinished() const { return d.isFinished(); } + bool isRunning() const { return d.isRunning(); } + + int resultCount() const { return d.resultCount(); } + int progressValue() const { return d.progressValue(); } + int progressMinimum() const { return d.progressMinimum(); } + int progressMaximum() const { return d.progressMaximum(); } + QString progressText() const { return d.progressText(); } + void waitForFinished() { d.waitForFinished(); } + + inline T result() const; + inline T resultAt(int index) const; + bool isResultReadyAt(int resultIndex) const { return d.isResultReadyAt(resultIndex); } + + operator T() const { return result(); } + QList results() const { return d.results(); } + + class const_iterator + { + public: + typedef std::bidirectional_iterator_tag iterator_category; + typedef qptrdiff difference_type; + typedef T value_type; + typedef const T *pointer; + typedef const T &reference; + + inline const_iterator() {} + inline const_iterator(QFuture const * const _future, int _index) : future(_future), index(_index) {} + inline const_iterator(const const_iterator &o) : future(o.future), index(o.index) {} + inline const_iterator &operator=(const const_iterator &o) + { future = o.future; index = o.index; return *this; } + inline const T &operator*() const { return future->d.resultReference(index); } + inline const T *operator->() const { return future->d.resultPointer(index); } + + inline bool operator!=(const const_iterator &other) const + { + if (index == -1 && other.index == -1) // comparing end != end? + return false; + if (other.index == -1) + return (future->isRunning() || (index < future->resultCount())); + return (index != other.index); + } + + inline bool operator==(const const_iterator &o) const { return !operator!=(o); } + inline const_iterator &operator++() { ++index; return *this; } + inline const_iterator operator++(int) { const_iterator r = *this; ++index; return r; } + inline const_iterator &operator--() { --index; return *this; } + inline const_iterator operator--(int) { const_iterator r = *this; --index; return r; } + inline const_iterator operator+(int j) const { return const_iterator(future, index + j); } + inline const_iterator operator-(int j) const { return const_iterator(future, index - j); } + inline const_iterator &operator+=(int j) { index += j; return *this; } + inline const_iterator &operator-=(int j) { index -= j; return *this; } + private: + QFuture const * future; + int index; + }; + friend class const_iterator; + typedef const_iterator ConstIterator; + + const_iterator begin() const { return const_iterator(this, 0); } + const_iterator constBegin() const { return const_iterator(this, 0); } + const_iterator end() const { return const_iterator(this, -1); } + const_iterator constEnd() const { return const_iterator(this, -1); } + +private: + friend class QFutureWatcher; + +public: // Warning: the d pointer is not documented and is considered private. + mutable QFutureInterface d; +}; + +template +inline QFuture &QFuture::operator=(const QFuture &other) +{ + d = other.d; + return *this; +} + +template +inline T QFuture::result() const +{ + d.waitForResult(0); + return d.resultReference(0); +} + +template +inline T QFuture::resultAt(int index) const +{ + d.waitForResult(index); + return d.resultReference(index); +} + +template +inline QFuture QFutureInterface::future() +{ + return QFuture(this); +} + +Q_DECLARE_SEQUENTIAL_ITERATOR(Future) + +template <> +class QFuture +{ +public: + QFuture() + : d(QFutureInterface::canceledResult()) + { } + explicit QFuture(QFutureInterfaceBase *p) // internal + : d(*p) + { } + QFuture(const QFuture &other) + : d(other.d) + { } + ~QFuture() + { } + + QFuture &operator=(const QFuture &other); + bool operator==(const QFuture &other) const { return (d == other.d); } + bool operator!=(const QFuture &other) const { return (d != other.d); } + +#if !defined(Q_CC_XLC) + template + QFuture(const QFuture &other) + : d(other.d) + { } + + template + QFuture &operator=(const QFuture &other) + { + d = other.d; + return *this; + } +#endif + + void cancel() { d.cancel(); } + bool isCanceled() const { return d.isCanceled(); } + + void setPaused(bool paused) { d.setPaused(paused); } + bool isPaused() const { return d.isPaused(); } + void pause() { setPaused(true); } + void resume() { setPaused(false); } + void togglePaused() { d.togglePaused(); } + + bool isStarted() const { return d.isStarted(); } + bool isFinished() const { return d.isFinished(); } + bool isRunning() const { return d.isRunning(); } + + int resultCount() const { return d.resultCount(); } + int progressValue() const { return d.progressValue(); } + int progressMinimum() const { return d.progressMinimum(); } + int progressMaximum() const { return d.progressMaximum(); } + QString progressText() const { return d.progressText(); } + void waitForFinished() { d.waitForFinished(); } + +private: + friend class QFutureWatcher; + +#ifdef QFUTURE_TEST +public: +#endif + mutable QFutureInterfaceBase d; +}; + +inline QFuture &QFuture::operator=(const QFuture &other) +{ + d = other.d; + return *this; +} + +inline QFuture QFutureInterface::future() +{ + return QFuture(this); +} + +template +QFuture qToVoidFuture(const QFuture &future) +{ + return QFuture(future.d); +} + +QT_END_NAMESPACE +QT_END_HEADER + +#endif // QT_NO_CONCURRENT + +#endif // QFUTURE_H diff --git a/src/corelib/concurrent/qfutureinterface.cpp b/src/corelib/concurrent/qfutureinterface.cpp new file mode 100644 index 0000000000..c5bd802480 --- /dev/null +++ b/src/corelib/concurrent/qfutureinterface.cpp @@ -0,0 +1,565 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// qfutureinterface.h included from qfuture.h +#include "qfuture.h" + +#ifndef QT_NO_QFUTURE + +#include +#include +#include +#include + +#include "qfutureinterface_p.h" + +QT_BEGIN_NAMESPACE + +enum { + MaxProgressEmitsPerSecond = 25 +}; + +QFutureInterfaceBase::QFutureInterfaceBase(State initialState) + : d(new QFutureInterfaceBasePrivate(initialState)) +{ } + +QFutureInterfaceBase::QFutureInterfaceBase(const QFutureInterfaceBase &other) + : d(other.d) +{ + d->refCount.ref(); +} + +QFutureInterfaceBase::~QFutureInterfaceBase() +{ + if (!d->refCount.deref()) + delete d; +} + +void QFutureInterfaceBase::cancel() +{ + QMutexLocker locker(&d->m_mutex); + if (d->state & Canceled) + return; + + d->state = State((d->state & ~Paused) | Canceled); + d->waitCondition.wakeAll(); + d->pausedWaitCondition.wakeAll(); + d->sendCallOut(QFutureCallOutEvent(QFutureCallOutEvent::Canceled)); +} + +void QFutureInterfaceBase::setPaused(bool paused) +{ + QMutexLocker locker(&d->m_mutex); + if (paused) { + d->state = State(d->state | Paused); + d->sendCallOut(QFutureCallOutEvent(QFutureCallOutEvent::Paused)); + } else { + d->state = State(d->state & ~Paused); + d->pausedWaitCondition.wakeAll(); + d->sendCallOut(QFutureCallOutEvent(QFutureCallOutEvent::Resumed)); + } +} + +void QFutureInterfaceBase::togglePaused() +{ + QMutexLocker locker(&d->m_mutex); + if (d->state & Paused) { + d->state = State(d->state & ~Paused); + d->pausedWaitCondition.wakeAll(); + d->sendCallOut(QFutureCallOutEvent(QFutureCallOutEvent::Resumed)); + } else { + d->state = State(d->state | Paused); + d->sendCallOut(QFutureCallOutEvent(QFutureCallOutEvent::Paused)); + } +} + +void QFutureInterfaceBase::setThrottled(bool enable) +{ + // bail out if we are not changing the state + if ((enable && (d->state & Throttled)) || (!enable && !(d->state & Throttled))) + return; + + // lock and change the state + QMutexLocker lock(&d->m_mutex); + if (enable) { + d->state = State(d->state | Throttled); + } else { + d->state = State(d->state & ~Throttled); + if (!(d->state & Paused)) + d->pausedWaitCondition.wakeAll(); + } +} + + +bool QFutureInterfaceBase::isRunning() const +{ + return queryState(Running); +} + +bool QFutureInterfaceBase::isStarted() const +{ + return queryState(Started); +} + +bool QFutureInterfaceBase::isCanceled() const +{ + return queryState(Canceled); +} + +bool QFutureInterfaceBase::isFinished() const +{ + return queryState(Finished); +} + +bool QFutureInterfaceBase::isPaused() const +{ + return queryState(Paused); +} + +bool QFutureInterfaceBase::isThrottled() const +{ + return queryState(Throttled); +} + +bool QFutureInterfaceBase::isResultReadyAt(int index) const +{ + QMutexLocker lock(&d->m_mutex); + return d->internal_isResultReadyAt(index); +} + +bool QFutureInterfaceBase::waitForNextResult() +{ + QMutexLocker lock(&d->m_mutex); + return d->internal_waitForNextResult(); +} + +void QFutureInterfaceBase::waitForResume() +{ + // return early if possible to avoid taking the mutex lock. + if ((d->state & Paused) == false || (d->state & Canceled)) + return; + + QMutexLocker lock(&d->m_mutex); + if ((d->state & Paused) == false || (d->state & Canceled)) + return; + + // decrease active thread count since this thread will wait. + QThreadPool::globalInstance()->releaseThread(); + + d->pausedWaitCondition.wait(&d->m_mutex); + + QThreadPool::globalInstance()->reserveThread(); +} + +int QFutureInterfaceBase::progressValue() const +{ + return d->m_progressValue; +} + +int QFutureInterfaceBase::progressMinimum() const +{ + return d->m_progressMinimum; +} + +int QFutureInterfaceBase::progressMaximum() const +{ + return d->m_progressMaximum; +} + +int QFutureInterfaceBase::resultCount() const +{ + QMutexLocker lock(&d->m_mutex); + return d->internal_resultCount(); +} + +QString QFutureInterfaceBase::progressText() const +{ + QMutexLocker locker(&d->m_mutex); + return d->m_progressText; +} + +bool QFutureInterfaceBase::isProgressUpdateNeeded() const +{ + QMutexLocker locker(&d->m_mutex); + return !d->progressTime.isValid() || (d->progressTime.elapsed() > (1000 / MaxProgressEmitsPerSecond)); +} + +void QFutureInterfaceBase::reportStarted() +{ + QMutexLocker locker(&d->m_mutex); + if ((d->state & Started) || (d->state & Canceled) || (d->state & Finished)) + return; + + d->setState(State(Started | Running)); + d->sendCallOut(QFutureCallOutEvent(QFutureCallOutEvent::Started)); +} + +void QFutureInterfaceBase::reportCanceled() +{ + cancel(); +} + +#ifndef QT_NO_EXCEPTIONS +void QFutureInterfaceBase::reportException(const QtConcurrent::Exception &exception) +{ + QMutexLocker locker(&d->m_mutex); + if ((d->state & Canceled) || (d->state & Finished)) + return; + + d->m_exceptionStore.setException(exception); + d->state = State(d->state | Canceled); + d->waitCondition.wakeAll(); + d->pausedWaitCondition.wakeAll(); + d->sendCallOut(QFutureCallOutEvent(QFutureCallOutEvent::Canceled)); +} +#endif + +void QFutureInterfaceBase::reportFinished() +{ + QMutexLocker locker(&d->m_mutex); + if (!(d->state & Finished)) { + d->state = State((d->state & ~Running) | Finished); + d->waitCondition.wakeAll(); + d->sendCallOut(QFutureCallOutEvent(QFutureCallOutEvent::Finished)); + } +} + +void QFutureInterfaceBase::setExpectedResultCount(int resultCount) +{ + if (d->manualProgress == false) + setProgressRange(0, resultCount); + d->m_expectedResultCount = resultCount; +} + +int QFutureInterfaceBase::expectedResultCount() +{ + return d->m_expectedResultCount; +} + +bool QFutureInterfaceBase::queryState(State state) const +{ + return (d->state & state); +} + +void QFutureInterfaceBase::waitForResult(int resultIndex) +{ + d->m_exceptionStore.throwPossibleException(); + + if (!(d->state & Running)) + return; + + // To avoid deadlocks and reduce the number of threads used, try to + // run the runnable in the current thread. + QThreadPool::globalInstance()->d_func()->stealRunnable(d->runnable); + + QMutexLocker lock(&d->m_mutex); + + if (!(d->state & Running)) + return; + + const int waitIndex = (resultIndex == -1) ? INT_MAX : resultIndex; + while ((d->state & Running) && d->internal_isResultReadyAt(waitIndex) == false) + d->waitCondition.wait(&d->m_mutex); + + d->m_exceptionStore.throwPossibleException(); +} + +void QFutureInterfaceBase::waitForFinished() +{ + if (d->state & Running) { + QThreadPool::globalInstance()->d_func()->stealRunnable(d->runnable); + + QMutexLocker lock(&d->m_mutex); + + while (d->state & Running) + d->waitCondition.wait(&d->m_mutex); + } + + d->m_exceptionStore.throwPossibleException(); +} + +void QFutureInterfaceBase::reportResultsReady(int beginIndex, int endIndex) +{ + if ((d->state & Canceled) || (d->state & Finished) || beginIndex == endIndex) + return; + + d->waitCondition.wakeAll(); + + if (d->manualProgress == false) { + if (d->internal_updateProgress(d->m_progressValue + endIndex - beginIndex) == false) { + d->sendCallOut(QFutureCallOutEvent(QFutureCallOutEvent::ResultsReady, + beginIndex, + endIndex)); + return; + } + + d->sendCallOuts(QFutureCallOutEvent(QFutureCallOutEvent::Progress, + d->m_progressValue, + d->m_progressText), + QFutureCallOutEvent(QFutureCallOutEvent::ResultsReady, + beginIndex, + endIndex)); + return; + } + d->sendCallOut(QFutureCallOutEvent(QFutureCallOutEvent::ResultsReady, beginIndex, endIndex)); +} + +void QFutureInterfaceBase::setRunnable(QRunnable *runnable) +{ + d->runnable = runnable; +} + +void QFutureInterfaceBase::setFilterMode(bool enable) +{ + QMutexLocker locker(&d->m_mutex); + resultStoreBase().setFilterMode(enable); +} + +void QFutureInterfaceBase::setProgressRange(int minimum, int maximum) +{ + QMutexLocker locker(&d->m_mutex); + d->m_progressMinimum = minimum; + d->m_progressMaximum = maximum; + d->sendCallOut(QFutureCallOutEvent(QFutureCallOutEvent::ProgressRange, minimum, maximum)); +} + +void QFutureInterfaceBase::setProgressValue(int progressValue) +{ + setProgressValueAndText(progressValue, QString()); +} + +void QFutureInterfaceBase::setProgressValueAndText(int progressValue, + const QString &progressText) +{ + QMutexLocker locker(&d->m_mutex); + if (d->manualProgress == false) + d->manualProgress = true; + if (d->m_progressValue >= progressValue) + return; + + if ((d->state & Canceled) || (d->state & Finished)) + return; + + if (d->internal_updateProgress(progressValue, progressText)) { + d->sendCallOut(QFutureCallOutEvent(QFutureCallOutEvent::Progress, + d->m_progressValue, + d->m_progressText)); + } +} + +QMutex *QFutureInterfaceBase::mutex() const +{ + return &d->m_mutex; +} + +QtConcurrent::internal::ExceptionStore &QFutureInterfaceBase::exceptionStore() +{ + return d->m_exceptionStore; +} + +QtConcurrent::ResultStoreBase &QFutureInterfaceBase::resultStoreBase() +{ + return d->m_results; +} + +const QtConcurrent::ResultStoreBase &QFutureInterfaceBase::resultStoreBase() const +{ + return d->m_results; +} + +QFutureInterfaceBase &QFutureInterfaceBase::operator=(const QFutureInterfaceBase &other) +{ + other.d->refCount.ref(); + if (!d->refCount.deref()) + delete d; + d = other.d; + return *this; +} + +bool QFutureInterfaceBase::referenceCountIsOne() const +{ + return d->refCount == 1; +} + +QFutureInterfaceBasePrivate::QFutureInterfaceBasePrivate(QFutureInterfaceBase::State initialState) + : refCount(1), m_progressValue(0), m_progressMinimum(0), m_progressMaximum(0), + state(initialState), pendingResults(0), + manualProgress(false), m_expectedResultCount(0), runnable(0) +{ + progressTime.invalidate(); +} + +int QFutureInterfaceBasePrivate::internal_resultCount() const +{ + return m_results.count(); // ### subtract canceled results. +} + +bool QFutureInterfaceBasePrivate::internal_isResultReadyAt(int index) const +{ + return (m_results.contains(index)); +} + +bool QFutureInterfaceBasePrivate::internal_waitForNextResult() +{ + if (m_results.hasNextResult()) + return true; + + while ((state & QFutureInterfaceBase::Running) && m_results.hasNextResult() == false) + waitCondition.wait(&m_mutex); + + return (!(state & QFutureInterfaceBase::Canceled) && m_results.hasNextResult()); +} + +bool QFutureInterfaceBasePrivate::internal_updateProgress(int progress, + const QString &progressText) +{ + if (m_progressValue >= progress) + return false; + + m_progressValue = progress; + m_progressText = progressText; + + if (progressTime.isValid() && m_progressValue != m_progressMaximum) // make sure the first and last steps are emitted. + if (progressTime.elapsed() < (1000 / MaxProgressEmitsPerSecond)) + return false; + + progressTime.start(); + return true; +} + +void QFutureInterfaceBasePrivate::internal_setThrottled(bool enable) +{ + // bail out if we are not changing the state + if ((enable && (state & QFutureInterfaceBase::Throttled)) + || (!enable && !(state & QFutureInterfaceBase::Throttled))) + return; + + // change the state + if (enable) { + state = QFutureInterfaceBase::State(state | QFutureInterfaceBase::Throttled); + } else { + state = QFutureInterfaceBase::State(state & ~QFutureInterfaceBase::Throttled); + if (!(state & QFutureInterfaceBase::Paused)) + pausedWaitCondition.wakeAll(); + } +} + +void QFutureInterfaceBasePrivate::sendCallOut(const QFutureCallOutEvent &callOutEvent) +{ + if (outputConnections.isEmpty()) + return; + + for (int i = 0; i < outputConnections.count(); ++i) + outputConnections.at(i)->postCallOutEvent(callOutEvent); +} + +void QFutureInterfaceBasePrivate::sendCallOuts(const QFutureCallOutEvent &callOutEvent1, + const QFutureCallOutEvent &callOutEvent2) +{ + if (outputConnections.isEmpty()) + return; + + for (int i = 0; i < outputConnections.count(); ++i) { + QFutureCallOutInterface *interface = outputConnections.at(i); + interface->postCallOutEvent(callOutEvent1); + interface->postCallOutEvent(callOutEvent2); + } +} + +// This function connects an output interface (for example a QFutureWatcher) +// to this future. While holding the lock we check the state and ready results +// and add the appropriate callouts to the queue. In order to avoid deadlocks, +// the actual callouts are made at the end while not holding the lock. +void QFutureInterfaceBasePrivate::connectOutputInterface(QFutureCallOutInterface *interface) +{ + QMutexLocker locker(&m_mutex); + + if (state & QFutureInterfaceBase::Started) { + interface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::Started)); + interface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::ProgressRange, + m_progressMinimum, + m_progressMaximum)); + interface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::Progress, + m_progressValue, + m_progressText)); + } + + QtConcurrent::ResultIteratorBase it = m_results.begin(); + while (it != m_results.end()) { + const int begin = it.resultIndex(); + const int end = begin + it.batchSize(); + interface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::ResultsReady, + begin, + end)); + it.batchedAdvance(); + } + + if (state & QFutureInterfaceBase::Paused) + interface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::Paused)); + + if (state & QFutureInterfaceBase::Canceled) + interface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::Canceled)); + + if (state & QFutureInterfaceBase::Finished) + interface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::Finished)); + + outputConnections.append(interface); +} + +void QFutureInterfaceBasePrivate::disconnectOutputInterface(QFutureCallOutInterface *interface) +{ + QMutexLocker lock(&m_mutex); + const int index = outputConnections.indexOf(interface); + if (index == -1) + return; + outputConnections.removeAt(index); + + interface->callOutInterfaceDisconnected(); +} + +void QFutureInterfaceBasePrivate::setState(QFutureInterfaceBase::State newState) +{ + state = newState; +} + +QT_END_NAMESPACE + +#endif // QT_NO_CONCURRENT diff --git a/src/corelib/concurrent/qfutureinterface.h b/src/corelib/concurrent/qfutureinterface.h new file mode 100644 index 0000000000..7f90519a74 --- /dev/null +++ b/src/corelib/concurrent/qfutureinterface.h @@ -0,0 +1,313 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QFUTUREINTERFACE_H +#define QFUTUREINTERFACE_H + +#include +#include + +#ifndef QT_NO_QFUTURE + +#include +#include +#include + +QT_BEGIN_HEADER +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +template class QFuture; +class QFutureInterfaceBasePrivate; +class QFutureWatcherBase; +class QFutureWatcherBasePrivate; + +class Q_CORE_EXPORT QFutureInterfaceBase +{ +public: + enum State { + NoState = 0x00, + Running = 0x01, + Started = 0x02, + Finished = 0x04, + Canceled = 0x08, + Paused = 0x10, + Throttled = 0x20 + }; + + QFutureInterfaceBase(State initialState = NoState); + QFutureInterfaceBase(const QFutureInterfaceBase &other); + virtual ~QFutureInterfaceBase(); + + // reporting functions available to the engine author: + void reportStarted(); + void reportFinished(); + void reportCanceled(); +#ifndef QT_NO_EXCEPTIONS + void reportException(const QtConcurrent::Exception &e); +#endif + void reportResultsReady(int beginIndex, int endIndex); + + void setRunnable(QRunnable *runnable); + void setFilterMode(bool enable); + void setProgressRange(int minimum, int maximum); + int progressMinimum() const; + int progressMaximum() const; + bool isProgressUpdateNeeded() const; + void setProgressValue(int progressValue); + int progressValue() const; + void setProgressValueAndText(int progressValue, const QString &progressText); + QString progressText() const; + + void setExpectedResultCount(int resultCount); + int expectedResultCount(); + int resultCount() const; + + bool queryState(State state) const; + bool isRunning() const; + bool isStarted() const; + bool isCanceled() const; + bool isFinished() const; + bool isPaused() const; + bool isThrottled() const; + bool isResultReadyAt(int index) const; + + void cancel(); + void setPaused(bool paused); + void togglePaused(); + void setThrottled(bool enable); + + void waitForFinished(); + bool waitForNextResult(); + void waitForResult(int resultIndex); + void waitForResume(); + + QMutex *mutex() const; + QtConcurrent::internal::ExceptionStore &exceptionStore(); + QtConcurrent::ResultStoreBase &resultStoreBase(); + const QtConcurrent::ResultStoreBase &resultStoreBase() const; + + inline bool operator==(const QFutureInterfaceBase &other) const { return d == other.d; } + inline bool operator!=(const QFutureInterfaceBase &other) const { return d != other.d; } + QFutureInterfaceBase &operator=(const QFutureInterfaceBase &other); + +protected: + bool referenceCountIsOne() const; +public: + +#ifndef QFUTURE_TEST +private: +#endif + QFutureInterfaceBasePrivate *d; + +private: + friend class QFutureWatcherBase; + friend class QFutureWatcherBasePrivate; +}; + +template +class QFutureInterface : public QFutureInterfaceBase +{ +public: + QFutureInterface(State initialState = NoState) + : QFutureInterfaceBase(initialState) + { } + QFutureInterface(const QFutureInterface &other) + : QFutureInterfaceBase(other) + { } + ~QFutureInterface() + { + if (referenceCountIsOne()) + resultStore().clear(); + } + + static QFutureInterface canceledResult() + { return QFutureInterface(State(Started | Finished | Canceled)); } + + QFutureInterface &operator=(const QFutureInterface &other) + { + if (referenceCountIsOne()) + resultStore().clear(); + QFutureInterfaceBase::operator=(other); + return *this; + } + + inline QFuture future(); // implemented in qfuture.h + + inline void reportResult(const T *result, int index = -1); + inline void reportResult(const T &result, int index = -1); + inline void reportResults(const QVector &results, int beginIndex = -1, int count = -1); + inline void reportFinished(const T *result = 0); + + inline const T &resultReference(int index) const; + inline const T *resultPointer(int index) const; + inline QList results(); +private: + QtConcurrent::ResultStore &resultStore() + { return static_cast &>(resultStoreBase()); } + const QtConcurrent::ResultStore &resultStore() const + { return static_cast &>(resultStoreBase()); } +}; + +template +inline void QFutureInterface::reportResult(const T *result, int index) +{ + QMutexLocker locker(mutex()); + if (this->queryState(Canceled) || this->queryState(Finished)) { + return; + } + + QtConcurrent::ResultStore &store = resultStore(); + + + if (store.filterMode()) { + const int resultCountBefore = store.count(); + store.addResult(index, result); + this->reportResultsReady(resultCountBefore, resultCountBefore + store.count()); + } else { + const int insertIndex = store.addResult(index, result); + this->reportResultsReady(insertIndex, insertIndex + 1); + } +} + +template +inline void QFutureInterface::reportResult(const T &result, int index) +{ + reportResult(&result, index); +} + +template +inline void QFutureInterface::reportResults(const QVector &_results, int beginIndex, int count) +{ + QMutexLocker locker(mutex()); + if (this->queryState(Canceled) || this->queryState(Finished)) { + return; + } + + QtConcurrent::ResultStore &store = resultStore(); + + if (store.filterMode()) { + const int resultCountBefore = store.count(); + store.addResults(beginIndex, &_results, count); + this->reportResultsReady(resultCountBefore, store.count()); + } else { + const int insertIndex = store.addResults(beginIndex, &_results, count); + this->reportResultsReady(insertIndex, insertIndex + _results.count()); + } +} + +template +inline void QFutureInterface::reportFinished(const T *result) +{ + if (result) + reportResult(result); + QFutureInterfaceBase::reportFinished(); +} + +template +inline const T &QFutureInterface::resultReference(int index) const +{ + QMutexLocker lock(mutex()); + return resultStore().resultAt(index).value(); +} + +template +inline const T *QFutureInterface::resultPointer(int index) const +{ + QMutexLocker lock(mutex()); + return resultStore().resultAt(index).pointer(); +} + +template +inline QList QFutureInterface::results() +{ + if (this->isCanceled()) { + exceptionStore().throwPossibleException(); + return QList(); + } + QFutureInterfaceBase::waitForResult(-1); + + QList res; + QMutexLocker lock(mutex()); + + QtConcurrent::ResultIterator it = resultStore().begin(); + while (it != resultStore().end()) { + res.append(it.value()); + ++it; + } + + return res; +} + +template <> +class QFutureInterface : public QFutureInterfaceBase +{ +public: + QFutureInterface(State initialState = NoState) + : QFutureInterfaceBase(initialState) + { } + QFutureInterface(const QFutureInterface &other) + : QFutureInterfaceBase(other) + { } + + static QFutureInterface canceledResult() + { return QFutureInterface(State(Started | Finished | Canceled)); } + + QFutureInterface &operator=(const QFutureInterface &other) + { + QFutureInterfaceBase::operator=(other); + return *this; + } + + inline QFuture future(); // implemented in qfuture.h + + void reportResult(const void *, int) { } + void reportResults(const QVector &, int) { } + void reportFinished(void * = 0) { QFutureInterfaceBase::reportFinished(); } +}; + +QT_END_NAMESPACE +QT_END_HEADER + +#endif // QT_NO_CONCURRENT + +#endif // QFUTUREINTERFACE_H diff --git a/src/corelib/concurrent/qfutureinterface_p.h b/src/corelib/concurrent/qfutureinterface_p.h new file mode 100644 index 0000000000..538947ead0 --- /dev/null +++ b/src/corelib/concurrent/qfutureinterface_p.h @@ -0,0 +1,167 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QFUTUREINTERFACE_P_H +#define QFUTUREINTERFACE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QFutureCallOutEvent : public QEvent +{ +public: + enum CallOutType { + Started, + Finished, + Canceled, + Paused, + Resumed, + Progress, + ProgressRange, + ResultsReady + }; + + QFutureCallOutEvent() + : QEvent(QEvent::FutureCallOut), callOutType(CallOutType(0)), index1(-1), index2(-1) + { } + QFutureCallOutEvent(CallOutType callOutType, int index1 = -1) + : QEvent(QEvent::FutureCallOut), callOutType(callOutType), index1(index1), index2(-1) + { } + QFutureCallOutEvent(CallOutType callOutType, int index1, int index2) + : QEvent(QEvent::FutureCallOut), callOutType(callOutType), index1(index1), index2(index2) + { } + + QFutureCallOutEvent(CallOutType callOutType, int index1, const QString &text) + : QEvent(QEvent::FutureCallOut), + callOutType(callOutType), + index1(index1), + index2(-1), + text(text) + { } + + CallOutType callOutType; + int index1; + int index2; + QString text; + + QFutureCallOutEvent *clone() const + { + return new QFutureCallOutEvent(callOutType, index1, index2, text); + } + +private: + QFutureCallOutEvent(CallOutType callOutType, + int index1, + int index2, + const QString &text) + : QEvent(QEvent::FutureCallOut), + callOutType(callOutType), + index1(index1), + index2(index2), + text(text) + { } +}; + +class QFutureCallOutInterface +{ +public: + virtual ~QFutureCallOutInterface() {} + virtual void postCallOutEvent(const QFutureCallOutEvent &) = 0; + virtual void callOutInterfaceDisconnected() = 0; +}; + +class QFutureInterfaceBasePrivate +{ +public: + QFutureInterfaceBasePrivate(QFutureInterfaceBase::State initialState); + + QAtomicInt refCount; + mutable QMutex m_mutex; + QWaitCondition waitCondition; + QList outputConnections; + int m_progressValue; + int m_progressMinimum; + int m_progressMaximum; + QFutureInterfaceBase::State state; + QElapsedTimer progressTime; + QWaitCondition pausedWaitCondition; + int pendingResults; + QtConcurrent::ResultStoreBase m_results; + bool manualProgress; + int m_expectedResultCount; + QtConcurrent::internal::ExceptionStore m_exceptionStore; + QString m_progressText; + QRunnable *runnable; + + // Internal functions that does not change the mutex state. + // The mutex must be locked when calling these. + int internal_resultCount() const; + bool internal_isResultReadyAt(int index) const; + bool internal_waitForNextResult(); + bool internal_updateProgress(int progress, const QString &progressText = QString()); + void internal_setThrottled(bool enable); + void sendCallOut(const QFutureCallOutEvent &callOut); + void sendCallOuts(const QFutureCallOutEvent &callOut1, const QFutureCallOutEvent &callOut2); + void connectOutputInterface(QFutureCallOutInterface *iface); + void disconnectOutputInterface(QFutureCallOutInterface *iface); + + void setState(QFutureInterfaceBase::State state); +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/corelib/concurrent/qfuturesynchronizer.cpp b/src/corelib/concurrent/qfuturesynchronizer.cpp new file mode 100644 index 0000000000..b52e5fb73d --- /dev/null +++ b/src/corelib/concurrent/qfuturesynchronizer.cpp @@ -0,0 +1,156 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! \class QFutureSynchronizer + \since 4.4 + + \brief The QFutureSynchronizer class is a convenience class that simplifies + QFuture synchronization. + + \ingroup thread + + QFutureSynchronizer is a template class that simplifies synchronization of + one or more QFuture objects. Futures are added using the addFuture() or + setFuture() functions. The futures() function returns a list of futures. + Use clearFutures() to remove all futures from the QFutureSynchronizer. + + The waitForFinished() function waits for all futures to finish. + The destructor of QFutureSynchronizer calls waitForFinished(), providing + an easy way to ensure that all futures have finished before returning from + a function: + + \snippet doc/src/snippets/code/src_corelib_concurrent_qfuturesynchronizer.cpp 0 + + The behavior of waitForFinished() can be changed using the + setCancelOnWait() function. Calling setCancelOnWait(true) will cause + waitForFinished() to cancel all futures before waiting for them to finish. + You can query the status of the cancel-on-wait feature using the + cancelOnWait() function. + + \sa QFuture, QFutureWatcher, {Concurrent Programming}{Qt Concurrent} +*/ + +/*! + \fn QFutureSynchronizer::QFutureSynchronizer() + + Constructs a QFutureSynchronizer. +*/ + +/*! + \fn QFutureSynchronizer::QFutureSynchronizer(const QFuture &future) + + Constructs a QFutureSynchronizer and begins watching \a future by calling + addFuture(). + + \sa addFuture() +*/ + +/*! + \fn QFutureSynchronizer::~QFutureSynchronizer() + + Calls waitForFinished() function to ensure that all futures have finished + before destroying this QFutureSynchronizer. + + \sa waitForFinished() +*/ + +/*! + \fn void QFutureSynchronizer::setFuture(const QFuture &future) + + Sets \a future to be the only future managed by this QFutureSynchronizer. + This is a convenience function that calls waitForFinished(), + then clearFutures(), and finally passes \a future to addFuture(). + + \sa addFuture(), waitForFinished(), clearFutures() +*/ + +/*! + \fn void QFutureSynchronizer::addFuture(const QFuture &future) + + Adds \a future to the list of managed futures. + + \sa futures() +*/ + +/*! + \fn void QFutureSynchronizer::waitForFinished() + + Waits for all futures to finish. If cancelOnWait() returns true, each + future is canceled before waiting for them to finish. + + \sa cancelOnWait(), setCancelOnWait() +*/ + +/*! + \fn void QFutureSynchronizer::clearFutures() + + Removes all managed futures from this QFutureSynchronizer. + + \sa addFuture(), setFuture() +*/ + +/*! + \fn QList > QFutureSynchronizer::futures() const + + Returns a list of all managed futures. + + \sa addFuture(), setFuture() +*/ + +/*! + \fn void QFutureSynchronizer::setCancelOnWait(bool enabled) + + Enables or disables the cancel-on-wait feature based on the \a enabled + argument. If \a enabled is true, the waitForFinished() function will cancel + all futures before waiting for them to finish. + + \sa waitForFinished() +*/ + +/*! + \fn bool QFutureSynchronizer::cancelOnWait() const + + Returns true if the cancel-on-wait feature is enabled; otherwise returns + false. If cancel-on-wait is enabled, the waitForFinished() function will + cancel all futures before waiting for them to finish. + + \sa waitForFinished() +*/ diff --git a/src/corelib/concurrent/qfuturesynchronizer.h b/src/corelib/concurrent/qfuturesynchronizer.h new file mode 100644 index 0000000000..23a2c242aa --- /dev/null +++ b/src/corelib/concurrent/qfuturesynchronizer.h @@ -0,0 +1,121 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QFUTRUESYNCHRONIZER_H +#define QFUTRUESYNCHRONIZER_H + +#include + +#ifndef QT_NO_CONCURRENT + +QT_BEGIN_HEADER +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +template +class QFutureSynchronizer +{ + Q_DISABLE_COPY(QFutureSynchronizer) + +public: + QFutureSynchronizer() : m_cancelOnWait(false) { } + explicit QFutureSynchronizer(const QFuture &future) + : m_cancelOnWait(false) + { addFuture(future); } + ~QFutureSynchronizer() { waitForFinished(); } + + void setFuture(const QFuture &future) + { + waitForFinished(); + m_futures.clear(); + addFuture(future); + } + + void addFuture(const QFuture &future) + { + m_futures.append(future); + } + + void waitForFinished() + { + if (m_cancelOnWait) { + for (int i = 0; i < m_futures.count(); ++i) { + m_futures[i].cancel(); + } + } + + for (int i = 0; i < m_futures.count(); ++i) { + m_futures[i].waitForFinished(); + } + } + + void clearFutures() + { + m_futures.clear(); + } + + QList > futures() const + { + return m_futures; + } + + void setCancelOnWait(bool enabled) + { + m_cancelOnWait = enabled; + } + + bool cancelOnWait() const + { + return m_cancelOnWait; + } + +protected: + QList > m_futures; + bool m_cancelOnWait; +}; + +QT_END_NAMESPACE +QT_END_HEADER + +#endif // QT_NO_CONCURRENT + +#endif // QFUTRUESYNCHRONIZER_H diff --git a/src/corelib/concurrent/qfuturewatcher.cpp b/src/corelib/concurrent/qfuturewatcher.cpp new file mode 100644 index 0000000000..ea12bc9302 --- /dev/null +++ b/src/corelib/concurrent/qfuturewatcher.cpp @@ -0,0 +1,592 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qfuturewatcher.h" + +#ifndef QT_NO_QFUTURE + +#include +#include +#include + +#include "qfuturewatcher_p.h" + +QT_BEGIN_NAMESPACE + +/*! \class QFutureWatcher + \reentrant + \since 4.4 + + \ingroup thread + + \brief The QFutureWatcher class allows monitoring a QFuture using signals + and slots. + + QFutureWatcher provides information and notifications about a QFuture. Use + the setFuture() function to start watching a particular QFuture. The + future() function returns the future set with setFuture(). + + For convenience, several of QFuture's functions are also available in + QFutureWatcher: progressValue(), progressMinimum(), progressMaximum(), + progressText(), isStarted(), isFinished(), isRunning(), isCanceled(), + isPaused(), waitForFinished(), result(), and resultAt(). The cancel(), + setPaused(), pause(), resume(), and togglePaused() functions are slots in + QFutureWatcher. + + Status changes are reported via the started(), finished(), canceled(), + paused(), resumed(), resultReadyAt(), and resultsReadyAt() signals. + Progress information is provided from the progressRangeChanged(), + void progressValueChanged(), and progressTextChanged() signals. + + Throttling control is provided by the setPendingResultsLimit() function. + When the number of pending resultReadyAt() or resultsReadyAt() signals + exceeds the limit, the computation represented by the future will be + throttled automatically. The computation will resume once the number of + pending signals drops below the limit. + + Example: Starting a computation and getting a slot callback when it's + finished: + + \snippet doc/src/snippets/code/src_corelib_concurrent_qfuturewatcher.cpp 0 + + Be aware that not all asynchronous computations can be canceled or paused. + For example, the future returned by QtConcurrent::run() cannot be + canceled; but the future returned by QtConcurrent::mappedReduced() can. + + QFutureWatcher is specialized to not contain any of the result + fetching functions. Any QFuture can be watched by a + QFutureWatcher as well. This is useful if only status or progress + information is needed; not the actual result data. + + \sa QFuture, {Concurrent Programming}{Qt Concurrent} +*/ + +/*! \fn QFutureWatcher::QFutureWatcher(QObject *parent) + + Constructs a new QFutureWatcher with the given \a parent. +*/ +QFutureWatcherBase::QFutureWatcherBase(QObject *parent) + :QObject(*new QFutureWatcherBasePrivate, parent) +{ } + +/*! \fn QFutureWatcher::~QFutureWatcher() + + Destroys the QFutureWatcher. +*/ + +/*! \fn void QFutureWatcher::cancel() + + Cancels the asynchronous computation represented by the future(). Note that + the cancelation is asynchronous. Use waitForFinished() after calling + cancel() when you need synchronous cancelation. + + Currently available results may still be accessed on a canceled QFuture, + but new results will \e not become available after calling this function. + Also, this QFutureWatcher will not deliver progress and result ready + signals once canceled. This includes the progressValueChanged(), + progressRangeChanged(), progressTextChanged(), resultReadyAt(), and + resultsReadyAt() signals. + + Be aware that not all asynchronous computations can be canceled. For + example, the QFuture returned by QtConcurrent::run() cannot be canceled; + but the QFuture returned by QtConcurrent::mappedReduced() can. +*/ +void QFutureWatcherBase::cancel() +{ + futureInterface().cancel(); +} + +/*! \fn void QFutureWatcher::setPaused(bool paused) + + If \a paused is true, this function pauses the asynchronous computation + represented by the future(). If the computation is already paused, this + function does nothing. This QFutureWatcher will stop delivering progress + and result ready signals while the future is paused. Signal delivery will + continue once the computation is resumed. + + If \a paused is false, this function resumes the asynchronous computation. + If the computation was not previously paused, this function does nothing. + + Be aware that not all computations can be paused. For example, the + QFuture returned by QtConcurrent::run() cannot be paused; but the QFuture + returned by QtConcurrent::mappedReduced() can. + + \sa pause(), resume(), togglePaused() +*/ +void QFutureWatcherBase::setPaused(bool paused) +{ + futureInterface().setPaused(paused); +} + +/*! \fn void QFutureWatcher::pause() + + Pauses the asynchronous computation represented by the future(). This is a + convenience method that simply calls setPaused(true). + + \sa resume() +*/ +void QFutureWatcherBase::pause() +{ + futureInterface().setPaused(true); +} + +/*! \fn void QFutureWatcher::resume() + + Resumes the asynchronous computation represented by the future(). This is + a convenience method that simply calls setPaused(false). + + \sa pause() +*/ +void QFutureWatcherBase::resume() +{ + futureInterface().setPaused(false); +} + +/*! \fn void QFutureWatcher::togglePaused() + + Toggles the paused state of the asynchronous computation. In other words, + if the computation is currently paused, calling this function resumes it; + if the computation is running, it becomes paused. This is a convenience + method for calling setPaused(!isPaused()). + + \sa setPaused(), pause(), resume() +*/ +void QFutureWatcherBase::togglePaused() +{ + futureInterface().togglePaused(); +} + +/*! \fn int QFutureWatcher::progressValue() const + + Returns the current progress value, which is between the progressMinimum() + and progressMaximum(). + + \sa progressMinimum(), progressMaximum() +*/ +int QFutureWatcherBase::progressValue() const +{ + return futureInterface().progressValue(); +} + +/*! \fn int QFutureWatcher::progressMinimum() const + + Returns the minimum progressValue(). + + \sa progressValue(), progressMaximum() +*/ +int QFutureWatcherBase::progressMinimum() const +{ + return futureInterface().progressMinimum(); +} + +/*! \fn int QFutureWatcher::progressMaximum() const + + Returns the maximum progressValue(). + + \sa progressValue(), progressMinimum() +*/ +int QFutureWatcherBase::progressMaximum() const +{ + return futureInterface().progressMaximum(); +} + +/*! \fn QString QFutureWatcher::progressText() const + + Returns the (optional) textual representation of the progress as reported + by the asynchronous computation. + + Be aware that not all computations provide a textual representation of the + progress, and as such, this function may return an empty string. +*/ +QString QFutureWatcherBase::progressText() const +{ + return futureInterface().progressText(); +} + +/*! \fn bool QFutureWatcher::isStarted() const + + Returns true if the asynchronous computation represented by the future() + has been started; otherwise returns false. +*/ +bool QFutureWatcherBase::isStarted() const +{ + return futureInterface().queryState(QFutureInterfaceBase::Started); +} + +/*! \fn bool QFutureWatcher::isFinished() const + + Returns true if the asynchronous computation represented by the future() + has finished; otherwise returns false. +*/ +bool QFutureWatcherBase::isFinished() const +{ + Q_D(const QFutureWatcherBase); + return d->finished; +} + +/*! \fn bool QFutureWatcher::isRunning() const + + Returns true if the asynchronous computation represented by the future() + is currently running; otherwise returns false. +*/ +bool QFutureWatcherBase::isRunning() const +{ + return futureInterface().queryState(QFutureInterfaceBase::Running); +} + +/*! \fn bool QFutureWatcher::isCanceled() const + + Returns true if the asynchronous computation has been canceled with the + cancel() function; otherwise returns false. + + Be aware that the computation may still be running even though this + function returns true. See cancel() for more details. +*/ +bool QFutureWatcherBase::isCanceled() const +{ + return futureInterface().queryState(QFutureInterfaceBase::Canceled); +} + +/*! \fn bool QFutureWatcher::isPaused() const + + Returns true if the asynchronous computation has been paused with the + pause() function; otherwise returns false. + + Be aware that the computation may still be running even though this + function returns true. See setPaused() for more details. + + \sa setPaused(), togglePaused() +*/ +bool QFutureWatcherBase::isPaused() const +{ + return futureInterface().queryState(QFutureInterfaceBase::Paused); +} + +/*! \fn void QFutureWatcher::waitForFinished() + + Waits for the asynchronous computation to finish (including cancel()ed + computations). +*/ +void QFutureWatcherBase::waitForFinished() +{ + futureInterface().waitForFinished(); +} + +/*! \fn void QFutureWatcher::setPendingResultsLimit(int limit) + + The setPendingResultsLimit() provides throttling control. When the number + of pending resultReadyAt() or resultsReadyAt() signals exceeds the + \a limit, the computation represented by the future will be throttled + automatically. The computation will resume once the number of pending + signals drops below the \a limit. +*/ + +bool QFutureWatcherBase::event(QEvent *event) +{ + Q_D(QFutureWatcherBase); + if (event->type() == QEvent::FutureCallOut) { + QFutureCallOutEvent *callOutEvent = static_cast(event); + + if (futureInterface().isPaused()) { + d->pendingCallOutEvents.append(callOutEvent->clone()); + return true; + } + + if (callOutEvent->callOutType == QFutureCallOutEvent::Resumed + && !d->pendingCallOutEvents.isEmpty()) { + // send the resume + d->sendCallOutEvent(callOutEvent); + + // next send all pending call outs + for (int i = 0; i < d->pendingCallOutEvents.count(); ++i) + d->sendCallOutEvent(d->pendingCallOutEvents.at(i)); + qDeleteAll(d->pendingCallOutEvents); + d->pendingCallOutEvents.clear(); + } else { + d->sendCallOutEvent(callOutEvent); + } + return true; + } + return QObject::event(event); +} + +void QFutureWatcherBase::setPendingResultsLimit(int limit) +{ + Q_D(QFutureWatcherBase); + d->maximumPendingResultsReady = limit; +} + +void QFutureWatcherBase::connectNotify(const char * signal) +{ + Q_D(QFutureWatcherBase); + if (qstrcmp(signal, SIGNAL(resultReadyAt(int))) == 0) + d->resultAtConnected.ref(); +#ifndef QT_NO_DEBUG + if (qstrcmp(signal, SIGNAL(finished())) == 0) { + if (futureInterface().isRunning()) { + //connections should be established before calling stFuture to avoid race. + // (The future could finish before the connection is made.) + qWarning("QFutureWatcher::connect: connecting after calling setFuture() is likely to produce race"); + } + } +#endif +} + +void QFutureWatcherBase::disconnectNotify(const char * signal) +{ + Q_D(QFutureWatcherBase); + if (qstrcmp(signal, SIGNAL(resultReadyAt(int))) == 0) + d->resultAtConnected.deref(); +} + +/*! + \internal +*/ +QFutureWatcherBasePrivate::QFutureWatcherBasePrivate() + : maximumPendingResultsReady(QThread::idealThreadCount() * 2), + resultAtConnected(0) +{ } + +/*! + \internal +*/ +void QFutureWatcherBase::connectOutputInterface() +{ + futureInterface().d->connectOutputInterface(d_func()); +} + +/*! + \internal +*/ +void QFutureWatcherBase::disconnectOutputInterface(bool pendingAssignment) +{ + if (pendingAssignment) { + Q_D(QFutureWatcherBase); + d->pendingResultsReady = 0; + qDeleteAll(d->pendingCallOutEvents); + d->pendingCallOutEvents.clear(); + d->finished = false; + } + + futureInterface().d->disconnectOutputInterface(d_func()); +} + +void QFutureWatcherBasePrivate::postCallOutEvent(const QFutureCallOutEvent &callOutEvent) +{ + Q_Q(QFutureWatcherBase); + + if (callOutEvent.callOutType == QFutureCallOutEvent::ResultsReady) { + if (pendingResultsReady.fetchAndAddRelaxed(1) >= maximumPendingResultsReady) + q->futureInterface().d->internal_setThrottled(true); + } + + QCoreApplication::postEvent(q, callOutEvent.clone()); +} + +void QFutureWatcherBasePrivate::callOutInterfaceDisconnected() +{ + QCoreApplication::removePostedEvents(q_func(), QEvent::FutureCallOut); +} + +void QFutureWatcherBasePrivate::sendCallOutEvent(QFutureCallOutEvent *event) +{ + Q_Q(QFutureWatcherBase); + + switch (event->callOutType) { + case QFutureCallOutEvent::Started: + emit q->started(); + break; + case QFutureCallOutEvent::Finished: + finished = true; + emit q->finished(); + break; + case QFutureCallOutEvent::Canceled: + pendingResultsReady = 0; + emit q->canceled(); + break; + case QFutureCallOutEvent::Paused: + if (q->futureInterface().isCanceled()) + break; + emit q->paused(); + break; + case QFutureCallOutEvent::Resumed: + if (q->futureInterface().isCanceled()) + break; + emit q->resumed(); + break; + case QFutureCallOutEvent::ResultsReady: { + if (q->futureInterface().isCanceled()) + break; + + if (pendingResultsReady.fetchAndAddRelaxed(-1) <= maximumPendingResultsReady) + q->futureInterface().setThrottled(false); + + const int beginIndex = event->index1; + const int endIndex = event->index2; + + emit q->resultsReadyAt(beginIndex, endIndex); + + if (int(resultAtConnected) <= 0) + break; + + for (int i = beginIndex; i < endIndex; ++i) + emit q->resultReadyAt(i); + + } break; + case QFutureCallOutEvent::Progress: + if (q->futureInterface().isCanceled()) + break; + + emit q->progressValueChanged(event->index1); + if (!event->text.isNull()) // ### + q->progressTextChanged(event->text); + break; + case QFutureCallOutEvent::ProgressRange: + emit q->progressRangeChanged(event->index1, event->index2); + break; + default: break; + } +} + + +/*! \fn const T &QFutureWatcher::result() const + + Returns the first result in the future(). If the result is not immediately + available, this function will block and wait for the result to become + available. This is a convenience method for calling resultAt(0). + + \sa resultAt() +*/ + +/*! \fn const T &QFutureWatcher::resultAt(int index) const + + Returns the result at \a index in the future(). If the result is not + immediately available, this function will block and wait for the result to + become available. + + \sa result() +*/ + +/*! \fn void QFutureWatcher::setFuture(const QFuture &future) + + Starts watching the given \a future. + + One of the signals might be emitted for the current state of the + \a future. For example, if the future is already stopped, the + finished signal will be emitted. + + To avoid a race condition, it is important to call this function + \e after doing the connections. +*/ + +/*! \fn QFuture QFutureWatcher::future() const + + Returns the watched future. +*/ + +/*! \fn void QFutureWatcher::started() + + This signal is emitted when this QFutureWatcher starts watching the future + set with setFuture(). +*/ + +/*! + \fn void QFutureWatcher::finished() + This signal is emitted when the watched future finishes. +*/ + +/*! + \fn void QFutureWatcher::canceled() + This signal is emitted if the watched future is canceled. +*/ + +/*! \fn void QFutureWatcher::paused() + This signal is emitted when the watched future is paused. +*/ + +/*! \fn void QFutureWatcher::resumed() + This signal is emitted when the watched future is resumed. +*/ + +/*! + \fn void QFutureWatcher::progressRangeChanged(int minimum, int maximum) + + The progress range for the watched future has changed to \a minimum and + \a maximum +*/ + +/*! + \fn void QFutureWatcher::progressValueChanged(int progressValue) + + This signal is emitted when the watched future reports progress, + \a progressValue gives the current progress. In order to avoid overloading + the GUI event loop, QFutureWatcher limits the progress signal emission + rate. This means that listeners connected to this slot might not get all + progress reports the future makes. The last progress update (where + \a progressValue equals the maximum value) will always be delivered. +*/ + +/*! \fn void QFutureWatcher::progressTextChanged(const QString &progressText) + + This signal is emitted when the watched future reports textual progress + information, \a progressText. +*/ + +/*! + \fn void QFutureWatcher::resultReadyAt(int index) + + This signal is emitted when the watched future reports a ready result at + \a index. If the future reports multiple results, the index will indicate + which one it is. Results can be reported out-of-order. To get the result, + call future().result(index); +*/ + +/*! + \fn void QFutureWatcher::resultsReadyAt(int beginIndex, int endIndex); + + This signal is emitted when the watched future reports ready results. + The results are indexed from \a beginIndex to \a endIndex. + +*/ + +QT_END_NAMESPACE + +#endif // QT_NO_CONCURRENT diff --git a/src/corelib/concurrent/qfuturewatcher.h b/src/corelib/concurrent/qfuturewatcher.h new file mode 100644 index 0000000000..5fe2007a01 --- /dev/null +++ b/src/corelib/concurrent/qfuturewatcher.h @@ -0,0 +1,222 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QFUTUREWATCHER_H +#define QFUTUREWATCHER_H + +#include + +#ifndef QT_NO_QFUTURE + +#include + +QT_BEGIN_HEADER +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +class QEvent; + +class QFutureWatcherBasePrivate; +class Q_CORE_EXPORT QFutureWatcherBase : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QFutureWatcherBase) + +public: + QFutureWatcherBase(QObject *parent = 0); + + int progressValue() const; + int progressMinimum() const; + int progressMaximum() const; + QString progressText() const; + + bool isStarted() const; + bool isFinished() const; + bool isRunning() const; + bool isCanceled() const; + bool isPaused() const; + + void waitForFinished(); + + void setPendingResultsLimit(int limit); + + bool event(QEvent *event); + +Q_SIGNALS: + void started(); + void finished(); + void canceled(); + void paused(); + void resumed(); + void resultReadyAt(int resultIndex); + void resultsReadyAt(int beginIndex, int endIndex); + void progressRangeChanged(int minimum, int maximum); + void progressValueChanged(int progressValue); + void progressTextChanged(const QString &progressText); + +public Q_SLOTS: + void cancel(); + void setPaused(bool paused); + void pause(); + void resume(); + void togglePaused(); + +protected: + void connectNotify (const char * signal); + void disconnectNotify (const char * signal); + + // called from setFuture() implemented in template sub-classes + void connectOutputInterface(); + void disconnectOutputInterface(bool pendingAssignment = false); + +private: + // implemented in the template sub-classes + virtual const QFutureInterfaceBase &futureInterface() const = 0; + virtual QFutureInterfaceBase &futureInterface() = 0; +}; + +template +class QFutureWatcher : public QFutureWatcherBase +{ +public: + QFutureWatcher(QObject *_parent = 0) + : QFutureWatcherBase(_parent) + { } + ~QFutureWatcher() + { disconnectOutputInterface(); } + + void setFuture(const QFuture &future); + QFuture future() const + { return m_future; } + + T result() const { return m_future.result(); } + T resultAt(int index) const { return m_future.resultAt(index); } + +#ifdef qdoc + int progressValue() const; + int progressMinimum() const; + int progressMaximum() const; + QString progressText() const; + + bool isStarted() const; + bool isFinished() const; + bool isRunning() const; + bool isCanceled() const; + bool isPaused() const; + + void waitForFinished(); + + void setPendingResultsLimit(int limit); + +Q_SIGNALS: + void started(); + void finished(); + void canceled(); + void paused(); + void resumed(); + void resultReadyAt(int resultIndex); + void resultsReadyAt(int beginIndex, int endIndex); + void progressRangeChanged(int minimum, int maximum); + void progressValueChanged(int progressValue); + void progressTextChanged(const QString &progressText); + +public Q_SLOTS: + void cancel(); + void setPaused(bool paused); + void pause(); + void resume(); + void togglePaused(); +#endif + +private: + QFuture m_future; + const QFutureInterfaceBase &futureInterface() const { return m_future.d; } + QFutureInterfaceBase &futureInterface() { return m_future.d; } +}; + +template +Q_INLINE_TEMPLATE void QFutureWatcher::setFuture(const QFuture &_future) +{ + if (_future == m_future) + return; + + disconnectOutputInterface(true); + m_future = _future; + connectOutputInterface(); +} + +template <> +class QFutureWatcher : public QFutureWatcherBase +{ +public: + QFutureWatcher(QObject *_parent = 0) + : QFutureWatcherBase(_parent) + { } + ~QFutureWatcher() + { disconnectOutputInterface(); } + + void setFuture(const QFuture &future); + QFuture future() const + { return m_future; } + +private: + QFuture m_future; + const QFutureInterfaceBase &futureInterface() const { return m_future.d; } + QFutureInterfaceBase &futureInterface() { return m_future.d; } +}; + +Q_INLINE_TEMPLATE void QFutureWatcher::setFuture(const QFuture &_future) +{ + if (_future == m_future) + return; + + disconnectOutputInterface(true); + m_future = _future; + connectOutputInterface(); +} + +QT_END_NAMESPACE +QT_END_HEADER + +#endif // QT_NO_CONCURRENT + +#endif // QFUTUREWATCHER_H diff --git a/src/corelib/concurrent/qfuturewatcher_p.h b/src/corelib/concurrent/qfuturewatcher_p.h new file mode 100644 index 0000000000..fd02d44773 --- /dev/null +++ b/src/corelib/concurrent/qfuturewatcher_p.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QFUTUREWATCHER_P_H +#define QFUTUREWATCHER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qfutureinterface_p.h" +#include + +#ifndef QT_NO_QFUTURE + +#include + +QT_BEGIN_NAMESPACE + +class QFutureWatcherBase; +class QFutureWatcherBasePrivate : public QObjectPrivate, + public QFutureCallOutInterface +{ + Q_DECLARE_PUBLIC(QFutureWatcherBase) + +public: + QFutureWatcherBasePrivate(); + + void postCallOutEvent(const QFutureCallOutEvent &callOutEvent); + void callOutInterfaceDisconnected(); + + void sendCallOutEvent(QFutureCallOutEvent *event); + + QList pendingCallOutEvents; + QAtomicInt pendingResultsReady; + int maximumPendingResultsReady; + + QAtomicInt resultAtConnected; + bool finished; +}; + +QT_END_NAMESPACE + +#endif // QT_NO_QFUTURE +#endif diff --git a/src/corelib/concurrent/qrunnable.cpp b/src/corelib/concurrent/qrunnable.cpp new file mode 100644 index 0000000000..1aa7d59986 --- /dev/null +++ b/src/corelib/concurrent/qrunnable.cpp @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \class QRunnable + \since 4.4 + \brief The QRunnable class is the base class for all runnable objects. + + \ingroup thread + + The QRunnable class is an interface for representing a task or + piece of code that needs to be executed, represented by your + reimplementation of the run() function. + + You can use QThreadPool to execute your code in a separate + thread. QThreadPool deletes the QRunnable automatically if + autoDelete() returns true (the default). Use setAutoDelete() to + change the auto-deletion flag. + + QThreadPool supports executing the same QRunnable more than once + by calling QThreadPool::tryStart(this) from within the run() function. + If autoDelete is enabled the QRunnable will be deleted when + the last thread exits the run function. Calling QThreadPool::start() + multiple times with the same QRunnable when autoDelete is enabled + creates a race condition and is not recommended. + + \sa QThreadPool +*/ + +/*! \fn QRunnable::run() + Implement this pure virtual function in your subclass. +*/ + +/*! \fn QRunnable::QRunnable() + Constructs a QRunnable. Auto-deletion is enabled by default. + + \sa autoDelete(), setAutoDelete() +*/ + +/*! \fn QRunnable::~QRunnable() + QRunnable virtual destructor. +*/ + +/*! \fn bool QRunnable::autoDelete() const + + Returns true is auto-deletion is enabled; false otherwise. + + If auto-deletion is enabled, QThreadPool will automatically delete + this runnable after calling run(); otherwise, ownership remains + with the application programmer. + + \sa setAutoDelete(), QThreadPool +*/ + +/*! \fn bool QRunnable::setAutoDelete(bool autoDelete) + + Enables auto-deletion if \a autoDelete is true; otherwise + auto-deletion is disabled. + + If auto-deletion is enabled, QThreadPool will automatically delete + this runnable after calling run(); otherwise, ownership remains + with the application programmer. + + Note that this flag must be set before calling + QThreadPool::start(). Calling this function after + QThreadPool::start() results in undefined behavior. + + \sa autoDelete(), QThreadPool +*/ diff --git a/src/corelib/concurrent/qrunnable.h b/src/corelib/concurrent/qrunnable.h new file mode 100644 index 0000000000..c4eea1184f --- /dev/null +++ b/src/corelib/concurrent/qrunnable.h @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QRUNNABLE_H +#define QRUNNABLE_H + +#include + +QT_BEGIN_HEADER +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +class QRunnable +{ + int ref; + + friend class QThreadPool; + friend class QThreadPoolPrivate; + friend class QThreadPoolThread; + +public: + virtual void run() = 0; + + QRunnable() : ref(0) { } + virtual ~QRunnable() { } + + bool autoDelete() const { return ref != -1; } + void setAutoDelete(bool _autoDelete) { ref = _autoDelete ? 0 : -1; } +}; + +QT_END_NAMESPACE +QT_END_HEADER + +#endif diff --git a/src/corelib/concurrent/qtconcurrentcompilertest.h b/src/corelib/concurrent/qtconcurrentcompilertest.h new file mode 100644 index 0000000000..fad0c35109 --- /dev/null +++ b/src/corelib/concurrent/qtconcurrentcompilertest.h @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTCONCURRENT_COMPILERTEST_H +#define QTCONCURRENT_COMPILERTEST_H + +#include + +#ifndef QT_NO_CONCURRENT + +QT_BEGIN_HEADER +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +#if defined (Q_CC_MSVC) && (_MSC_VER < 1300) +# define QT_TYPENAME +#else +# define QT_TYPENAME typename +#endif + +QT_END_NAMESPACE +QT_END_HEADER + +#endif // QT_NO_CONCURRENT + +#endif diff --git a/src/corelib/concurrent/qtconcurrentexception.cpp b/src/corelib/concurrent/qtconcurrentexception.cpp new file mode 100644 index 0000000000..b4123f52ca --- /dev/null +++ b/src/corelib/concurrent/qtconcurrentexception.cpp @@ -0,0 +1,223 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qtconcurrentexception.h" + +#ifndef QT_NO_QFUTURE +#ifndef QT_NO_EXCEPTIONS + +QT_BEGIN_NAMESPACE + +/*! + \class QtConcurrent::Exception + \brief The Exception class provides a base class for exceptions that can transferred across threads. + \since 4.4 + + Qt Concurrent supports throwing and catching exceptions across thread + boundaries, provided that the exception inherit from QtConcurrent::Exception + and implement two helper functions: + + \snippet doc/src/snippets/code/src_corelib_concurrent_qtconcurrentexception.cpp 0 + + QtConcurrent::Exception subclasses must be thrown by value and + caught by reference: + + \snippet doc/src/snippets/code/src_corelib_concurrent_qtconcurrentexception.cpp 1 + + If you throw an exception that is not a subclass of QtConcurrent::Exception, + the Qt Concurrent functions will throw a QtConcurrent::UnhandledException + in the receiver thread. + + When using QFuture, transferred exceptions will be thrown when calling the following functions: + \list + \o QFuture::waitForFinished() + \o QFuture::result() + \o QFuture::resultAt() + \o QFuture::results() + \endlist +*/ + +/*! + \fn QtConcurrent::Exception::raise() const + In your QtConcurrent::Exception subclass, reimplement raise() like this: + + \snippet doc/src/snippets/code/src_corelib_concurrent_qtconcurrentexception.cpp 2 +*/ + +/*! + \fn QtConcurrent::Exception::clone() const + In your QtConcurrent::Exception subclass, reimplement clone() like this: + + \snippet doc/src/snippets/code/src_corelib_concurrent_qtconcurrentexception.cpp 3 +*/ + +/*! + \class QtConcurrent::UnhandledException + + \brief The UnhandledException class represents an unhandled exception in a worker thread. + \since 4.4 + + If a worker thread throws an exception that is not a subclass of QtConcurrent::Exception, + the Qt Concurrent functions will throw a QtConcurrent::UnhandledException + on the receiver thread side. + + Inheriting from this class is not supported. +*/ + +/*! + \fn QtConcurrent::UnhandledException::raise() const + \internal +*/ + +/*! + \fn QtConcurrent::UnhandledException::clone() const + \internal +*/ + +namespace QtConcurrent +{ + +void Exception::raise() const +{ + Exception e = *this; + throw e; +} + +Exception *Exception::clone() const +{ + return new Exception(*this); +} + +void UnhandledException::raise() const +{ + UnhandledException e = *this; + throw e; +} + +Exception *UnhandledException::clone() const +{ + return new UnhandledException(*this); +} + +#ifndef qdoc + +namespace internal { + +class Base +{ +public: + Base(Exception *exception) + : exception(exception), refCount(1), hasThrown(false) { } + ~Base() { delete exception; } + + Exception *exception; + QAtomicInt refCount; + bool hasThrown; +}; + +ExceptionHolder::ExceptionHolder(Exception *exception) +: base(new Base(exception)) {} + +ExceptionHolder::ExceptionHolder(const ExceptionHolder &other) +: base(other.base) +{ + base->refCount.ref(); +} + +void ExceptionHolder::operator=(const ExceptionHolder &other) +{ + if (base == other.base) + return; + + if (base->refCount.deref() == false) + delete base; + + base = other.base; + base->refCount.ref(); +} + +ExceptionHolder::~ExceptionHolder() +{ + if (base->refCount.deref() == 0) + delete base; +} + +Exception *ExceptionHolder::exception() const +{ + return base->exception; +} + +void ExceptionStore::setException(const Exception &e) +{ + if (hasException() == false) + exceptionHolder = ExceptionHolder(e.clone()); +} + +bool ExceptionStore::hasException() const +{ + return (exceptionHolder.exception() != 0); +} + +ExceptionHolder ExceptionStore::exception() +{ + return exceptionHolder; +} + +void ExceptionStore::throwPossibleException() +{ + if (hasException() ) { + exceptionHolder.base->hasThrown = true; + exceptionHolder.exception()->raise(); + } +} + +bool ExceptionStore::hasThrown() const { return exceptionHolder.base->hasThrown; } + +} // namespace internal + +#endif //qdoc + +} // namespace QtConcurrent + +QT_END_NAMESPACE + +#endif // QT_NO_EXCEPTIONS +#endif // QT_NO_CONCURRENT diff --git a/src/corelib/concurrent/qtconcurrentexception.h b/src/corelib/concurrent/qtconcurrentexception.h new file mode 100644 index 0000000000..044b100e99 --- /dev/null +++ b/src/corelib/concurrent/qtconcurrentexception.h @@ -0,0 +1,128 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTCONCURRENT_EXCEPTION_H +#define QTCONCURRENT_EXCEPTION_H + +#include + +#ifndef QT_NO_QFUTURE + +#include + +#ifndef QT_NO_EXCEPTIONS +# include +#endif + +QT_BEGIN_HEADER +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +namespace QtConcurrent +{ + +#ifndef QT_NO_EXCEPTIONS + +class Q_CORE_EXPORT Exception : public std::exception +{ +public: + virtual void raise() const; + virtual Exception *clone() const; +}; + +class Q_CORE_EXPORT UnhandledException : public Exception +{ +public: + void raise() const; + Exception *clone() const; +}; + +namespace internal { + +class Base; +class ExceptionHolder +{ +public: + ExceptionHolder(Exception *exception = 0); + ExceptionHolder(const ExceptionHolder &other); + void operator=(const ExceptionHolder &other); + ~ExceptionHolder(); + Exception *exception() const; + Base *base; +}; + +class Q_CORE_EXPORT ExceptionStore +{ +public: + void setException(const Exception &e); + bool hasException() const; + ExceptionHolder exception(); + void throwPossibleException(); + bool hasThrown() const; + ExceptionHolder exceptionHolder; +}; + +} // namespace internal + +#else // QT_NO_EXCEPTIONS + +namespace internal { + +class Q_CORE_EXPORT ExceptionStore +{ +public: + ExceptionStore() { } + inline void throwPossibleException() const {} +}; + +} // namespace internal + +#endif + +} // namespace QtConcurrent + +QT_END_NAMESPACE +QT_END_HEADER + +#endif // QT_NO_CONCURRENT + +#endif diff --git a/src/corelib/concurrent/qtconcurrentfilter.cpp b/src/corelib/concurrent/qtconcurrentfilter.cpp new file mode 100644 index 0000000000..6646e02afc --- /dev/null +++ b/src/corelib/concurrent/qtconcurrentfilter.cpp @@ -0,0 +1,330 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \headerfile + \title Concurrent Filter and Filter-Reduce + \ingroup thread + + \brief The header provides concurrent Filter and + Filter-Reduce. + + These functions are a part of the \l {Concurrent Programming}{Qt Concurrent} framework. + + The QtConcurrent::filter(), QtConcurrent::filtered() and + QtConcurrent::filteredReduced() functions filter items in a sequence such + as a QList or a QVector in parallel. QtConcurrent::filter() modifies a + sequence in-place, QtConcurrent::filtered() returns a new sequence + containing the filtered content, and QtConcurrent::filteredReduced() + returns a single result. + + Each of the above functions have a blocking variant that returns the final + result instead of a QFuture. You use them in the same way as the + asynchronous variants. + + \snippet doc/src/snippets/code/src_corelib_concurrent_qtconcurrentfilter.cpp 6 + + Note that the result types above are not QFuture objects, but real result + types (in this case, QStringList and QSet). + + \section1 Concurrent Filter + + QtConcurrent::filtered() takes an input sequence and a filter function. + This filter function is then called for each item in the sequence, and a + new sequence containing the filtered values is returned. + + The filter function must be of the form: + + \snippet doc/src/snippets/code/src_corelib_concurrent_qtconcurrentfilter.cpp 0 + + T must match the type stored in the sequence. The function returns true if + the item should be kept, false if it should be discarded. + + This example shows how to keep strings that are all lower-case from a + QStringList: + + \snippet doc/src/snippets/code/src_corelib_concurrent_qtconcurrentfilter.cpp 1 + + The results of the filter are made available through QFuture. See the + QFuture and QFutureWatcher documentation for more information on how to + use QFuture in your applications. + + If you want to modify a sequence in-place, use QtConcurrent::filter(): + + \snippet doc/src/snippets/code/src_corelib_concurrent_qtconcurrentfilter.cpp 2 + + Since the sequence is modified in place, QtConcurrent::filter() does not + return any results via QFuture. However, you can still use QFuture and + QFutureWatcher to monitor the status of the filter. + + \section1 Concurrent Filter-Reduce + + QtConcurrent::filteredReduced() is similar to QtConcurrent::filtered(), + but instead of returing a sequence with the filtered results, the results + are combined into a single value using a reduce function. + + The reduce function must be of the form: + + \snippet doc/src/snippets/code/src_corelib_concurrent_qtconcurrentfilter.cpp 3 + + T is the type of the final result, U is the type of items being filtered. + Note that the return value and return type of the reduce function are not + used. + + Call QtConcurrent::filteredReduced() like this: + + \snippet doc/src/snippets/code/src_corelib_concurrent_qtconcurrentfilter.cpp 4 + + The reduce function will be called once for each result kept by the filter + function, and should merge the \e{intermediate} into the \e{result} + variable. QtConcurrent::filteredReduced() guarantees that only one thread + will call reduce at a time, so using a mutex to lock the result variable + is not necessary. The QtConcurrent::ReduceOptions enum provides a way to + control the order in which the reduction is done. + + \section1 Additional API Features + + \section2 Using Iterators instead of Sequence + + Each of the above functions has a variant that takes an iterator range + instead of a sequence. You use them in the same way as the sequence + variants: + + \snippet doc/src/snippets/code/src_corelib_concurrent_qtconcurrentfilter.cpp 5 + + + \section2 Using Member Functions + + QtConcurrent::filter(), QtConcurrent::filtered(), and + QtConcurrent::filteredReduced() accept pointers to member functions. + The member function class type must match the type stored in the sequence: + + \snippet doc/src/snippets/code/src_corelib_concurrent_qtconcurrentfilter.cpp 7 + + Note that when using QtConcurrent::filteredReduced(), you can mix the use of + normal and member functions freely: + + \snippet doc/src/snippets/code/src_corelib_concurrent_qtconcurrentfilter.cpp 8 + + \section2 Using Function Objects + + QtConcurrent::filter(), QtConcurrent::filtered(), and + QtConcurrent::filteredReduced() accept function objects, which can be used to + add state to a function call. The result_type typedef must define the + result type of the function call operator: + + \snippet doc/src/snippets/code/src_corelib_concurrent_qtconcurrentfilter.cpp 13 + + \section2 Using Bound Function Arguments + + Note that Qt does not provide support for bound functions. This is + provided by 3rd party libraries like + \l{http://www.boost.org/libs/bind/bind.html}{Boost} or + \l{http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1836.pdf} + {C++ TR1 Library Extensions}. + + If you want to use a filter function takes more than one argument, you can + use boost::bind() or std::tr1::bind() to transform it onto a function that + takes one argument. + + As an example, we use QString::contains(): + + \snippet doc/src/snippets/code/src_corelib_concurrent_qtconcurrentfilter.cpp 9 + + QString::contains() takes 2 arguments (including the "this" pointer) and + can't be used with QtConcurrent::filtered() directly, because + QtConcurrent::filtered() expects a function that takes one argument. To + use QString::contains() with QtConcurrent::filtered() we have to provide a + value for the \e regexp argument: + + \snippet doc/src/snippets/code/src_corelib_concurrent_qtconcurrentfilter.cpp 10 + + The return value from boost::bind() is a function object (functor) with + the following signature: + + \snippet doc/src/snippets/code/src_corelib_concurrent_qtconcurrentfilter.cpp 11 + + This matches what QtConcurrent::filtered() expects, and the complete + example becomes: + + \snippet doc/src/snippets/code/src_corelib_concurrent_qtconcurrentfilter.cpp 12 +*/ + +/*! + \fn QFuture QtConcurrent::filter(Sequence &sequence, FilterFunction filterFunction) + \relates + + Calls \a filterFunction once for each item in \a sequence. If + \a filterFunction returns true, the item is kept in \a sequence; + otherwise, the item is removed from \a sequence. +*/ + +/*! + \fn QFuture QtConcurrent::filtered(const Sequence &sequence, FilterFunction filterFunction) + \relates + + Calls \a filterFunction once for each item in \a sequence and returns a + new Sequence of kept items. If \a filterFunction returns true, a copy of + the item is put in the new Sequence. Otherwise, the item will \e not + appear in the new Sequence. +*/ + +/*! + \fn QFuture QtConcurrent::filtered(ConstIterator begin, ConstIterator end, FilterFunction filterFunction) + \relates + + Calls \a filterFunction once for each item from \a begin to \a end and + returns a new Sequence of kept items. If \a filterFunction returns true, a + copy of the item is put in the new Sequence. Otherwise, the item will + \e not appear in the new Sequence. +*/ + +/*! + \fn QFuture QtConcurrent::filteredReduced(const Sequence &sequence, FilterFunction filterFunction, ReduceFunction reduceFunction, QtConcurrent::ReduceOptions reduceOptions) + \relates + + Calls \a filterFunction once for each item in \a sequence. If + \a filterFunction returns true for an item, that item is then passed to + \a reduceFunction. In other words, the return value is the result of + \a reduceFunction for each item where \a filterFunction returns true. + + Note that while \a filterFunction is called concurrently, only one thread + at a time will call \a reduceFunction. The order in which \a reduceFunction + is called is undefined if \a reduceOptions is + QtConcurrent::UnorderedReduce. If \a reduceOptions is + QtConcurrent::OrderedReduce, \a reduceFunction is called in the order of + the original sequence. +*/ + +/*! + \fn QFuture QtConcurrent::filteredReduced(ConstIterator begin, ConstIterator end, FilterFunction filterFunction, ReduceFunction reduceFunction, QtConcurrent::ReduceOptions reduceOptions) + \relates + + Calls \a filterFunction once for each item from \a begin to \a end. If + \a filterFunction returns true for an item, that item is then passed to + \a reduceFunction. In other words, the return value is the result of + \a reduceFunction for each item where \a filterFunction returns true. + + Note that while \a filterFunction is called concurrently, only one thread + at a time will call \a reduceFunction. The order in which + \a reduceFunction is called is undefined if \a reduceOptions is + QtConcurrent::UnorderedReduce. If \a reduceOptions is + QtConcurrent::OrderedReduce, the \a reduceFunction is called in the order + of the original sequence. +*/ + +/*! + \fn void QtConcurrent::blockingFilter(Sequence &sequence, FilterFunction filterFunction) + + Calls \a filterFunction once for each item in \a sequence. If + \a filterFunction returns true, the item is kept in \a sequence; + otherwise, the item is removed from \a sequence. + + \note This function will block until all items in the sequence have been processed. +*/ + +/*! + \fn Sequence QtConcurrent::blockingFiltered(const Sequence &sequence, FilterFunction filterFunction) + + Calls \a filterFunction once for each item in \a sequence and returns a + new Sequence of kept items. If \a filterFunction returns true, a copy of + the item is put in the new Sequence. Otherwise, the item will \e not + appear in the new Sequence. + + \note This function will block until all items in the sequence have been processed. + + \sa filtered() +*/ + +/*! + \fn Sequence QtConcurrent::blockingFiltered(ConstIterator begin, ConstIterator end, FilterFunction filterFunction) + + Calls \a filterFunction once for each item from \a begin to \a end and + returns a new Sequence of kept items. If \a filterFunction returns true, a + copy of the item is put in the new Sequence. Otherwise, the item will + \e not appear in the new Sequence. + + \note This function will block until the iterator reaches the end of the + sequence being processed. + + \sa filtered() +*/ + +/*! + \fn T QtConcurrent::blockingFilteredReduced(const Sequence &sequence, FilterFunction filterFunction, ReduceFunction reduceFunction, QtConcurrent::ReduceOptions reduceOptions) + + Calls \a filterFunction once for each item in \a sequence. If + \a filterFunction returns true for an item, that item is then passed to + \a reduceFunction. In other words, the return value is the result of + \a reduceFunction for each item where \a filterFunction returns true. + + Note that while \a filterFunction is called concurrently, only one thread + at a time will call \a reduceFunction. The order in which \a reduceFunction + is called is undefined if \a reduceOptions is + QtConcurrent::UnorderedReduce. If \a reduceOptions is + QtConcurrent::OrderedReduce, \a reduceFunction is called in the order of + the original sequence. + + \note This function will block until all items in the sequence have been processed. + + \sa filteredReduced() +*/ + +/*! + \fn T QtConcurrent::blockingFilteredReduced(ConstIterator begin, ConstIterator end, FilterFunction filterFunction, ReduceFunction reduceFunction, QtConcurrent::ReduceOptions reduceOptions) + + Calls \a filterFunction once for each item from \a begin to \a end. If + \a filterFunction returns true for an item, that item is then passed to + \a reduceFunction. In other words, the return value is the result of + \a reduceFunction for each item where \a filterFunction returns true. + + Note that while \a filterFunction is called concurrently, only one thread + at a time will call \a reduceFunction. The order in which + \a reduceFunction is called is undefined if \a reduceOptions is + QtConcurrent::UnorderedReduce. If \a reduceOptions is + QtConcurrent::OrderedReduce, the \a reduceFunction is called in the order + of the original sequence. + + \note This function will block until the iterator reaches the end of the + sequence being processed. + + \sa filteredReduced() +*/ diff --git a/src/corelib/concurrent/qtconcurrentfilter.h b/src/corelib/concurrent/qtconcurrentfilter.h new file mode 100644 index 0000000000..e392212b12 --- /dev/null +++ b/src/corelib/concurrent/qtconcurrentfilter.h @@ -0,0 +1,736 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTCONCURRENT_FILTER_H +#define QTCONCURRENT_FILTER_H + +#include + +#ifndef QT_NO_CONCURRENT + +#include +#include + +QT_BEGIN_HEADER +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +#ifdef qdoc + +namespace QtConcurrent { + + QFuture filter(Sequence &sequence, FilterFunction filterFunction); + + template + QFuture filtered(const Sequence &sequence, FilterFunction filterFunction); + template + QFuture filtered(ConstIterator begin, ConstIterator end, FilterFunction filterFunction); + + template + QFuture filteredReduced(const Sequence &sequence, + FilterFunction filterFunction, + ReduceFunction reduceFunction, + QtConcurrent::ReduceOptions reduceOptions = UnorderedReduce | SequentialReduce); + template + QFuture filteredReduced(ConstIterator begin, + ConstIterator end, + FilterFunction filterFunction, + ReduceFunction reduceFunction, + QtConcurrent::ReduceOptions reduceOptions = UnorderedReduce | SequentialReduce); + + void blockingFilter(Sequence &sequence, FilterFunction filterFunction); + + template + Sequence blockingFiltered(const Sequence &sequence, FilterFunction filterFunction); + template + Sequence blockingFiltered(ConstIterator begin, ConstIterator end, FilterFunction filterFunction); + + template + T blockingFilteredReduced(const Sequence &sequence, + FilterFunction filterFunction, + ReduceFunction reduceFunction, + QtConcurrent::ReduceOptions reduceOptions = UnorderedReduce | SequentialReduce); + template + T blockingFilteredReduced(ConstIterator begin, + ConstIterator end, + FilterFunction filterFunction, + ReduceFunction reduceFunction, + QtConcurrent::ReduceOptions reduceOptions = UnorderedReduce | SequentialReduce); + +} // namespace QtConcurrent + +#else + +namespace QtConcurrent { + +template +ThreadEngineStarter filterInternal(Sequence &sequence, KeepFunctor keep, T (C::*reduce)(U)) +{ + typedef MemberFunctionWrapper1 ReduceFunctor; + typedef typename Sequence::const_iterator Iterator; + typedef FilterKernel KernelType; + return startThreadEngine(new KernelType(sequence, keep, reduce)); +} + +// filter() on sequences +template +QFuture filter(Sequence &sequence, KeepFunctor keep) +{ + return filterInternal(sequence, keep, &Sequence::push_back); +} + +template +QFuture filter(Sequence &sequence, bool (keep)(T)) +{ + return filterInternal(sequence, FunctionWrapper1(keep), &Sequence::push_back); +} + +template +QFuture filter(Sequence &sequence, bool (C::*keep)() const) +{ + return filterInternal(sequence, ConstMemberFunctionWrapper(keep), &Sequence::push_back); +} + +// filteredReduced() on sequences +template +QFuture filteredReduced(const Sequence &sequence, + KeepFunctor keep, + ReduceFunctor reduce, + ReduceOptions options = ReduceOptions(UnorderedReduce | SequentialReduce)) +{ + return startFilteredReduced(sequence, keep, reduce, options); + } + +template +QFuture filteredReduced(const Sequence &sequence, + bool (filter)(T), + ReduceFunctor reduce, + ReduceOptions options = ReduceOptions(UnorderedReduce | SequentialReduce)) +{ + return filteredReduced(sequence, + FunctionWrapper1(filter), + reduce, + options); +} + +template +QFuture filteredReduced(const Sequence &sequence, + bool (C::*filter)() const, + ReduceFunctor reduce, + ReduceOptions options = ReduceOptions(UnorderedReduce | SequentialReduce)) +{ + return filteredReduced(sequence, + ConstMemberFunctionWrapper(filter), + reduce, + options); +} + +template +QFuture filteredReduced(const Sequence &sequence, + KeepFunctor keep, + T (reduce)(U &, V), + ReduceOptions options = ReduceOptions(UnorderedReduce | SequentialReduce)) +{ + return filteredReduced(sequence, + keep, + FunctionWrapper2(reduce), + options); +} + +template +QFuture filteredReduced(const Sequence &sequence, + KeepFunctor keep, + T (C::*reduce)(U), + ReduceOptions options = ReduceOptions(UnorderedReduce | SequentialReduce)) +{ + return filteredReduced(sequence, + keep, + MemberFunctionWrapper1(reduce), + options); +} + +template +QFuture filteredReduced(const Sequence &sequence, + bool (keep)(T), + U (reduce)(V &, W), + ReduceOptions options = ReduceOptions(UnorderedReduce | SequentialReduce)) +{ + return filteredReduced(sequence, + FunctionWrapper1(keep), + FunctionWrapper2(reduce), + options); +} + +template +QFuture filteredReduced(const Sequence &sequence, + bool (C::*keep)() const, + T (reduce)(U &, V), + ReduceOptions options = ReduceOptions(UnorderedReduce | SequentialReduce)) +{ + return filteredReduced(sequence, + ConstMemberFunctionWrapper(keep), + FunctionWrapper2(reduce), + options); +} + +template +QFuture filteredReduced(const Sequence &sequence, + bool (keep)(T), + U (C::*reduce)(V), + ReduceOptions options = ReduceOptions(UnorderedReduce | SequentialReduce)) +{ + return filteredReduced(sequence, + FunctionWrapper1(keep), + MemberFunctionWrapper1(reduce), + options); +} + +template +QFuture filteredReduced(const Sequence &sequence, + bool (C::*keep)() const, + T (D::*reduce)(U), + ReduceOptions options = ReduceOptions(UnorderedReduce | SequentialReduce)) +{ + return filteredReduced(sequence, + ConstMemberFunctionWrapper(keep), + MemberFunctionWrapper1(reduce), + options); +} + +// filteredReduced() on iterators +template +QFuture filteredReduced(Iterator begin, + Iterator end, + KeepFunctor keep, + ReduceFunctor reduce, + ReduceOptions options = ReduceOptions(UnorderedReduce | SequentialReduce)) +{ + return startFilteredReduced(begin, end, keep, reduce, options); +} + +template +QFuture filteredReduced(Iterator begin, + Iterator end, + bool (filter)(T), + ReduceFunctor reduce, + ReduceOptions options = ReduceOptions(UnorderedReduce | SequentialReduce)) +{ + return filteredReduced(begin, + end, + FunctionWrapper1(filter), + reduce, + options); +} + +template +QFuture filteredReduced(Iterator begin, + Iterator end, + bool (C::*filter)() const, + ReduceFunctor reduce, + ReduceOptions options = ReduceOptions(UnorderedReduce | SequentialReduce)) +{ + return filteredReduced(begin, + end, + ConstMemberFunctionWrapper(filter), + reduce, + options); +} + +template +QFuture filteredReduced(Iterator begin, + Iterator end, + KeepFunctor keep, + T (reduce)(U &, V), + ReduceOptions options = ReduceOptions(UnorderedReduce | SequentialReduce)) +{ + return filteredReduced(begin, + end, + keep, + FunctionWrapper2(reduce), + options); +} + +template +QFuture filteredReduced(Iterator begin, + Iterator end, + KeepFunctor keep, + T (C::*reduce)(U), + ReduceOptions options = ReduceOptions(UnorderedReduce | SequentialReduce)) +{ + return filteredReduced(begin, + end, + keep, + MemberFunctionWrapper1(reduce), + options); +} + +template +QFuture filteredReduced(Iterator begin, + Iterator end, + bool (keep)(T), + U (reduce)(V &, W), + ReduceOptions options = ReduceOptions(UnorderedReduce | SequentialReduce)) +{ + return filteredReduced(begin, + end, + FunctionWrapper1(keep), + FunctionWrapper2(reduce), + options); +} + +template +QFuture filteredReduced(Iterator begin, + Iterator end, + bool (C::*keep)() const, + T (reduce)(U &, V), + ReduceOptions options = ReduceOptions(UnorderedReduce | SequentialReduce)) +{ + return filteredReduced(begin, + end, + ConstMemberFunctionWrapper(keep), + FunctionWrapper2(reduce), + options); +} + +template +QFuture filteredReduced(Iterator begin, + Iterator end, + bool (keep)(T), + U (C::*reduce)(V), + ReduceOptions options = ReduceOptions(UnorderedReduce | SequentialReduce)) +{ + return filteredReduced(begin, + end, + FunctionWrapper1(keep), + MemberFunctionWrapper1(reduce), + options); +} + +template +QFuture filteredReduced(Iterator begin, + Iterator end, + bool (C::*keep)() const, + T (D::*reduce)(U), + ReduceOptions options = ReduceOptions(UnorderedReduce | SequentialReduce)) +{ + return filteredReduced(begin, + end, + ConstMemberFunctionWrapper(keep), + MemberFunctionWrapper1(reduce), + options); +} + + +// filtered() on sequences +template +QFuture filtered(const Sequence &sequence, KeepFunctor keep) +{ + return startFiltered(sequence, keep); +} + +template +QFuture filtered(const Sequence &sequence, bool (keep)(T)) +{ + return startFiltered(sequence, FunctionWrapper1(keep)); +} + +template +QFuture filtered(const Sequence &sequence, bool (C::*keep)() const) +{ + return startFiltered(sequence, ConstMemberFunctionWrapper(keep)); +} + +// filtered() on iterators +template +QFuture::value_type> filtered(Iterator begin, Iterator end, KeepFunctor keep) +{ + return startFiltered(begin, end, keep); +} + +template +QFuture::value_type> filtered(Iterator begin, Iterator end, bool (keep)(T)) +{ + return startFiltered(begin, end, FunctionWrapper1(keep)); +} + +template +QFuture::value_type> filtered(Iterator begin, + Iterator end, + bool (C::*keep)() const) +{ + return startFiltered(begin, end, ConstMemberFunctionWrapper(keep)); +} + + +// blocking filter() on sequences +template +void blockingFilter(Sequence &sequence, KeepFunctor keep) +{ + filterInternal(sequence, keep, &Sequence::push_back).startBlocking(); +} + +template +void blockingFilter(Sequence &sequence, bool (keep)(T)) +{ + filterInternal(sequence, FunctionWrapper1(keep), &Sequence::push_back) + .startBlocking(); +} + +template +void blockingFilter(Sequence &sequence, bool (C::*keep)() const) +{ + filterInternal(sequence, + ConstMemberFunctionWrapper(keep), + &Sequence::push_back) + .startBlocking(); +} + +// blocking filteredReduced() on sequences +template +ResultType blockingFilteredReduced(const Sequence &sequence, + KeepFunctor keep, + ReduceFunctor reduce, + ReduceOptions options = ReduceOptions(UnorderedReduce | SequentialReduce)) +{ + return startFilteredReduced(sequence, keep, reduce, options) + .startBlocking(); +} + +template +ResultType blockingFilteredReduced(const Sequence &sequence, + bool (filter)(T), + ReduceFunctor reduce, + ReduceOptions options = ReduceOptions(UnorderedReduce | SequentialReduce)) +{ + return blockingFilteredReduced + (sequence, + FunctionWrapper1(filter), + reduce, + options); +} + +template +ResultType blockingFilteredReduced(const Sequence &sequence, + bool (C::*filter)() const, + ReduceFunctor reduce, + ReduceOptions options = ReduceOptions(UnorderedReduce | SequentialReduce)) +{ + return blockingFilteredReduced + (sequence, + ConstMemberFunctionWrapper(filter), + reduce, + options); +} + +template +U blockingFilteredReduced(const Sequence &sequence, + KeepFunctor keep, + T (reduce)(U &, V), + ReduceOptions options = ReduceOptions(UnorderedReduce | SequentialReduce)) +{ + return blockingFilteredReduced + (sequence, + keep, + FunctionWrapper2(reduce), + options); +} + +template +C blockingFilteredReduced(const Sequence &sequence, + KeepFunctor keep, + T (C::*reduce)(U), + ReduceOptions options = ReduceOptions(UnorderedReduce | SequentialReduce)) +{ + return blockingFilteredReduced + (sequence, + keep, + MemberFunctionWrapper1(reduce), + options); +} + +template +V blockingFilteredReduced(const Sequence &sequence, + bool (keep)(T), + U (reduce)(V &, W), + ReduceOptions options = ReduceOptions(UnorderedReduce | SequentialReduce)) +{ + return blockingFilteredReduced + (sequence, + FunctionWrapper1(keep), + FunctionWrapper2(reduce), + options); +} + +template +U blockingFilteredReduced(const Sequence &sequence, + bool (C::*keep)() const, + T (reduce)(U &, V), + ReduceOptions options = ReduceOptions(UnorderedReduce | SequentialReduce)) +{ + return blockingFilteredReduced + (sequence, + ConstMemberFunctionWrapper(keep), + FunctionWrapper2(reduce), + options); +} + +template +C blockingFilteredReduced(const Sequence &sequence, + bool (keep)(T), + U (C::*reduce)(V), + ReduceOptions options = ReduceOptions(UnorderedReduce | SequentialReduce)) +{ + return blockingFilteredReduced + (sequence, + FunctionWrapper1(keep), + MemberFunctionWrapper1(reduce), + options); +} + +template +D blockingFilteredReduced(const Sequence &sequence, + bool (C::*keep)() const, + T (D::*reduce)(U), + ReduceOptions options = ReduceOptions(UnorderedReduce | SequentialReduce)) +{ + return blockingFilteredReduced + (sequence, + ConstMemberFunctionWrapper(keep), + MemberFunctionWrapper1(reduce), + options); +} + +// blocking filteredReduced() on iterators +template +ResultType blockingFilteredReduced(Iterator begin, + Iterator end, + KeepFunctor keep, + ReduceFunctor reduce, + ReduceOptions options = ReduceOptions(UnorderedReduce | SequentialReduce)) +{ + return startFilteredReduced(begin, end, keep, reduce, options) + .startBlocking(); +} + +template +ResultType blockingFilteredReduced(Iterator begin, + Iterator end, + bool (filter)(T), + ReduceFunctor reduce, + ReduceOptions options = ReduceOptions(UnorderedReduce | SequentialReduce)) +{ + return blockingFilteredReduced + (begin, + end, + FunctionWrapper1(filter), + reduce, + options); +} + +template +ResultType blockingFilteredReduced(Iterator begin, + Iterator end, + bool (C::*filter)() const, + ReduceFunctor reduce, + ReduceOptions options = ReduceOptions(UnorderedReduce | SequentialReduce)) +{ + return blockingFilteredReduced + (begin, + end, + ConstMemberFunctionWrapper(filter), + reduce, + options); +} + +template +U blockingFilteredReduced(Iterator begin, + Iterator end, + KeepFunctor keep, + T (reduce)(U &, V), + ReduceOptions options = ReduceOptions(UnorderedReduce | SequentialReduce)) +{ + return blockingFilteredReduced + (begin, + end, + keep, + FunctionWrapper2(reduce), + options); +} + +template +C blockingFilteredReduced(Iterator begin, + Iterator end, + KeepFunctor keep, + T (C::*reduce)(U), + ReduceOptions options = ReduceOptions(UnorderedReduce | SequentialReduce)) +{ + return blockingFilteredReduced + (begin, + end, + keep, + MemberFunctionWrapper1(reduce), + options); +} + +template +V blockingFilteredReduced(Iterator begin, + Iterator end, + bool (keep)(T), + U (reduce)(V &, W), + ReduceOptions options = ReduceOptions(UnorderedReduce | SequentialReduce)) +{ + return blockingFilteredReduced + (begin, + end, + FunctionWrapper1(keep), + FunctionWrapper2(reduce), + options); +} + +template +U blockingFilteredReduced(Iterator begin, + Iterator end, + bool (C::*keep)() const, + T (reduce)(U &, V), + ReduceOptions options = ReduceOptions(UnorderedReduce | SequentialReduce)) +{ + return blockingFilteredReduced + (begin, + end, + ConstMemberFunctionWrapper(keep), + FunctionWrapper2(reduce), + options); +} + +template +C blockingFilteredReduced(Iterator begin, + Iterator end, + bool (keep)(T), + U (C::*reduce)(V), + ReduceOptions options = ReduceOptions(UnorderedReduce | SequentialReduce)) +{ + return blockingFilteredReduced + (begin, + end, + FunctionWrapper1(keep), + MemberFunctionWrapper1(reduce), + options); +} + +template +D blockingFilteredReduced(Iterator begin, + Iterator end, + bool (C::*keep)() const, + T (D::*reduce)(U), + ReduceOptions options = ReduceOptions(UnorderedReduce | SequentialReduce)) +{ + return blockingFilteredReduced + (begin, + end, + ConstMemberFunctionWrapper(keep), + MemberFunctionWrapper1(reduce), + options); +} + +// blocking filtered() on sequences +template +Sequence blockingFiltered(const Sequence &sequence, KeepFunctor keep) +{ + return blockingFilteredReduced(sequence, keep, &Sequence::push_back, OrderedReduce); +} + +template +Sequence blockingFiltered(const Sequence &sequence, bool (keep)(T)) +{ + return blockingFilteredReduced(sequence, keep, &Sequence::push_back, OrderedReduce); +} + +template +Sequence blockingFiltered(const Sequence &sequence, bool (C::*filter)() const) +{ + return blockingFilteredReduced(sequence, + filter, + &Sequence::push_back, + OrderedReduce); +} + +// blocking filtered() on iterators +template +OutputSequence blockingFiltered(Iterator begin, Iterator end, KeepFunctor keep) +{ + return blockingFilteredReduced(begin, + end, + keep, + &OutputSequence::push_back, + OrderedReduce); +} + +template +OutputSequence blockingFiltered(Iterator begin, Iterator end, bool (keep)(T)) +{ + return blockingFilteredReduced(begin, + end, + keep, + &OutputSequence::push_back, + OrderedReduce); +} + +template +OutputSequence blockingFiltered(Iterator begin, Iterator end, bool (C::*filter)() const) +{ + return blockingFilteredReduced(begin, + end, + filter, + &OutputSequence::push_back, + OrderedReduce); +} + +} // namespace QtConcurrent + +#endif // qdoc + +QT_END_NAMESPACE +QT_END_HEADER + +#endif // QT_NO_CONCURRENT + +#endif diff --git a/src/corelib/concurrent/qtconcurrentfilterkernel.h b/src/corelib/concurrent/qtconcurrentfilterkernel.h new file mode 100644 index 0000000000..cd926c006f --- /dev/null +++ b/src/corelib/concurrent/qtconcurrentfilterkernel.h @@ -0,0 +1,351 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTCONCURRENT_FILTERKERNEL_H +#define QTCONCURRENT_FILTERKERNEL_H + +#include + +#ifndef QT_NO_CONCURRENT + +#include +#include +#include + +QT_BEGIN_HEADER +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +#ifndef qdoc + +namespace QtConcurrent { + +template +struct qValueType +{ + typedef typename T::value_type value_type; +}; + +template +struct qValueType +{ + typedef T value_type; +}; + +template +struct qValueType +{ + typedef T value_type; +}; + +// Implementation of filter +template +class FilterKernel : public IterateKernel +{ + typedef ReduceKernel Reducer; + typedef IterateKernel IterateKernelType; + typedef typename ReduceFunctor::result_type T; + + Sequence reducedResult; + Sequence &sequence; + KeepFunctor keep; + ReduceFunctor reduce; + Reducer reducer; + +public: + FilterKernel(Sequence &_sequence, KeepFunctor _keep, ReduceFunctor _reduce) + : IterateKernelType(const_cast(_sequence).begin(), const_cast(_sequence).end()), reducedResult(), + sequence(_sequence), + keep(_keep), + reduce(_reduce), + reducer(OrderedReduce) + { } + + bool runIteration(typename Sequence::const_iterator it, int index, T *) + { + IntermediateResults results; + results.begin = index; + results.end = index + 1; + + if (keep(*it)) + results.vector.append(*it); + + reducer.runReduce(reduce, reducedResult, results); + return false; + } + + bool runIterations(typename Sequence::const_iterator sequenceBeginIterator, int begin, int end, T *) + { + IntermediateResults results; + results.begin = begin; + results.end = end; + results.vector.reserve(end - begin); + + + typename Sequence::const_iterator it = sequenceBeginIterator; + advance(it, begin); + for (int i = begin; i < end; ++i) { + if (keep(*it)) + results.vector.append(*it); + advance(it, 1); + } + + reducer.runReduce(reduce, reducedResult, results); + return false; + } + + void finish() + { + reducer.finish(reduce, reducedResult); + sequence = reducedResult; + } + + inline bool shouldThrottleThread() + { + return IterateKernelType::shouldThrottleThread() || reducer.shouldThrottle(); + } + + inline bool shouldStartThread() + { + return IterateKernelType::shouldStartThread() && reducer.shouldStartThread(); + } + + typedef void ReturnType; + typedef void ResultType; +}; + +// Implementation of filter-reduce +template ::value_type> > +class FilteredReducedKernel : public IterateKernel +{ + ReducedResultType reducedResult; + KeepFunctor keep; + ReduceFunctor reduce; + Reducer reducer; + typedef IterateKernel IterateKernelType; + +public: + FilteredReducedKernel(Iterator begin, + Iterator end, + KeepFunctor _keep, + ReduceFunctor _reduce, + ReduceOptions reduceOption) + : IterateKernelType(begin, end), reducedResult(), keep(_keep), reduce(_reduce), reducer(reduceOption) + { } + +#if 0 + FilteredReducedKernel(ReducedResultType initialValue, + KeepFunctor keep, + ReduceFunctor reduce, + ReduceOption reduceOption) + : reducedResult(initialValue), keep(keep), reduce(reduce), reducer(reduceOption) + { } +#endif + + bool runIteration(Iterator it, int index, ReducedResultType *) + { + IntermediateResults::value_type> results; + results.begin = index; + results.end = index + 1; + + if (keep(*it)) + results.vector.append(*it); + + reducer.runReduce(reduce, reducedResult, results); + return false; + } + + bool runIterations(Iterator sequenceBeginIterator, int begin, int end, ReducedResultType *) + { + IntermediateResults::value_type> results; + results.begin = begin; + results.end = end; + results.vector.reserve(end - begin); + + Iterator it = sequenceBeginIterator; + advance(it, begin); + for (int i = begin; i < end; ++i) { + if (keep(*it)) + results.vector.append(*it); + advance(it, 1); + } + + reducer.runReduce(reduce, reducedResult, results); + return false; + } + + void finish() + { + reducer.finish(reduce, reducedResult); + } + + inline bool shouldThrottleThread() + { + return IterateKernelType::shouldThrottleThread() || reducer.shouldThrottle(); + } + + inline bool shouldStartThread() + { + return IterateKernelType::shouldStartThread() && reducer.shouldStartThread(); + } + + typedef ReducedResultType ReturnType; + typedef ReducedResultType ResultType; + ReducedResultType *result() + { + return &reducedResult; + } +}; + +// Implementation of filter that reports individual results via QFutureInterface +template +class FilteredEachKernel : public IterateKernel::value_type> +{ + typedef typename qValueType::value_type T; + typedef IterateKernel IterateKernelType; + + KeepFunctor keep; + +public: + typedef T ReturnType; + typedef T ResultType; + + FilteredEachKernel(Iterator begin, Iterator end, KeepFunctor _keep) + : IterateKernelType(begin, end), keep(_keep) + { } + + void start() + { + if (this->futureInterface) + this->futureInterface->setFilterMode(true); + IterateKernelType::start(); + } + + bool runIteration(Iterator it, int index, T *) + { + if (keep(*it)) + this->reportResult(&(*it), index); + else + this->reportResult(0, index); + return false; + } + + bool runIterations(Iterator sequenceBeginIterator, int begin, int end, T *) + { + const int count = end - begin; + IntermediateResults::value_type> results; + results.begin = begin; + results.end = end; + results.vector.reserve(count); + + Iterator it = sequenceBeginIterator; + advance(it, begin); + for (int i = begin; i < end; ++i) { + if (keep(*it)) + results.vector.append(*it); + advance(it, 1); + } + + this->reportResults(results.vector, begin, count); + return false; + } +}; + +template +inline +ThreadEngineStarter::value_type> +startFiltered(Iterator begin, Iterator end, KeepFunctor functor) +{ + return startThreadEngine(new FilteredEachKernel(begin, end, functor)); +} + +template +inline ThreadEngineStarter +startFiltered(const Sequence &sequence, KeepFunctor functor) +{ + typedef SequenceHolder1, + KeepFunctor> + SequenceHolderType; + return startThreadEngine(new SequenceHolderType(sequence, functor)); +} + +template +inline ThreadEngineStarter startFilteredReduced(const Sequence & sequence, + MapFunctor mapFunctor, ReduceFunctor reduceFunctor, + ReduceOptions options) +{ + typedef typename Sequence::const_iterator Iterator; + typedef ReduceKernel::value_type > Reducer; + typedef FilteredReducedKernel FilteredReduceType; + typedef SequenceHolder2 SequenceHolderType; + return startThreadEngine(new SequenceHolderType(sequence, mapFunctor, reduceFunctor, options)); +} + + +template +inline ThreadEngineStarter startFilteredReduced(Iterator begin, Iterator end, + MapFunctor mapFunctor, ReduceFunctor reduceFunctor, + ReduceOptions options) +{ + typedef ReduceKernel::value_type> Reducer; + typedef FilteredReducedKernel FilteredReduceType; + return startThreadEngine(new FilteredReduceType(begin, end, mapFunctor, reduceFunctor, options)); +} + + +} // namespace QtConcurrent + +#endif // qdoc + +QT_END_NAMESPACE +QT_END_HEADER + +#endif // QT_NO_CONCURRENT + +#endif diff --git a/src/corelib/concurrent/qtconcurrentfunctionwrappers.h b/src/corelib/concurrent/qtconcurrentfunctionwrappers.h new file mode 100644 index 0000000000..f31f7d2877 --- /dev/null +++ b/src/corelib/concurrent/qtconcurrentfunctionwrappers.h @@ -0,0 +1,173 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTCONCURRENT_FUNCTIONWRAPPERS_H +#define QTCONCURRENT_FUNCTIONWRAPPERS_H + +#include + +#ifndef QT_NO_CONCURRENT + +QT_BEGIN_HEADER +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +#ifndef qdoc + +namespace QtConcurrent { + +template +class FunctionWrapper0 +{ +public: + typedef T (*FunctionPointerType)(); + typedef T result_type; + inline FunctionWrapper0(FunctionPointerType _functionPointer) + :functionPointer(_functionPointer) { } + + inline T operator()() + { + return functionPointer(); + } +private: + FunctionPointerType functionPointer; +}; + +template +class FunctionWrapper1 +{ +public: + typedef T (*FunctionPointerType)(U u); + typedef T result_type; + inline FunctionWrapper1(FunctionPointerType _functionPointer) + :functionPointer(_functionPointer) { } + + inline T operator()(U u) + { + return functionPointer(u); + } + +private: + FunctionPointerType functionPointer; +}; + +template +class FunctionWrapper2 +{ +public: + typedef T (*FunctionPointerType)(U u, V v); + typedef T result_type; + inline FunctionWrapper2(FunctionPointerType _functionPointer) + :functionPointer(_functionPointer) { } + + inline T operator()(U u, V v) + { + return functionPointer(u, v); + } +private: + FunctionPointerType functionPointer; +}; + +template +class MemberFunctionWrapper +{ +public: + typedef T (C::*FunctionPointerType)(); + typedef T result_type; + inline MemberFunctionWrapper(FunctionPointerType _functionPointer) + :functionPointer(_functionPointer) { } + + inline T operator()(C &c) + { + return (c.*functionPointer)(); + } +private: + FunctionPointerType functionPointer; +}; + +template +class MemberFunctionWrapper1 +{ +public: + typedef T (C::*FunctionPointerType)(U); + typedef T result_type; + + inline MemberFunctionWrapper1(FunctionPointerType _functionPointer) + : functionPointer(_functionPointer) + { } + + inline T operator()(C &c, U u) + { + return (c.*functionPointer)(u); + } + +private: + FunctionPointerType functionPointer; +}; + +template +class ConstMemberFunctionWrapper +{ +public: + typedef T (C::*FunctionPointerType)() const; + typedef T result_type; + inline ConstMemberFunctionWrapper(FunctionPointerType _functionPointer) + :functionPointer(_functionPointer) { } + + inline T operator()(const C &c) const + { + return (c.*functionPointer)(); + } +private: + FunctionPointerType functionPointer; +}; + +} // namespace QtConcurrent. + +#endif //qdoc + +QT_END_NAMESPACE +QT_END_HEADER + +#endif // QT_NO_CONCURRENT + +#endif diff --git a/src/corelib/concurrent/qtconcurrentiteratekernel.cpp b/src/corelib/concurrent/qtconcurrentiteratekernel.cpp new file mode 100644 index 0000000000..a59e6cfa47 --- /dev/null +++ b/src/corelib/concurrent/qtconcurrentiteratekernel.cpp @@ -0,0 +1,199 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qtconcurrentiteratekernel.h" + +#if defined(Q_OS_MAC) +#include +#include +#include +#elif defined(Q_OS_UNIX) +#if defined(Q_OS_HURD) +#include +#endif +#include +#include +#elif defined(Q_OS_WIN) +#include +#endif + +#include "private/qfunctions_p.h" + + +#ifndef QT_NO_CONCURRENT + +QT_BEGIN_NAMESPACE + +enum { + TargetRatio = 100, + MedianSize = 7 +}; + +#if defined(Q_OS_MAC) + +static qint64 getticks() +{ + return mach_absolute_time(); +} + +#elif defined(Q_OS_UNIX) + + +static qint64 getticks() +{ +#if defined(_POSIX_TIMERS) && (_POSIX_TIMERS > 0) + clockid_t clockId; + +#ifndef _POSIX_THREAD_CPUTIME + clockId = CLOCK_REALTIME; +#elif (_POSIX_THREAD_CPUTIME-0 <= 0) + // if we don't have CLOCK_THREAD_CPUTIME_ID, we have to just use elapsed realtime instead + clockId = CLOCK_REALTIME; + +# if (_POSIX_THREAD_CPUTIME-0 == 0) + // detect availablility of CLOCK_THREAD_CPUTIME_ID + static long useThreadCpuTime = -2; + if (useThreadCpuTime == -2) { + // sysconf() will return either -1 or _POSIX_VERSION (don't care about thread races here) + useThreadCpuTime = sysconf(_SC_THREAD_CPUTIME); + } + if (useThreadCpuTime != -1) + clockId = CLOCK_THREAD_CPUTIME_ID; +# endif +#else + clockId = CLOCK_THREAD_CPUTIME_ID; +#endif + + struct timespec ts; + if (clock_gettime(clockId, &ts) == -1) + return 0; + return (ts.tv_sec * 1000000000) + ts.tv_nsec; +#else + +#ifdef Q_OS_SYMBIAN + return clock(); +#else + // no clock_gettime(), fall back to wall time + struct timeval tv; + gettimeofday(&tv, 0); + return (tv.tv_sec * 1000000) + tv.tv_usec; +#endif + +#endif +} + +#elif defined(Q_OS_WIN) + +static qint64 getticks() +{ + LARGE_INTEGER x; + if (!QueryPerformanceCounter(&x)) + return 0; + return x.QuadPart; +} + +#endif + +static double elapsed(qint64 after, qint64 before) +{ + return double(after - before); +} + +namespace QtConcurrent { + +/*! \internal + +*/ +BlockSizeManager::BlockSizeManager(int iterationCount) +: maxBlockSize(iterationCount / (QThreadPool::globalInstance()->maxThreadCount() * 2)), + beforeUser(0), afterUser(0), + controlPartElapsed(MedianSize), userPartElapsed(MedianSize), + m_blockSize(1) +{ } + +// Records the time before user code. +void BlockSizeManager::timeBeforeUser() +{ + if (blockSizeMaxed()) + return; + + beforeUser = getticks(); + controlPartElapsed.addValue(elapsed(beforeUser, afterUser)); +} + + // Records the time after user code and adjust the block size if we are spending + // to much time in the for control code compared with the user code. +void BlockSizeManager::timeAfterUser() +{ + if (blockSizeMaxed()) + return; + + afterUser = getticks(); + userPartElapsed.addValue(elapsed(afterUser, beforeUser)); + + if (controlPartElapsed.isMedianValid() == false) + return; + + if (controlPartElapsed.median() * TargetRatio < userPartElapsed.median()) + return; + + m_blockSize = qMin(m_blockSize * 2, maxBlockSize); + +#ifdef QTCONCURRENT_FOR_DEBUG + qDebug() << QThread::currentThread() << "adjusting block size" << controlPartElapsed.median() << userPartElapsed.median() << m_blockSize; +#endif + + // Reset the medians after adjusting the block size so we get + // new measurements with the new block size. + controlPartElapsed.reset(); + userPartElapsed.reset(); +} + +int BlockSizeManager::blockSize() +{ + return m_blockSize; +} + +} // namespace QtConcurrent + +QT_END_NAMESPACE + +#endif // QT_NO_CONCURRENT diff --git a/src/corelib/concurrent/qtconcurrentiteratekernel.h b/src/corelib/concurrent/qtconcurrentiteratekernel.h new file mode 100644 index 0000000000..186f752e63 --- /dev/null +++ b/src/corelib/concurrent/qtconcurrentiteratekernel.h @@ -0,0 +1,340 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTCONCURRENT_ITERATEKERNEL_H +#define QTCONCURRENT_ITERATEKERNEL_H + +#include + +#ifndef QT_NO_CONCURRENT + +#include +#include +#include + +#ifndef QT_NO_STL +# include +#endif + +QT_BEGIN_HEADER +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +#ifndef qdoc + +namespace QtConcurrent { + +#ifndef QT_NO_STL + using std::advance; +#else + template + void advance(It &it, T value) + { + it+=value; + } +#endif + +/* + The BlockSizeManager class manages how many iterations a thread should + reserve and process at a time. This is done by measuring the time spent + in the user code versus the control part code, and then increasing + the block size if the ratio between them is to small. The block size + management is done on the basis of the median of several timing measuremens, + and it is done induvidualy for each thread. +*/ +class Q_CORE_EXPORT BlockSizeManager +{ +public: + BlockSizeManager(int iterationCount); + void timeBeforeUser(); + void timeAfterUser(); + int blockSize(); +private: + inline bool blockSizeMaxed() + { + return (m_blockSize >= maxBlockSize); + } + + const int maxBlockSize; + qint64 beforeUser; + qint64 afterUser; + Median controlPartElapsed; + Median userPartElapsed; + int m_blockSize; +}; + +template +class ResultReporter +{ +public: + ResultReporter(ThreadEngine *_threadEngine) + :threadEngine(_threadEngine) + { + + } + + void reserveSpace(int resultCount) + { + currentResultCount = resultCount; + vector.resize(qMax(resultCount, vector.count())); + } + + void reportResults(int begin) + { + const int useVectorThreshold = 4; // Tunable parameter. + if (currentResultCount > useVectorThreshold) { + vector.resize(currentResultCount); + threadEngine->reportResults(vector, begin); + } else { + for (int i = 0; i < currentResultCount; ++i) + threadEngine->reportResult(&vector.at(i), begin + i); + } + } + + inline T * getPointer() + { + return vector.data(); + } + + int currentResultCount; + ThreadEngine *threadEngine; + QVector vector; +}; + +template <> +class ResultReporter +{ +public: + inline ResultReporter(ThreadEngine *) { } + inline void reserveSpace(int) { }; + inline void reportResults(int) { }; + inline void * getPointer() { return 0; } +}; + +#ifndef QT_NO_STL +inline bool selectIteration(std::bidirectional_iterator_tag) +{ + return false; // while +} + +inline bool selectIteration(std::forward_iterator_tag) +{ + return false; // while +} + +inline bool selectIteration(std::random_access_iterator_tag) +{ + return true; // for +} +#else +// no stl support, always use while iteration +template +inline bool selectIteration(T) +{ + return false; // while +} +#endif + +template +class IterateKernel : public ThreadEngine +{ +public: + typedef T ResultType; + + IterateKernel(Iterator _begin, Iterator _end) +#if defined (QT_NO_STL) + : begin(_begin), end(_end), current(_begin), currentIndex(0), + forIteration(false), progressReportingEnabled(true) +#else + : begin(_begin), end(_end), current(_begin), currentIndex(0), + forIteration(selectIteration(typename std::iterator_traits::iterator_category())), progressReportingEnabled(true) +#endif + { +#if defined (QT_NO_STL) + iterationCount = 0; +#else + iterationCount = forIteration ? std::distance(_begin, _end) : 0; + +#endif + } + + virtual ~IterateKernel() { } + + virtual bool runIteration(Iterator it, int index , T *result) + { Q_UNUSED(it); Q_UNUSED(index); Q_UNUSED(result); return false; } + virtual bool runIterations(Iterator _begin, int beginIndex, int endIndex, T *results) + { Q_UNUSED(_begin); Q_UNUSED(beginIndex); Q_UNUSED(endIndex); Q_UNUSED(results); return false; } + + void start() + { + progressReportingEnabled = this->isProgressReportingEnabled(); + if (progressReportingEnabled && iterationCount > 0) + this->setProgressRange(0, iterationCount); + } + + bool shouldStartThread() + { + if (forIteration) + return (currentIndex < iterationCount) && !this->shouldThrottleThread(); + else // whileIteration + return (iteratorThreads == 0); + } + + ThreadFunctionResult threadFunction() + { + if (forIteration) + return this->forThreadFunction(); + else // whileIteration + return this->whileThreadFunction(); + } + + ThreadFunctionResult forThreadFunction() + { + BlockSizeManager blockSizeManager(iterationCount); + ResultReporter resultReporter(this); + + for(;;) { + if (this->isCanceled()) + break; + + const int currentBlockSize = blockSizeManager.blockSize(); + + if (currentIndex >= iterationCount) + break; + + // Atomically reserve a block of iterationCount for this thread. + const int beginIndex = currentIndex.fetchAndAddRelease(currentBlockSize); + const int endIndex = qMin(beginIndex + currentBlockSize, iterationCount); + + if (beginIndex >= endIndex) { + // No more work + break; + } + + this->waitForResume(); // (only waits if the qfuture is paused.) + + if (shouldStartThread()) + this->startThread(); + + const int finalBlockSize = endIndex - beginIndex; // block size adjusted for possible end-of-range + resultReporter.reserveSpace(finalBlockSize); + + // Call user code with the current iteration range. + blockSizeManager.timeBeforeUser(); + const bool resultsAvailable = this->runIterations(begin, beginIndex, endIndex, resultReporter.getPointer()); + blockSizeManager.timeAfterUser(); + + if (resultsAvailable) + resultReporter.reportResults(beginIndex); + + // Report progress if progress reporting enabled. + if (progressReportingEnabled) { + completed.fetchAndAddAcquire(finalBlockSize); + this->setProgressValue(this->completed); + } + + if (this->shouldThrottleThread()) + return ThrottleThread; + } + return ThreadFinished; + } + + ThreadFunctionResult whileThreadFunction() + { + if (iteratorThreads.testAndSetAcquire(0, 1) == false) + return ThreadFinished; + + ResultReporter resultReporter(this); + resultReporter.reserveSpace(1); + + while (current != end) { + // The following two lines breaks support for input iterators according to + // the sgi docs: dereferencing prev after calling ++current is not allowed + // on input iterators. (prev is dereferenced inside user.runIteration()) + Iterator prev = current; + ++current; + int index = currentIndex.fetchAndAddRelaxed(1); + iteratorThreads.testAndSetRelease(1, 0); + + this->waitForResume(); // (only waits if the qfuture is paused.) + + if (shouldStartThread()) + this->startThread(); + + const bool resultAavailable = this->runIteration(prev, index, resultReporter.getPointer()); + if (resultAavailable) + resultReporter.reportResults(index); + + if (this->shouldThrottleThread()) + return ThrottleThread; + + if (iteratorThreads.testAndSetAcquire(0, 1) == false) + return ThreadFinished; + } + + return ThreadFinished; + } + + +public: + const Iterator begin; + const Iterator end; + Iterator current; + QAtomicInt currentIndex; + bool forIteration; + QAtomicInt iteratorThreads; + int iterationCount; + + bool progressReportingEnabled; + QAtomicInt completed; +}; + +} // namespace QtConcurrent + +#endif //qdoc + +QT_END_NAMESPACE +QT_END_HEADER + +#endif // QT_NO_CONCURRENT + +#endif diff --git a/src/corelib/concurrent/qtconcurrentmap.cpp b/src/corelib/concurrent/qtconcurrentmap.cpp new file mode 100644 index 0000000000..fe083f17a2 --- /dev/null +++ b/src/corelib/concurrent/qtconcurrentmap.cpp @@ -0,0 +1,402 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \namespace QtConcurrent + \inmodule QtCore + \since 4.4 + \brief The QtConcurrent namespace provides high-level APIs that make it + possible to write multi-threaded programs without using low-level + threading primitives. + + See the \l {Concurrent Programming}{Qt Concurrent} chapter in + the \l{threads.html}{threading} documentation. + + \inheaderfile QtCore + \ingroup thread +*/ + +/*! + \namespace QtConcurrent::internal + \internal + + \brief The QtConcurrent::internal namespace contains QtConcurrent + implementation details. +*/ + +/*! + \enum QtConcurrent::ReduceOption + This enum specifies the order of which results from the map or filter + function are passed to the reduce function. + + \value UnorderedReduce Reduction is done in an arbitrary order. + \value OrderedReduce Reduction is done in the order of the + original sequence. + \value SequentialReduce Reduction is done sequentially: only one + thread will enter the reduce function at a time. (Parallel reduction + might be supported in a future version of Qt Concurrent.) +*/ + +/*! + \headerfile + \title Concurrent Map and Map-Reduce + \ingroup thread + + \brief The header provides concurrent Map and MapReduce. + + These functions are a part of the \l {Concurrent Programming}{Qt Concurrent} framework. + + The QtConcurrent::map(), QtConcurrent::mapped() and + QtConcurrent::mappedReduced() functions run computations in parallel on + the items in a sequence such as a QList or a QVector. QtConcurrent::map() + modifies a sequence in-place, QtConcurrent::mapped() returns a new + sequence containing the modified content, and QtConcurrent::mappedReduced() + returns a single result. + + Each of the above functions has a blocking variant that returns + the final result instead of a QFuture. You use them in the same + way as the asynchronous variants. + + \snippet doc/src/snippets/code/src_corelib_concurrent_qtconcurrentmap.cpp 7 + + Note that the result types above are not QFuture objects, but real result + types (in this case, QList and QImage). + + \section1 Concurrent Map + + QtConcurrent::mapped() takes an input sequence and a map function. This map + function is then called for each item in the sequence, and a new sequence + containing the return values from the map function is returned. + + The map function must be of the form: + + \snippet doc/src/snippets/code/src_corelib_concurrent_qtconcurrentmap.cpp 0 + + T and U can be any type (and they can even be the same type), but T must + match the type stored in the sequence. The function returns the modified + or \e mapped content. + + This example shows how to apply a scale function to all the items + in a sequence: + + \snippet doc/src/snippets/code/src_corelib_concurrent_qtconcurrentmap.cpp 1 + + The results of the map are made available through QFuture. See the + QFuture and QFutureWatcher documentation for more information on how to + use QFuture in your applications. + + If you want to modify a sequence in-place, use QtConcurrent::map(). The + map function must then be of the form: + + \snippet doc/src/snippets/code/src_corelib_concurrent_qtconcurrentmap.cpp 2 + + Note that the return value and return type of the map function are not + used. + + Using QtConcurrent::map() is similar to using QtConcurrent::mapped(): + + \snippet doc/src/snippets/code/src_corelib_concurrent_qtconcurrentmap.cpp 3 + + Since the sequence is modified in place, QtConcurrent::map() does not + return any results via QFuture. However, you can still use QFuture and + QFutureWatcher to monitor the status of the map. + + \section1 Concurrent Map-Reduce + + QtConcurrent::mappedReduced() is similar to QtConcurrent::mapped(), but + instead of returning a sequence with the new results, the results are + combined into a single value using a reduce function. + + The reduce function must be of the form: + + \snippet doc/src/snippets/code/src_corelib_concurrent_qtconcurrentmap.cpp 4 + + T is the type of the final result, U is the return type of the map + function. Note that the return value and return type of the reduce + function are not used. + + Call QtConcurrent::mappedReduced() like this: + + \snippet doc/src/snippets/code/src_corelib_concurrent_qtconcurrentmap.cpp 5 + + The reduce function will be called once for each result returned by the map + function, and should merge the \e{intermediate} into the \e{result} + variable. QtConcurrent::mappedReduced() guarantees that only one thread + will call reduce at a time, so using a mutex to lock the result variable + is not necessary. The QtConcurrent::ReduceOptions enum provides a way to + control the order in which the reduction is done. If + QtConcurrent::UnorderedReduce is used (the default), the order is + undefined, while QtConcurrent::OrderedReduce ensures that the reduction + is done in the order of the original sequence. + + \section1 Additional API Features + + \section2 Using Iterators instead of Sequence + + Each of the above functions has a variant that takes an iterator range + instead of a sequence. You use them in the same way as the sequence + variants: + + \snippet doc/src/snippets/code/src_corelib_concurrent_qtconcurrentmap.cpp 6 + + \section2 Blocking Variants + + Each of the above functions has a blocking variant that returns + the final result instead of a QFuture. You use them in the same + way as the asynchronous variants. + + \snippet doc/src/snippets/code/src_corelib_concurrent_qtconcurrentmap.cpp 7 + + Note that the result types above are not QFuture objects, but real result + types (in this case, QList and QImage). + + \section2 Using Member Functions + + QtConcurrent::map(), QtConcurrent::mapped(), and + QtConcurrent::mappedReduced() accept pointers to member functions. + The member function class type must match the type stored in the sequence: + + \snippet doc/src/snippets/code/src_corelib_concurrent_qtconcurrentmap.cpp 8 + + Note that when using QtConcurrent::mappedReduced(), you can mix the use of + normal and member functions freely: + + \snippet doc/src/snippets/code/src_corelib_concurrent_qtconcurrentmap.cpp 9 + + \section2 Using Function Objects + + QtConcurrent::map(), QtConcurrent::mapped(), and + QtConcurrent::mappedReduced() accept function objects, which can be used to + add state to a function call. The result_type typedef must define the + result type of the function call operator: + + \snippet doc/src/snippets/code/src_corelib_concurrent_qtconcurrentmap.cpp 14 + + \section2 Using Bound Function Arguments + + Note that Qt does not provide support for bound functions. This is + provided by 3rd party libraries like + \l{http://www.boost.org/libs/bind/bind.html}{Boost} or + \l{http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1836.pdf}{C++ + TR1 Library Extensions}. + + If you want to use a map function that takes more than one argument you can + use boost::bind() or std::tr1::bind() to transform it onto a function that + takes one argument. + + As an example, we'll use QImage::scaledToWidth(): + + \snippet doc/src/snippets/code/src_corelib_concurrent_qtconcurrentmap.cpp 10 + + scaledToWidth takes three arguments (including the "this" pointer) and + can't be used with QtConcurrent::mapped() directly, because + QtConcurrent::mapped() expects a function that takes one argument. To use + QImage::scaledToWidth() with QtConcurrent::mapped() we have to provide a + value for the \e{width} and the \e{transformation mode}: + + \snippet doc/src/snippets/code/src_corelib_concurrent_qtconcurrentmap.cpp 11 + + The return value from boost::bind() is a function object (functor) with + the following signature: + + \snippet doc/src/snippets/code/src_corelib_concurrent_qtconcurrentmap.cpp 12 + + This matches what QtConcurrent::mapped() expects, and the complete example + becomes: + + \snippet doc/src/snippets/code/src_corelib_concurrent_qtconcurrentmap.cpp 13 +*/ + +/*! + \fn QFuture QtConcurrent::map(Sequence &sequence, MapFunction function) + \relates + + Calls \a function once for each item in \a sequence. The \a function is + passed a reference to the item, so that any modifications done to the item + will appear in \a sequence. +*/ + +/*! + \fn QFuture QtConcurrent::map(Iterator begin, Iterator end, MapFunction function) + \relates + + Calls \a function once for each item from \a begin to \a end. The + \a function is passed a reference to the item, so that any modifications + done to the item will appear in the sequence which the iterators belong to. +*/ + +/*! + \fn QFuture QtConcurrent::mapped(const Sequence &sequence, MapFunction function) + \relates + + Calls \a function once for each item in \a sequence and returns a future + with each mapped item as a result. You can use QFuture::const_iterator or + QFutureIterator to iterate through the results. +*/ + +/*! + \fn QFuture QtConcurrent::mapped(ConstIterator begin, ConstIterator end, MapFunction function) + \relates + + Calls \a function once for each item from \a begin to \a end and returns a + future with each mapped item as a result. You can use + QFuture::const_iterator or QFutureIterator to iterate through the results. +*/ + +/*! + \fn QFuture QtConcurrent::mappedReduced(const Sequence &sequence, + MapFunction mapFunction, ReduceFunction reduceFunction, + QtConcurrent::ReduceOptions reduceOptions) + + \relates + + Calls \a mapFunction once for each item in \a sequence. The return value of + each \a mapFunction is passed to \a reduceFunction. + + Note that while \a mapFunction is called concurrently, only one thread at a + time will call \a reduceFunction. The order in which \a reduceFunction is + called is determined by \a reduceOptions. +*/ + +/*! + \fn QFuture QtConcurrent::mappedReduced(ConstIterator begin, + ConstIterator end, MapFunction mapFunction, ReduceFunction reduceFunction, + QtConcurrent::ReduceOptions reduceOptions) + + \relates + + Calls \a mapFunction once for each item from \a begin to \a end. The return + value of each \a mapFunction is passed to \a reduceFunction. + + Note that while \a mapFunction is called concurrently, only one thread at a + time will call \a reduceFunction. By default, the order in which + \a reduceFunction is called is undefined. + + \note QtConcurrent::OrderedReduce results in the ordered reduction. +*/ + +/*! + \fn void QtConcurrent::blockingMap(Sequence &sequence, MapFunction function) + + Calls \a function once for each item in \a sequence. The \a function is + passed a reference to the item, so that any modifications done to the item + will appear in \a sequence. + + \note This function will block until all items in the sequence have been processed. + + \sa map() +*/ + +/*! + \fn void QtConcurrent::blockingMap(Iterator begin, Iterator end, MapFunction function) + + Calls \a function once for each item from \a begin to \a end. The + \a function is passed a reference to the item, so that any modifications + done to the item will appear in the sequence which the iterators belong to. + + \note This function will block until the iterator reaches the end of the + sequence being processed. + + \sa map() +*/ + +/*! + \fn T QtConcurrent::blockingMapped(const Sequence &sequence, MapFunction function) + + Calls \a function once for each item in \a sequence and returns a Sequence containing + the results. The type of the results will match the type returned my the MapFunction. + + \note This function will block until all items in the sequence have been processed. + + \sa mapped() +*/ + +/*! + \fn T QtConcurrent::blockingMapped(ConstIterator begin, ConstIterator end, MapFunction function) + + Calls \a function once for each item from \a begin to \a end and returns a + container with the results. Specify the type of container as the a template + argument, like this: + + \code + QList ints = QtConcurrent::blockingMapped >(beginIterator, endIterator, fn); + \endcode + + \note This function will block until the iterator reaches the end of the + sequence being processed. + + \sa mapped() +*/ + +/*! + \fn T QtConcurrent::blockingMappedReduced(const Sequence &sequence, MapFunction mapFunction, ReduceFunction reduceFunction, QtConcurrent::ReduceOptions reduceOptions) + + \relates + + Calls \a mapFunction once for each item in \a sequence. The return value of + each \a mapFunction is passed to \a reduceFunction. + + Note that while \a mapFunction is called concurrently, only one thread at a + time will call \a reduceFunction. The order in which \a reduceFunction is + called is determined by \a reduceOptions. + + \note This function will block until all items in the sequence have been processed. + + \sa mapped() +*/ + +/*! + \fn T QtConcurrent::blockingMappedReduced(ConstIterator begin, ConstIterator end, MapFunction mapFunction, ReduceFunction reduceFunction, QtConcurrent::ReduceOptions reduceOptions) + + \relates + + Calls \a mapFunction once for each item from \a begin to \a end. The return + value of each \a mapFunction is passed to \a reduceFunction. + + Note that while \a mapFunction is called concurrently, only one thread at a + time will call \a reduceFunction. The order in which \a reduceFunction is + called is undefined. + + \note This function will block until the iterator reaches the end of the + sequence being processed. + + \sa blockingMappedReduced() +*/ diff --git a/src/corelib/concurrent/qtconcurrentmap.h b/src/corelib/concurrent/qtconcurrentmap.h new file mode 100644 index 0000000000..80edf7e136 --- /dev/null +++ b/src/corelib/concurrent/qtconcurrentmap.h @@ -0,0 +1,780 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTCONCURRENT_MAP_H +#define QTCONCURRENT_MAP_H + +#include + +#ifndef QT_NO_CONCURRENT + +#include +#include +#include +#include + +QT_BEGIN_HEADER +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +#ifdef qdoc + +namespace QtConcurrent { + + QFuture map(Sequence &sequence, MapFunction function); + QFuture map(Iterator begin, Iterator end, MapFunction function); + + template + QFuture mapped(const Sequence &sequence, MapFunction function); + template + QFuture mapped(ConstIterator begin, ConstIterator end, MapFunction function); + + template + QFuture mappedReduced(const Sequence &sequence, + MapFunction function, + ReduceFunction function, + QtConcurrent::ReduceOptions options = UnorderedReduce | SequentialReduce); + template + QFuture mappedReduced(ConstIterator begin, + ConstIterator end, + MapFunction function, + ReduceFunction function, + QtConcurrent::ReduceOptions options = UnorderedReduce | SequentialReduce); + + void blockingMap(Sequence &sequence, MapFunction function); + void blockingMap(Iterator begin, Iterator end, MapFunction function); + + template + T blockingMapped(const Sequence &sequence, MapFunction function); + template + T blockingMapped(ConstIterator begin, ConstIterator end, MapFunction function); + + template + T blockingMappedReduced(const Sequence &sequence, + MapFunction function, + ReduceFunction function, + QtConcurrent::ReduceOptions options = UnorderedReduce | SequentialReduce); + template + T blockingMappedReduced(ConstIterator begin, + ConstIterator end, + MapFunction function, + ReduceFunction function, + QtConcurrent::ReduceOptions options = UnorderedReduce | SequentialReduce); + +} // namespace QtConcurrent + +#else + +namespace QtConcurrent { + +// map() on sequences +template +QFuture map(Sequence &sequence, MapFunctor map) +{ + return startMap(sequence.begin(), sequence.end(), map); +} + +template +QFuture map(Sequence &sequence, T (map)(U)) +{ + return startMap(sequence.begin(), sequence.end(), FunctionWrapper1(map)); +} + +template +QFuture map(Sequence &sequence, T (C::*map)()) +{ + return startMap(sequence.begin(), sequence.end(), MemberFunctionWrapper(map)); +} + +// map() on iterators +template +QFuture map(Iterator begin, Iterator end, MapFunctor map) +{ + return startMap(begin, end, map); +} + +template +QFuture map(Iterator begin, Iterator end, T (map)(U)) +{ + return startMap(begin, end, FunctionWrapper1(map)); +} + +template +QFuture map(Iterator begin, Iterator end, T (C::*map)()) +{ + return startMap(begin, end, MemberFunctionWrapper(map)); +} + +// mappedReduced() for sequences. +template +QFuture mappedReduced(const Sequence &sequence, + MapFunctor map, + ReduceFunctor reduce, + ReduceOptions options = ReduceOptions(UnorderedReduce | SequentialReduce)) +{ + return startMappedReduced + (sequence, map, reduce, options); +} + +template +QFuture mappedReduced(const Sequence &sequence, + MapFunctor map, + T (reduce)(U &, V), + ReduceOptions options = ReduceOptions(UnorderedReduce | SequentialReduce)) +{ + return startMappedReduced + (sequence, map, FunctionWrapper2(reduce), options); +} + +template +QFuture mappedReduced(const Sequence &sequence, + MapFunctor map, + T (C::*reduce)(U), + ReduceOptions options = ReduceOptions(UnorderedReduce | SequentialReduce)) +{ + return startMappedReduced + (sequence, map, MemberFunctionWrapper1(reduce), options); +} + +template +QFuture mappedReduced(const Sequence &sequence, + T (map)(U), + ReduceFunctor reduce, + ReduceOptions options = ReduceOptions(UnorderedReduce | SequentialReduce)) +{ + return startMappedReduced + (sequence, FunctionWrapper1(map), reduce, options); +} + +template +QFuture mappedReduced(const Sequence &sequence, + T (C::*map)() const, + ReduceFunctor reduce, + ReduceOptions options = ReduceOptions(UnorderedReduce | SequentialReduce)) +{ + return startMappedReduced + (sequence, ConstMemberFunctionWrapper(map), reduce, options); +} + +template +QFuture mappedReduced(const Sequence &sequence, + T (map)(U), + V (reduce)(W &, X), + ReduceOptions options = ReduceOptions(UnorderedReduce | SequentialReduce)) +{ + return startMappedReduced + (sequence, FunctionWrapper1(map), FunctionWrapper2(reduce), options); +} + +template +QFuture mappedReduced(const Sequence &sequence, + T (C::*map)() const, + U (reduce)(V &, W), + ReduceOptions options = ReduceOptions(UnorderedReduce | SequentialReduce)) +{ + return startMappedReduced (sequence, ConstMemberFunctionWrapper(map), + FunctionWrapper2(reduce), options); +} + +template +QFuture mappedReduced(const Sequence &sequence, + T (map)(U), + V (C::*reduce)(W), + ReduceOptions options = ReduceOptions(UnorderedReduce | SequentialReduce)) +{ + return startMappedReduced (sequence, FunctionWrapper1(map), + MemberFunctionWrapper1(reduce), options); +} + +template +QFuture mappedReduced(const Sequence &sequence, + T (C::*map)() const, + U (D::*reduce)(V), + ReduceOptions options = ReduceOptions(UnorderedReduce | SequentialReduce)) +{ + return startMappedReduced(sequence, ConstMemberFunctionWrapper(map), + MemberFunctionWrapper1(reduce), options); +} + +// mappedReduced() for iterators +template +QFuture mappedReduced(Iterator begin, + Iterator end, + MapFunctor map, + ReduceFunctor reduce, + ReduceOptions options = ReduceOptions(UnorderedReduce | SequentialReduce)) +{ + return startMappedReduced + (begin, end, map, reduce, options); +} + +template +QFuture mappedReduced(Iterator begin, + Iterator end, + MapFunctor map, + T (reduce)(U &, V), + ReduceOptions options = ReduceOptions(UnorderedReduce | SequentialReduce)) +{ + return startMappedReduced + (begin, end, map, FunctionWrapper2(reduce), options); +} + +template +QFuture mappedReduced(Iterator begin, + Iterator end, + MapFunctor map, + T (C::*reduce)(U), + ReduceOptions options = ReduceOptions(UnorderedReduce | SequentialReduce)) +{ + return startMappedReduced + (begin, end, map, MemberFunctionWrapper1(reduce), options); +} + +template +QFuture mappedReduced(Iterator begin, + Iterator end, + T (map)(U), + ReduceFunctor reduce, + ReduceOptions options = ReduceOptions(UnorderedReduce | SequentialReduce)) +{ + return startMappedReduced + (begin, end, FunctionWrapper1(map), reduce, options); +} + +template +QFuture mappedReduced(Iterator begin, + Iterator end, + T (C::*map)() const, + ReduceFunctor reduce, + ReduceOptions options = ReduceOptions(UnorderedReduce | SequentialReduce)) +{ + return startMappedReduced + (begin, end, ConstMemberFunctionWrapper(map), reduce, options); +} + +template +QFuture mappedReduced(Iterator begin, + Iterator end, + T (map)(U), + V (reduce)(W &, X), + ReduceOptions options = ReduceOptions(UnorderedReduce | SequentialReduce)) +{ + return startMappedReduced + (begin, end, FunctionWrapper1(map), FunctionWrapper2(reduce), options); +} + +template +QFuture mappedReduced(Iterator begin, + Iterator end, + T (C::*map)() const, + U (reduce)(V &, W), + ReduceOptions options = ReduceOptions(UnorderedReduce | SequentialReduce)) +{ + return startMappedReduced(begin, end, ConstMemberFunctionWrapper(map), + FunctionWrapper2(reduce), options); +} + +template +QFuture mappedReduced(Iterator begin, + Iterator end, + T (map)(U), + V (C::*reduce)(W), + ReduceOptions options = ReduceOptions(UnorderedReduce | SequentialReduce)) +{ + return startMappedReduced + (begin, end, FunctionWrapper1(map), MemberFunctionWrapper1(reduce), options); +} + +template +QFuture mappedReduced(Iterator begin, + Iterator end, + T (C::*map)() const, + U (D::*reduce)(V), + ReduceOptions options = ReduceOptions(UnorderedReduce | SequentialReduce)) +{ + return startMappedReduced(begin, end, ConstMemberFunctionWrapper(map), + MemberFunctionWrapper1(reduce), options); +} + +// mapped() for sequences +template +QFuture mapped(const Sequence &sequence, MapFunctor map) +{ + return startMapped(sequence, map); +} + +template +QFuture mapped(const Sequence &sequence, T (map)(U)) +{ + return startMapped(sequence, FunctionWrapper1(map)); +} + +template +QFuture mapped(const Sequence &sequence, T (C::*map)() const) +{ + return startMapped(sequence, ConstMemberFunctionWrapper(map)); +} + +// mapped() for iterator ranges. +template +QFuture mapped(Iterator begin, Iterator end, MapFunctor map) +{ + return startMapped(begin, end, map); +} + +template +QFuture mapped(Iterator begin, Iterator end, T (map)(U)) +{ + return startMapped(begin, end, FunctionWrapper1(map)); +} + +template +QFuture mapped(Iterator begin, Iterator end, T (C::*map)() const) +{ + return startMapped(begin, end, ConstMemberFunctionWrapper(map)); +} + + +template +void blockingMap(Sequence &sequence, MapFunctor map) +{ + startMap(sequence.begin(), sequence.end(), map).startBlocking(); +} + +template +void blockingMap(Sequence &sequence, T (map)(U)) +{ + startMap(sequence.begin(), sequence.end(), QtConcurrent::FunctionWrapper1(map)).startBlocking(); +} + +template +void blockingMap(Sequence &sequence, T (C::*map)()) +{ + startMap(sequence.begin(), sequence.end(), QtConcurrent::MemberFunctionWrapper(map)).startBlocking(); +} + +template +void blockingMap(Iterator begin, Iterator end, MapFunctor map) +{ + startMap(begin, end, map).startBlocking(); +} + +template +void blockingMap(Iterator begin, Iterator end, T (map)(U)) +{ + startMap(begin, end, QtConcurrent::FunctionWrapper1(map)).startBlocking(); +} + +template +void blockingMap(Iterator begin, Iterator end, T (C::*map)()) +{ + startMap(begin, end, QtConcurrent::MemberFunctionWrapper(map)).startBlocking(); +} + +template +ResultType blockingMappedReduced(const Sequence &sequence, + MapFunctor map, + ReduceFunctor reduce, + ReduceOptions options = ReduceOptions(UnorderedReduce | SequentialReduce)) +{ + return QtConcurrent::startMappedReduced + (sequence, map, reduce, options).startBlocking(); +} + +template +U blockingMappedReduced(const Sequence &sequence, + MapFunctor map, + T (reduce)(U &, V), + QtConcurrent::ReduceOptions options = QtConcurrent::ReduceOptions(QtConcurrent::UnorderedReduce | QtConcurrent::SequentialReduce)) +{ + return QtConcurrent::startMappedReduced + (sequence, + map, + QtConcurrent::FunctionWrapper2(reduce), + options) + .startBlocking(); +} + +template +C blockingMappedReduced(const Sequence &sequence, + MapFunctor map, + T (C::*reduce)(U), + QtConcurrent::ReduceOptions options = QtConcurrent::ReduceOptions(QtConcurrent::UnorderedReduce | QtConcurrent::SequentialReduce)) +{ + return QtConcurrent::startMappedReduced + (sequence, + map, + QtConcurrent::MemberFunctionWrapper1(reduce), + options) + .startBlocking(); +} + +template +ResultType blockingMappedReduced(const Sequence &sequence, + T (map)(U), + ReduceFunctor reduce, + QtConcurrent::ReduceOptions options = QtConcurrent::ReduceOptions(QtConcurrent::UnorderedReduce | QtConcurrent::SequentialReduce)) +{ + return QtConcurrent::startMappedReduced + (sequence, + QtConcurrent::FunctionWrapper1(map), + reduce, + options) + .startBlocking(); +} + +template +ResultType blockingMappedReduced(const Sequence &sequence, + T (C::*map)() const, + ReduceFunctor reduce, + QtConcurrent::ReduceOptions options = QtConcurrent::ReduceOptions(QtConcurrent::UnorderedReduce | QtConcurrent::SequentialReduce)) +{ + return QtConcurrent::startMappedReduced + (sequence, + QtConcurrent::ConstMemberFunctionWrapper(map), + reduce, + options) + .startBlocking(); +} + +template +W blockingMappedReduced(const Sequence &sequence, + T (map)(U), + V (reduce)(W &, X), + QtConcurrent::ReduceOptions options = QtConcurrent::ReduceOptions(QtConcurrent::UnorderedReduce | QtConcurrent::SequentialReduce)) +{ + return QtConcurrent::startMappedReduced + (sequence, + QtConcurrent::FunctionWrapper1(map), + QtConcurrent::FunctionWrapper2(reduce), + options) + .startBlocking(); +} + +template +V blockingMappedReduced(const Sequence &sequence, + T (C::*map)() const, + U (reduce)(V &, W), + QtConcurrent::ReduceOptions options = QtConcurrent::ReduceOptions(QtConcurrent::UnorderedReduce | QtConcurrent::SequentialReduce)) +{ + return QtConcurrent::startMappedReduced + (sequence, + QtConcurrent::ConstMemberFunctionWrapper(map), + QtConcurrent::FunctionWrapper2(reduce), + options) + .startBlocking(); +} + +template +C blockingMappedReduced(const Sequence &sequence, + T (map)(U), + V (C::*reduce)(W), + QtConcurrent::ReduceOptions options = QtConcurrent::ReduceOptions(QtConcurrent::UnorderedReduce | QtConcurrent::SequentialReduce)) +{ + return QtConcurrent::startMappedReduced + (sequence, + QtConcurrent::FunctionWrapper1(map), + QtConcurrent::MemberFunctionWrapper1(reduce), + options) + .startBlocking(); +} + +template +D blockingMappedReduced(const Sequence &sequence, + T (C::*map)() const, + U (D::*reduce)(V), + QtConcurrent::ReduceOptions options = QtConcurrent::ReduceOptions(QtConcurrent::UnorderedReduce | QtConcurrent::SequentialReduce)) +{ + return QtConcurrent::startMappedReduced + (sequence, + QtConcurrent::ConstMemberFunctionWrapper(map), + QtConcurrent::MemberFunctionWrapper1(reduce), + options) + .startBlocking(); +} + +template +ResultType blockingMappedReduced(Iterator begin, + Iterator end, + MapFunctor map, + ReduceFunctor reduce, + QtConcurrent::ReduceOptions options = QtConcurrent::ReduceOptions(QtConcurrent::UnorderedReduce | QtConcurrent::SequentialReduce)) +{ + return QtConcurrent::startMappedReduced + (begin, end, map, reduce, options).startBlocking(); +} + +template +U blockingMappedReduced(Iterator begin, + Iterator end, + MapFunctor map, + T (reduce)(U &, V), + QtConcurrent::ReduceOptions options = QtConcurrent::ReduceOptions(QtConcurrent::UnorderedReduce | QtConcurrent::SequentialReduce)) +{ + return QtConcurrent::startMappedReduced + (begin, + end, + map, + QtConcurrent::FunctionWrapper2(reduce), + options) + .startBlocking(); +} + +template +C blockingMappedReduced(Iterator begin, + Iterator end, + MapFunctor map, + T (C::*reduce)(U), + QtConcurrent::ReduceOptions options = QtConcurrent::ReduceOptions(QtConcurrent::UnorderedReduce | QtConcurrent::SequentialReduce)) +{ + return QtConcurrent::startMappedReduced + (begin, + end, + map, + QtConcurrent::MemberFunctionWrapper1(reduce), + options) + .startBlocking(); +} + +template +ResultType blockingMappedReduced(Iterator begin, + Iterator end, + T (map)(U), + ReduceFunctor reduce, + QtConcurrent::ReduceOptions options = QtConcurrent::ReduceOptions(QtConcurrent::UnorderedReduce | QtConcurrent::SequentialReduce)) +{ + return QtConcurrent::startMappedReduced + (begin, + end, + QtConcurrent::FunctionWrapper1(map), + reduce, + options) + .startBlocking(); +} + +template +ResultType blockingMappedReduced(Iterator begin, + Iterator end, + T (C::*map)() const, + ReduceFunctor reduce, + QtConcurrent::ReduceOptions options = QtConcurrent::ReduceOptions(QtConcurrent::UnorderedReduce | QtConcurrent::SequentialReduce)) +{ + return QtConcurrent::startMappedReduced + (begin, + end, + QtConcurrent::ConstMemberFunctionWrapper(map), + reduce, + options) + .startBlocking(); +} + +template +W blockingMappedReduced(Iterator begin, + Iterator end, + T (map)(U), + V (reduce)(W &, X), + QtConcurrent::ReduceOptions options = QtConcurrent::ReduceOptions(QtConcurrent::UnorderedReduce | QtConcurrent::SequentialReduce)) +{ + return QtConcurrent::startMappedReduced + (begin, + end, + QtConcurrent::FunctionWrapper1(map), + QtConcurrent::FunctionWrapper2(reduce), + options) + .startBlocking(); +} + +template +V blockingMappedReduced(Iterator begin, + Iterator end, + T (C::*map)() const, + U (reduce)(V &, W), + QtConcurrent::ReduceOptions options = QtConcurrent::ReduceOptions(QtConcurrent::UnorderedReduce | QtConcurrent::SequentialReduce)) +{ + return QtConcurrent::startMappedReduced + (begin, + end, + QtConcurrent::ConstMemberFunctionWrapper(map), + QtConcurrent::FunctionWrapper2(reduce), + options) + .startBlocking(); +} + +template +C blockingMappedReduced(Iterator begin, + Iterator end, + T (map)(U), + V (C::*reduce)(W), + QtConcurrent::ReduceOptions options = QtConcurrent::ReduceOptions(QtConcurrent::UnorderedReduce | QtConcurrent::SequentialReduce)) +{ + return QtConcurrent::startMappedReduced + (begin, + end, + QtConcurrent::FunctionWrapper1(map), + QtConcurrent::MemberFunctionWrapper1(reduce), + options) + .startBlocking(); +} + +template +D blockingMappedReduced(Iterator begin, + Iterator end, + T (C::*map)() const, + U (D::*reduce)(V), + QtConcurrent::ReduceOptions options = QtConcurrent::ReduceOptions(QtConcurrent::UnorderedReduce | QtConcurrent::SequentialReduce)) +{ + return QtConcurrent::startMappedReduced + (begin, + end, + QtConcurrent::ConstMemberFunctionWrapper(map), + QtConcurrent::MemberFunctionWrapper1(reduce), + options) + .startBlocking(); +} + +// mapped() for sequences with a different putput sequence type. +template +OutputSequence blockingMapped(const InputSequence &sequence, MapFunctor map) +{ + return blockingMappedReduced(sequence, map, &OutputSequence::push_back, + QtConcurrent::OrderedReduce); +} + +template +OutputSequence blockingMapped(const InputSequence &sequence, T (map)(U)) +{ + return blockingMappedReduced(sequence, map, &OutputSequence::push_back, + QtConcurrent::OrderedReduce); +} + +template +OutputSequence blockingMapped(const InputSequence &sequence, T (C::*map)() const) +{ + return blockingMappedReduced(sequence, map, &OutputSequence::push_back, + QtConcurrent::OrderedReduce); +} +#ifndef QT_NO_TEMPLATE_TEMPLATE_PARAMETERS + +// overloads for changing the container value type: +template