From 727f25214e870bc5d947a2f89ada9a4a4bd07365 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Mon, 27 Aug 2012 18:33:55 +0200 Subject: Move QFuture from QtConcurrent to QtCore This class belongs to QThreadPool/QRunnable more than to QtConcurrent, so move to QtCore, where QThreadPool awaits it. Change-Id: Ibf20288a986593bf779453427c2dae8db1e1423a Reviewed-by: Qt Doc Bot Reviewed-by: Thiago Macieira --- src/concurrent/concurrent.pro | 5 - .../doc/snippets/code/src_concurrent_qfuture.cpp | 64 - src/concurrent/qfuture.cpp | 697 ---------- src/concurrent/qfuture.h | 276 ---- src/concurrent/qfutureinterface.cpp | 565 -------- src/concurrent/qfutureinterface.h | 313 ----- src/concurrent/qfutureinterface_p.h | 169 --- src/concurrent/qfuturesynchronizer.h | 2 +- src/concurrent/qfuturewatcher.h | 2 +- src/concurrent/qfuturewatcher_p.h | 2 +- src/concurrent/qtconcurrent_global.h | 4 - src/concurrent/qtconcurrentrunbase.h | 2 +- src/concurrent/qtconcurrentthreadengine.h | 2 +- .../snippets/code/src_corelib_thread_qfuture.cpp | 64 + src/corelib/thread/qfuture.h | 276 ++++ src/corelib/thread/qfuture.qdoc | 697 ++++++++++ src/corelib/thread/qfutureinterface.cpp | 565 ++++++++ src/corelib/thread/qfutureinterface.h | 311 +++++ src/corelib/thread/qfutureinterface_p.h | 167 +++ src/corelib/thread/thread.pri | 4 + tests/auto/concurrent/concurrent.pro | 1 - tests/auto/concurrent/qfuture/.gitignore | 1 - tests/auto/concurrent/qfuture/qfuture.pro | 6 - tests/auto/concurrent/qfuture/tst_qfuture.cpp | 1436 -------------------- .../tst_qfuturesynchronizer.cpp | 2 +- .../concurrent/qfuturewatcher/qfuturewatcher.pro | 2 +- tests/auto/corelib/thread/qfuture/.gitignore | 1 + tests/auto/corelib/thread/qfuture/qfuture.pro | 6 + tests/auto/corelib/thread/qfuture/tst_qfuture.cpp | 1436 ++++++++++++++++++++ tests/auto/corelib/thread/thread.pro | 1 + 30 files changed, 3535 insertions(+), 3544 deletions(-) delete mode 100644 src/concurrent/doc/snippets/code/src_concurrent_qfuture.cpp delete mode 100644 src/concurrent/qfuture.cpp delete mode 100644 src/concurrent/qfuture.h delete mode 100644 src/concurrent/qfutureinterface.cpp delete mode 100644 src/concurrent/qfutureinterface.h delete mode 100644 src/concurrent/qfutureinterface_p.h create mode 100644 src/corelib/doc/snippets/code/src_corelib_thread_qfuture.cpp create mode 100644 src/corelib/thread/qfuture.h create mode 100644 src/corelib/thread/qfuture.qdoc create mode 100644 src/corelib/thread/qfutureinterface.cpp create mode 100644 src/corelib/thread/qfutureinterface.h create mode 100644 src/corelib/thread/qfutureinterface_p.h delete mode 100644 tests/auto/concurrent/qfuture/.gitignore delete mode 100644 tests/auto/concurrent/qfuture/qfuture.pro delete mode 100644 tests/auto/concurrent/qfuture/tst_qfuture.cpp create mode 100644 tests/auto/corelib/thread/qfuture/.gitignore create mode 100644 tests/auto/corelib/thread/qfuture/qfuture.pro create mode 100644 tests/auto/corelib/thread/qfuture/tst_qfuture.cpp diff --git a/src/concurrent/concurrent.pro b/src/concurrent/concurrent.pro index d21dff91dd..8fdd6d8cd2 100644 --- a/src/concurrent/concurrent.pro +++ b/src/concurrent/concurrent.pro @@ -12,8 +12,6 @@ load(qt_module) PRECOMPILED_HEADER = ../corelib/global/qt_pch.h SOURCES += \ - qfuture.cpp \ - qfutureinterface.cpp \ qfuturesynchronizer.cpp \ qfuturewatcher.cpp \ qtconcurrentfilter.cpp \ @@ -23,8 +21,6 @@ SOURCES += \ HEADERS += \ qtconcurrent_global.h \ - qfuture.h \ - qfutureinterface.h \ qfuturesynchronizer.h \ qfuturewatcher.h \ qtconcurrentcompilertest.h \ @@ -44,7 +40,6 @@ HEADERS += \ # private headers HEADERS += \ - qfutureinterface_p.h \ qfuturewatcher_p.h QMAKE_DOCS = $$PWD/doc/qtconcurrent.qdocconf diff --git a/src/concurrent/doc/snippets/code/src_concurrent_qfuture.cpp b/src/concurrent/doc/snippets/code/src_concurrent_qfuture.cpp deleted file mode 100644 index 16c9d0b162..0000000000 --- a/src/concurrent/doc/snippets/code/src_concurrent_qfuture.cpp +++ /dev/null @@ -1,64 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the documentation of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:BSD$ -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names -** of its contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -//! [0] -QFuture future = ...; - -QFuture::const_iterator i; -for (i = future.constBegin(); i != future.constEnd(); ++i) - cout << *i << endl; -//! [0] - - -//! [1] -QFuture future; -... -QFutureIterator i(future); -while (i.hasNext()) - qDebug() << i.next(); -//! [1] - - -//! [2] -QFutureIterator i(future); -i.toBack(); -while (i.hasPrevious()) - qDebug() << i.previous(); -//! [2] diff --git a/src/concurrent/qfuture.cpp b/src/concurrent/qfuture.cpp deleted file mode 100644 index 0316366e6e..0000000000 --- a/src/concurrent/qfuture.cpp +++ /dev/null @@ -1,697 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtCore module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 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, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $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 code/src_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 code/src_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. - - \image javaiterators1.png - - Here's how to iterate over the elements in reverse order: - - \snippet code/src_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/concurrent/qfuture.h b/src/concurrent/qfuture.h deleted file mode 100644 index 3a9fd64bb8..0000000000 --- a/src/concurrent/qfuture.h +++ /dev/null @@ -1,276 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtCore module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 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, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QFUTURE_H -#define QFUTURE_H - -#include - -#ifndef QT_NO_QFUTURE - -#include -#include - -QT_BEGIN_HEADER -QT_BEGIN_NAMESPACE - - -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_QFUTURE - -#endif // QFUTURE_H diff --git a/src/concurrent/qfutureinterface.cpp b/src/concurrent/qfutureinterface.cpp deleted file mode 100644 index 2c33418ee9..0000000000 --- a/src/concurrent/qfutureinterface.cpp +++ /dev/null @@ -1,565 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtCore module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 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, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -// qfutureinterface.h included from qfuture.h -#include "qfuture.h" - -#ifndef QT_NO_QFUTURE - -#include "qfutureinterface_p.h" - -#include -#include -#include -#include - -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 QException &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; -} - -QtPrivate::ExceptionStore &QFutureInterfaceBase::exceptionStore() -{ - return d->m_exceptionStore; -} - -QtPrivate::ResultStoreBase &QFutureInterfaceBase::resultStoreBase() -{ - return d->m_results; -} - -const QtPrivate::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.load() == 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)); - } - - QtPrivate::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/concurrent/qfutureinterface.h b/src/concurrent/qfutureinterface.h deleted file mode 100644 index e9d8d37b67..0000000000 --- a/src/concurrent/qfutureinterface.h +++ /dev/null @@ -1,313 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtCore module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 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, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QFUTUREINTERFACE_H -#define QFUTUREINTERFACE_H - -#include - -#include - -#ifndef QT_NO_QFUTURE - -#include -#include -#include - -QT_BEGIN_HEADER -QT_BEGIN_NAMESPACE - - -template class QFuture; -class QFutureInterfaceBasePrivate; -class QFutureWatcherBase; -class QFutureWatcherBasePrivate; - -class Q_CONCURRENT_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 QException &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; - QtPrivate::ExceptionStore &exceptionStore(); - QtPrivate::ResultStoreBase &resultStoreBase(); - const QtPrivate::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: - QtPrivate::ResultStore &resultStore() - { return static_cast &>(resultStoreBase()); } - const QtPrivate::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; - } - - QtPrivate::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; - } - - QtPrivate::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()); - - QtPrivate::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(const void * = 0) { QFutureInterfaceBase::reportFinished(); } -}; - -QT_END_NAMESPACE -QT_END_HEADER - -#endif // QT_NO_CONCURRENT - -#endif // QFUTUREINTERFACE_H diff --git a/src/concurrent/qfutureinterface_p.h b/src/concurrent/qfutureinterface_p.h deleted file mode 100644 index 7fd2ea4d99..0000000000 --- a/src/concurrent/qfutureinterface_p.h +++ /dev/null @@ -1,169 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtCore module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 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, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $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 -#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) - { } - explicit 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; - QtPrivate::ResultStoreBase m_results; - bool manualProgress; - int m_expectedResultCount; - QtPrivate::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/concurrent/qfuturesynchronizer.h b/src/concurrent/qfuturesynchronizer.h index 9e003c0fa9..b82e968d8f 100644 --- a/src/concurrent/qfuturesynchronizer.h +++ b/src/concurrent/qfuturesynchronizer.h @@ -44,7 +44,7 @@ #include -#include +#include #ifndef QT_NO_CONCURRENT diff --git a/src/concurrent/qfuturewatcher.h b/src/concurrent/qfuturewatcher.h index 4cee31b5ef..a08f418d01 100644 --- a/src/concurrent/qfuturewatcher.h +++ b/src/concurrent/qfuturewatcher.h @@ -44,7 +44,7 @@ #include -#include +#include #ifndef QT_NO_QFUTURE diff --git a/src/concurrent/qfuturewatcher_p.h b/src/concurrent/qfuturewatcher_p.h index 2092084b86..23d9472cd7 100644 --- a/src/concurrent/qfuturewatcher_p.h +++ b/src/concurrent/qfuturewatcher_p.h @@ -55,7 +55,7 @@ #include -#include "qfutureinterface_p.h" +#include #include #ifndef QT_NO_QFUTURE diff --git a/src/concurrent/qtconcurrent_global.h b/src/concurrent/qtconcurrent_global.h index 54e70b67f8..464aa88d49 100644 --- a/src/concurrent/qtconcurrent_global.h +++ b/src/concurrent/qtconcurrent_global.h @@ -47,10 +47,6 @@ QT_BEGIN_HEADER QT_BEGIN_NAMESPACE -#ifdef QT_NO_CONCURRENT -# define QT_NO_QFUTURE -#endif - #ifndef QT_STATIC # if defined(QT_BUILD_CONCURRENT_LIB) # define Q_CONCURRENT_EXPORT Q_DECL_EXPORT diff --git a/src/concurrent/qtconcurrentrunbase.h b/src/concurrent/qtconcurrentrunbase.h index 897345f239..6c67338d25 100644 --- a/src/concurrent/qtconcurrentrunbase.h +++ b/src/concurrent/qtconcurrentrunbase.h @@ -46,7 +46,7 @@ #ifndef QT_NO_CONCURRENT -#include +#include #include #include diff --git a/src/concurrent/qtconcurrentthreadengine.h b/src/concurrent/qtconcurrentthreadengine.h index 2a5187502f..ee3f7e6bd6 100644 --- a/src/concurrent/qtconcurrentthreadengine.h +++ b/src/concurrent/qtconcurrentthreadengine.h @@ -47,7 +47,7 @@ #ifndef QT_NO_CONCURRENT #include -#include +#include #include #include #include diff --git a/src/corelib/doc/snippets/code/src_corelib_thread_qfuture.cpp b/src/corelib/doc/snippets/code/src_corelib_thread_qfuture.cpp new file mode 100644 index 0000000000..16c9d0b162 --- /dev/null +++ b/src/corelib/doc/snippets/code/src_corelib_thread_qfuture.cpp @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//! [0] +QFuture future = ...; + +QFuture::const_iterator i; +for (i = future.constBegin(); i != future.constEnd(); ++i) + cout << *i << endl; +//! [0] + + +//! [1] +QFuture future; +... +QFutureIterator i(future); +while (i.hasNext()) + qDebug() << i.next(); +//! [1] + + +//! [2] +QFutureIterator i(future); +i.toBack(); +while (i.hasPrevious()) + qDebug() << i.previous(); +//! [2] diff --git a/src/corelib/thread/qfuture.h b/src/corelib/thread/qfuture.h new file mode 100644 index 0000000000..8b16f308f2 --- /dev/null +++ b/src/corelib/thread/qfuture.h @@ -0,0 +1,276 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QFUTURE_H +#define QFUTURE_H + +#include + +#ifndef QT_NO_QFUTURE + +#include +#include + +QT_BEGIN_HEADER +QT_BEGIN_NAMESPACE + + +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_QFUTURE + +#endif // QFUTURE_H diff --git a/src/corelib/thread/qfuture.qdoc b/src/corelib/thread/qfuture.qdoc new file mode 100644 index 0000000000..91414a092a --- /dev/null +++ b/src/corelib/thread/qfuture.qdoc @@ -0,0 +1,697 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $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 code/src_corelib_thread_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 code/src_corelib_thread_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. + + \image javaiterators1.png + + Here's how to iterate over the elements in reverse order: + + \snippet code/src_corelib_thread_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/thread/qfutureinterface.cpp b/src/corelib/thread/qfutureinterface.cpp new file mode 100644 index 0000000000..ce9eb143d7 --- /dev/null +++ b/src/corelib/thread/qfutureinterface.cpp @@ -0,0 +1,565 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// qfutureinterface.h included from qfuture.h +#include "qfuture.h" + +#ifndef QT_NO_QFUTURE + +#include "qfutureinterface_p.h" + +#include +#include +#include +#include + +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 QException &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; +} + +QtPrivate::ExceptionStore &QFutureInterfaceBase::exceptionStore() +{ + return d->m_exceptionStore; +} + +QtPrivate::ResultStoreBase &QFutureInterfaceBase::resultStoreBase() +{ + return d->m_results; +} + +const QtPrivate::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.load() == 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)); + } + + QtPrivate::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_QFUTURE diff --git a/src/corelib/thread/qfutureinterface.h b/src/corelib/thread/qfutureinterface.h new file mode 100644 index 0000000000..b7bf779412 --- /dev/null +++ b/src/corelib/thread/qfutureinterface.h @@ -0,0 +1,311 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QFUTUREINTERFACE_H +#define QFUTUREINTERFACE_H + +#include + +#ifndef QT_NO_QFUTURE + +#include +#include +#include + +QT_BEGIN_HEADER +QT_BEGIN_NAMESPACE + + +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 QException &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; + QtPrivate::ExceptionStore &exceptionStore(); + QtPrivate::ResultStoreBase &resultStoreBase(); + const QtPrivate::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: + QtPrivate::ResultStore &resultStore() + { return static_cast &>(resultStoreBase()); } + const QtPrivate::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; + } + + QtPrivate::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; + } + + QtPrivate::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()); + + QtPrivate::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(const void * = 0) { QFutureInterfaceBase::reportFinished(); } +}; + +QT_END_NAMESPACE +QT_END_HEADER + +#endif // QT_NO_QFUTURE + +#endif // QFUTUREINTERFACE_H diff --git a/src/corelib/thread/qfutureinterface_p.h b/src/corelib/thread/qfutureinterface_p.h new file mode 100644 index 0000000000..4498cdfc4b --- /dev/null +++ b/src/corelib/thread/qfutureinterface_p.h @@ -0,0 +1,167 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $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) + { } + explicit 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 Q_CORE_EXPORT QFutureInterfaceBasePrivate // ### temporary +{ +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; + QtPrivate::ResultStoreBase m_results; + bool manualProgress; + int m_expectedResultCount; + QtPrivate::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/thread/thread.pri b/src/corelib/thread/thread.pri index d9468cc3a4..1accffcdfe 100644 --- a/src/corelib/thread/thread.pri +++ b/src/corelib/thread/thread.pri @@ -12,6 +12,8 @@ HEADERS += thread/qmutex.h \ thread/qatomic.h \ thread/qexception.h \ thread/qresultstore.h \ + thread/qfuture.h \ + thread/qfutureinterface.h \ thread/qbasicatomic.h \ thread/qgenericatomic.h \ thread/qoldbasicatomic.h @@ -19,6 +21,7 @@ HEADERS += thread/qmutex.h \ # private headers HEADERS += thread/qmutex_p.h \ thread/qmutexpool_p.h \ + thread/qfutureinterface_p.h \ thread/qorderedmutexlocker_p.h \ thread/qreadwritelock_p.h \ thread/qthread_p.h \ @@ -27,6 +30,7 @@ HEADERS += thread/qmutex_p.h \ SOURCES += thread/qatomic.cpp \ thread/qexception.cpp \ thread/qresultstore.cpp \ + thread/qfutureinterface.cpp \ thread/qmutex.cpp \ thread/qreadwritelock.cpp \ thread/qrunnable.cpp \ diff --git a/tests/auto/concurrent/concurrent.pro b/tests/auto/concurrent/concurrent.pro index c87ae9e673..80d19d30ec 100644 --- a/tests/auto/concurrent/concurrent.pro +++ b/tests/auto/concurrent/concurrent.pro @@ -1,6 +1,5 @@ TEMPLATE=subdirs SUBDIRS=\ - qfuture \ qfuturesynchronizer \ qfuturewatcher \ qtconcurrentfilter \ diff --git a/tests/auto/concurrent/qfuture/.gitignore b/tests/auto/concurrent/qfuture/.gitignore deleted file mode 100644 index 77c055c874..0000000000 --- a/tests/auto/concurrent/qfuture/.gitignore +++ /dev/null @@ -1 +0,0 @@ -tst_qfuture diff --git a/tests/auto/concurrent/qfuture/qfuture.pro b/tests/auto/concurrent/qfuture/qfuture.pro deleted file mode 100644 index 794bcfd6d7..0000000000 --- a/tests/auto/concurrent/qfuture/qfuture.pro +++ /dev/null @@ -1,6 +0,0 @@ -CONFIG += testcase parallel_test -TARGET = tst_qfuture -QT = core concurrent-private testlib concurrent -SOURCES = tst_qfuture.cpp -DEFINES += QT_STRICT_ITERATORS -DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 diff --git a/tests/auto/concurrent/qfuture/tst_qfuture.cpp b/tests/auto/concurrent/qfuture/tst_qfuture.cpp deleted file mode 100644 index ebf545f128..0000000000 --- a/tests/auto/concurrent/qfuture/tst_qfuture.cpp +++ /dev/null @@ -1,1436 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 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, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#include -#include - -#define QFUTURE_TEST - -#include -#include -#include -#include -#include -#include - -// COM interface macro. -#if defined(Q_OS_WIN) && defined(interface) -# undef interface -#endif - -class tst_QFuture: public QObject -{ - Q_OBJECT -private slots: - void resultStore(); - void future(); - void futureInterface(); - void refcounting(); - void cancel(); - void statePropagation(); - void multipleResults(); - void indexedResults(); - void progress(); - void progressText(); - void resultsAfterFinished(); - void resultsAsList(); - void implicitConversions(); - void iterators(); - void pause(); - void throttling(); - void voidConversions(); -#ifndef QT_NO_EXCEPTIONS - void exceptions(); - void nestedExceptions(); -#endif -}; - -void tst_QFuture::resultStore() -{ - int int0 = 0; - int int1 = 1; - int int2 = 2; - - { - QtPrivate::ResultStore store; - QVERIFY(store.begin() == store.end()); - QVERIFY(store.resultAt(0) == store.end()); - QVERIFY(store.resultAt(1) == store.end()); - } - - - { - QtPrivate::ResultStoreBase store; - store.addResult(-1, &int0); // note to self: adding a pointer to the stack here is ok since - store.addResult(1, &int1); // ResultStoreBase does not take ownership, only ResultStore<> does. - QtPrivate::ResultIteratorBase it = store.begin(); - QCOMPARE(it.resultIndex(), 0); - QVERIFY(it == store.begin()); - QVERIFY(it != store.end()); - - ++it; - QCOMPARE(it.resultIndex(), 1); - QVERIFY(it != store.begin()); - QVERIFY(it != store.end()); - - ++it; - QVERIFY(it != store.begin()); - QVERIFY(it == store.end()); - } - - QVector vec0 = QVector() << 2 << 3; - QVector vec1 = QVector() << 4 << 5; - - { - QtPrivate::ResultStoreBase store; - store.addResults(-1, &vec0, 2, 2); - store.addResults(-1, &vec1, 2, 2); - QtPrivate::ResultIteratorBase it = store.begin(); - QCOMPARE(it.resultIndex(), 0); - QVERIFY(it == store.begin()); - QVERIFY(it != store.end()); - - ++it; - QCOMPARE(it.resultIndex(), 1); - QVERIFY(it != store.begin()); - QVERIFY(it != store.end()); - - ++it; - QCOMPARE(it.resultIndex(), 2); - - ++it; - QCOMPARE(it.resultIndex(), 3); - - ++it; - QVERIFY(it == store.end()); - } - { - QtPrivate::ResultStoreBase store; - store.addResult(-1, &int0); - store.addResults(-1, &vec1, 2, 2); - store.addResult(-1, &int1); - - QtPrivate::ResultIteratorBase it = store.begin(); - QCOMPARE(it.resultIndex(), 0); - QVERIFY(it == store.begin()); - QVERIFY(it != store.end()); - - ++it; - QCOMPARE(it.resultIndex(), 1); - QVERIFY(it != store.begin()); - QVERIFY(it != store.end()); - - ++it; - QCOMPARE(it.resultIndex(), 2); - QVERIFY(it != store.end()); - ++it; - QCOMPARE(it.resultIndex(), 3); - QVERIFY(it != store.end()); - ++it; - QVERIFY(it == store.end()); - - QCOMPARE(store.resultAt(0).resultIndex(), 0); - QCOMPARE(store.resultAt(1).resultIndex(), 1); - QCOMPARE(store.resultAt(2).resultIndex(), 2); - QCOMPARE(store.resultAt(3).resultIndex(), 3); - QCOMPARE(store.resultAt(4), store.end()); - } - { - QtPrivate::ResultStore store; - store.addResult(-1, &int0); - store.addResults(-1, &vec0); - store.addResult(-1, &int1); - - QtPrivate::ResultIteratorBase it = store.begin(); - QCOMPARE(it.resultIndex(), 0); - QVERIFY(it == store.begin()); - QVERIFY(it != store.end()); - - ++it; - QCOMPARE(it.resultIndex(), 1); - QVERIFY(it != store.begin()); - QVERIFY(it != store.end()); - - ++it; - QCOMPARE(it.resultIndex(), 2); - QVERIFY(it != store.end()); - ++it; - QCOMPARE(it.resultIndex(), 3); - QVERIFY(it != store.end()); - ++it; - QVERIFY(it == store.end()); - - QCOMPARE(store.resultAt(0).value(), int0); - QCOMPARE(store.resultAt(1).value(), vec0[0]); - QCOMPARE(store.resultAt(2).value(), vec0[1]); - QCOMPARE(store.resultAt(3).value(), int1); - } - { - QtPrivate::ResultStore store; - store.addResult(-1, &int0); - store.addResults(-1, &vec0); - store.addResult(200, &int1); - - QCOMPARE(store.resultAt(0).value(), int0); - QCOMPARE(store.resultAt(1).value(), vec0[0]); - QCOMPARE(store.resultAt(2).value(), vec0[1]); - QCOMPARE(store.resultAt(200).value(), int1); - } - - { - QtPrivate::ResultStore store; - store.addResult(1, &int1); - store.addResult(0, &int0); - store.addResult(-1, &int2); - - QCOMPARE(store.resultAt(0).value(), int0); - QCOMPARE(store.resultAt(1).value(), int1); - QCOMPARE(store.resultAt(2).value(), int2); - } - - { - QtPrivate::ResultStore store; - QCOMPARE(store.contains(0), false); - QCOMPARE(store.contains(1), false); - QCOMPARE(store.contains(INT_MAX), false); - } - - { - // Test filter mode, where "gaps" in the result array aren't allowed. - QtPrivate::ResultStore store; - store.setFilterMode(true); - - store.addResult(0, &int0); - QCOMPARE(store.contains(0), true); - - store.addResult(2, &int2); // add result at index 2 - QCOMPARE(store.contains(2), false); // but 1 is missing, so this 2 won't be reported yet. - - store.addResult(1, &int1); - QCOMPARE(store.contains(1), true); - QCOMPARE(store.contains(2), true); // 2 should be visible now. - - store.addResult(4, &int0); - store.addResult(5, &int0); - store.addResult(7, &int0); - QCOMPARE(store.contains(4), false); - QCOMPARE(store.contains(5), false); - QCOMPARE(store.contains(7), false); - - store.addResult(3, &int0); // adding 3 makes 4 and 5 visible - QCOMPARE(store.contains(4), true); - QCOMPARE(store.contains(5), true); - QCOMPARE(store.contains(7), false); - - store.addResult(6, &int0); // adding 6 makes 7 visible - - QCOMPARE(store.contains(6), true); - QCOMPARE(store.contains(7), true); - QCOMPARE(store.contains(8), false); - } - - { - // test canceled results - QtPrivate::ResultStore store; - store.setFilterMode(true); - - store.addResult(0, &int0); - QCOMPARE(store.contains(0), true); - - store.addResult(2, &int0); - QCOMPARE(store.contains(2), false); - - store.addCanceledResult(1); // report no result at 1 - - QCOMPARE(store.contains(0), true); - QCOMPARE(store.contains(1), true); // 2 gets renamed to 1 - QCOMPARE(store.contains(2), false); - - store.addResult(3, &int0); - QCOMPARE(store.contains(2), true); //3 gets renamed to 2 - - store.addResult(6, &int0); - store.addResult(7, &int0); - QCOMPARE(store.contains(3), false); - - store.addCanceledResult(4); - store.addCanceledResult(5); - - QCOMPARE(store.contains(3), true); //6 gets renamed to 3 - QCOMPARE(store.contains(4), true); //7 gets renamed to 4 - - store.addResult(8, &int0); - QCOMPARE(store.contains(5), true); //8 gets renamed to 4 - - QCOMPARE(store.contains(6), false); - QCOMPARE(store.contains(7), false); - } - - { - // test addResult return value - QtPrivate::ResultStore store; - store.setFilterMode(true); - - store.addResult(0, &int0); - QCOMPARE(store.count(), 1); // result 0 becomes available - QCOMPARE(store.contains(0), true); - - store.addResult(2, &int0); - QCOMPARE(store.count(), 1); - QCOMPARE(store.contains(2), false); - - store.addCanceledResult(1); - QCOMPARE(store.count(), 2); // result 2 is renamed to 1 and becomes available - - QCOMPARE(store.contains(0), true); - QCOMPARE(store.contains(1), true); - QCOMPARE(store.contains(2), false); - - store.addResult(3, &int0); - QCOMPARE(store.count(), 3); - QCOMPARE(store.contains(2), true); - - store.addResult(6, &int0); - QCOMPARE(store.count(), 3); - store.addResult(7, &int0); - QCOMPARE(store.count(), 3); - QCOMPARE(store.contains(3), false); - - store.addCanceledResult(4); - store.addCanceledResult(5); - QCOMPARE(store.count(), 5); // 6 and 7 is renamed to 3 and 4 and becomes available - - QCOMPARE(store.contains(3), true); - QCOMPARE(store.contains(4), true); - - store.addResult(8, &int0); - QCOMPARE(store.contains(5), true); - QCOMPARE(store.count(), 6); - - QCOMPARE(store.contains(6), false); - QCOMPARE(store.contains(7), false); - } - - { - // test resultCount in non-filtered mode. It should always be possible - // to iterate through the results 0 to resultCount. - QtPrivate::ResultStore store; - store.addResult(0, &int0); - - QCOMPARE(store.count(), 1); - - store.addResult(2, &int0); - - QCOMPARE(store.count(), 1); - - store.addResult(1, &int0); - QCOMPARE(store.count(), 3); - } - - { - QtPrivate::ResultStore store; - store.addResult(2, &int0); - QCOMPARE(store.count(), 0); - - store.addResult(1, &int0); - QCOMPARE(store.count(), 0); - - store.addResult(0, &int0); - QCOMPARE(store.count(), 3); - } - - { - QtPrivate::ResultStore store; - store.addResults(2, &vec1); - QCOMPARE(store.count(), 0); - - store.addResult(1, &int0); - QCOMPARE(store.count(), 0); - - store.addResult(0, &int0); - QCOMPARE(store.count(), 4); - } - - { - QtPrivate::ResultStore store; - store.addResults(2, &vec1); - QCOMPARE(store.count(), 0); - - store.addResults(0, &vec0); - QCOMPARE(store.count(), 4); - } - { - QtPrivate::ResultStore store; - store.addResults(3, &vec1); - QCOMPARE(store.count(), 0); - - store.addResults(0, &vec0); - QCOMPARE(store.count(), 2); - - store.addResult(2, &int0); - QCOMPARE(store.count(), 5); - } - - { - QtPrivate::ResultStore store; - store.setFilterMode(true); - store.addResults(3, &vec1); - QCOMPARE(store.count(), 0); - - store.addResults(0, &vec0); - QCOMPARE(store.count(), 2); - - store.addCanceledResult(2); - QCOMPARE(store.count(), 4); - } - - { - QtPrivate::ResultStore store; - store.setFilterMode(true); - store.addResults(3, &vec1); - QCOMPARE(store.count(), 0); - - store.addCanceledResults(0, 3); - QCOMPARE(store.count(), 2); - } - - { - QtPrivate::ResultStore store; - store.setFilterMode(true); - store.addResults(3, &vec1); - QCOMPARE(store.count(), 0); - - store.addCanceledResults(0, 3); - QCOMPARE(store.count(), 2); // results at 3 and 4 become available at index 0, 1 - - store.addResult(5, &int0); - QCOMPARE(store.count(), 3);// result 5 becomes available at index 2 - } - - { - QtPrivate::ResultStore store; - store.addResult(1, &int0); - store.addResult(3, &int0); - store.addResults(6, &vec0); - QCOMPARE(store.contains(0), false); - QCOMPARE(store.contains(1), true); - QCOMPARE(store.contains(2), false); - QCOMPARE(store.contains(3), true); - QCOMPARE(store.contains(4), false); - QCOMPARE(store.contains(5), false); - QCOMPARE(store.contains(6), true); - QCOMPARE(store.contains(7), true); - } - - { - QtPrivate::ResultStore store; - store.setFilterMode(true); - store.addResult(1, &int0); - store.addResult(3, &int0); - store.addResults(6, &vec0); - QCOMPARE(store.contains(0), false); - QCOMPARE(store.contains(1), false); - QCOMPARE(store.contains(2), false); - QCOMPARE(store.contains(3), false); - QCOMPARE(store.contains(4), false); - QCOMPARE(store.contains(5), false); - QCOMPARE(store.contains(6), false); - QCOMPARE(store.contains(7), false); - - store.addCanceledResult(0); - store.addCanceledResult(2); - store.addCanceledResults(4, 2); - - QCOMPARE(store.contains(0), true); - QCOMPARE(store.contains(1), true); - QCOMPARE(store.contains(2), true); - QCOMPARE(store.contains(3), true); - QCOMPARE(store.contains(4), false); - QCOMPARE(store.contains(5), false); - QCOMPARE(store.contains(6), false); - QCOMPARE(store.contains(7), false); - } - { - QtPrivate::ResultStore store; - store.setFilterMode(true); - store.addCanceledResult(0); - QCOMPARE(store.contains(0), false); - - store.addResult(1, &int0); - QCOMPARE(store.contains(0), true); - QCOMPARE(store.contains(1), false); - } -} - -void tst_QFuture::future() -{ - // default constructors - QFuture intFuture; - intFuture.waitForFinished(); - QFuture stringFuture; - stringFuture.waitForFinished(); - QFuture voidFuture; - voidFuture.waitForFinished(); - QFuture defaultVoidFuture; - defaultVoidFuture.waitForFinished(); - - // copy constructor - QFuture intFuture2(intFuture); - QFuture voidFuture2(defaultVoidFuture); - - // assigmnent operator - intFuture2 = QFuture(); - voidFuture2 = QFuture(); - - // state - QCOMPARE(intFuture2.isStarted(), true); - QCOMPARE(intFuture2.isFinished(), true); -} - -class IntResult : public QFutureInterface -{ -public: - QFuture run() - { - this->reportStarted(); - QFuture future = QFuture(this); - - int res = 10; - reportFinished(&res); - return future; - } -}; - -int value = 10; - -class VoidResult : public QFutureInterfaceBase -{ -public: - QFuture run() - { - this->reportStarted(); - QFuture future = QFuture(this); - reportFinished(); - return future; - } -}; - -void tst_QFuture::futureInterface() -{ - { - QFuture future; - { - QFutureInterface i; - i.reportStarted(); - future = i.future(); - i.reportFinished(); - } - } - { - QFuture future; - { - QFutureInterface i; - i.reportStarted(); - i.reportResult(10); - future = i.future(); - i.reportFinished(); - } - QCOMPARE(future.resultAt(0), 10); - } - - { - QFuture intFuture; - - QCOMPARE(intFuture.isStarted(), true); - QCOMPARE(intFuture.isFinished(), true); - - IntResult result; - - result.reportStarted(); - intFuture = result.future(); - - QCOMPARE(intFuture.isStarted(), true); - QCOMPARE(intFuture.isFinished(), false); - - result.reportFinished(&value); - - QCOMPARE(intFuture.isStarted(), true); - QCOMPARE(intFuture.isFinished(), true); - - int e = intFuture.result(); - - QCOMPARE(intFuture.isStarted(), true); - QCOMPARE(intFuture.isFinished(), true); - QCOMPARE(intFuture.isCanceled(), false); - - QCOMPARE(e, value); - intFuture.waitForFinished(); - - IntResult intAlgo; - intFuture = intAlgo.run(); - QFuture intFuture2(intFuture); - QCOMPARE(intFuture.result(), value); - QCOMPARE(intFuture2.result(), value); - intFuture.waitForFinished(); - - VoidResult a; - a.run().waitForFinished(); - } -} - -template -void testRefCounting() -{ - QFutureInterface interface; - QCOMPARE(interface.d->refCount.load(), 1); - - { - interface.reportStarted(); - - QFuture f = interface.future(); - QCOMPARE(interface.d->refCount.load(), 2); - - QFuture f2(f); - QCOMPARE(interface.d->refCount.load(), 3); - - QFuture f3; - f3 = f2; - QCOMPARE(interface.d->refCount.load(), 4); - - interface.reportFinished(0); - QCOMPARE(interface.d->refCount.load(), 4); - } - - QCOMPARE(interface.d->refCount.load(), 1); -} - -void tst_QFuture::refcounting() -{ - testRefCounting(); -} - -void tst_QFuture::cancel() -{ - { - QFuture f; - QFutureInterface result; - - result.reportStarted(); - f = result.future(); - QVERIFY(f.isCanceled() == false); - result.reportCanceled(); - QVERIFY(f.isCanceled()); - result.reportFinished(); - QVERIFY(f.isCanceled()); - f.waitForFinished(); - QVERIFY(f.isCanceled()); - } - - // Cancel from the QFuture side and test if the result - // interface detects it. - { - QFutureInterface result; - - QFuture f; - QVERIFY(f.isStarted() == true); - - result.reportStarted(); - f = result.future(); - - QVERIFY(f.isStarted() == true); - - QVERIFY(result.isCanceled() == false); - f.cancel(); - - QVERIFY(result.isCanceled()); - - result.reportFinished(); - } - - // Test that finished futures can be canceled. - { - QFutureInterface result; - - QFuture f; - QVERIFY(f.isStarted() == true); - - result.reportStarted(); - f = result.future(); - - QVERIFY(f.isStarted() == true); - - result.reportFinished(); - - f.cancel(); - - QVERIFY(result.isCanceled()); - QVERIFY(f.isCanceled()); - } - - // Results reported after canceled is called should not be propagated. - { - - QFutureInterface futureInterface; - futureInterface.reportStarted(); - QFuture f = futureInterface.future(); - - int result = 0; - futureInterface.reportResult(&result); - result = 1; - futureInterface.reportResult(&result); - f.cancel(); - result = 2; - futureInterface.reportResult(&result); - result = 3; - futureInterface.reportResult(&result); - futureInterface.reportFinished(); - QCOMPARE(f.results(), QList()); - } -} - -void tst_QFuture::statePropagation() -{ - QFuture f1; - QFuture f2; - - QCOMPARE(f1.isStarted(), true); - - QFutureInterface result; - result.reportStarted(); - f1 = result.future(); - - f2 = f1; - - QCOMPARE(f2.isStarted(), true); - - result.reportCanceled(); - - QCOMPARE(f2.isStarted(), true); - QCOMPARE(f2.isCanceled(), true); - - QFuture f3 = f2; - - QCOMPARE(f3.isStarted(), true); - QCOMPARE(f3.isCanceled(), true); - - result.reportFinished(); - - QCOMPARE(f2.isStarted(), true); - QCOMPARE(f2.isCanceled(), true); - - QCOMPARE(f3.isStarted(), true); - QCOMPARE(f3.isCanceled(), true); -} - -/* - Tests that a QFuture can return multiple results. -*/ -void tst_QFuture::multipleResults() -{ - IntResult a; - a.reportStarted(); - QFuture f = a.future(); - - QFuture copy = f; - int result; - - result = 1; - a.reportResult(&result); - QCOMPARE(f.resultAt(0), 1); - - result = 2; - a.reportResult(&result); - QCOMPARE(f.resultAt(1), 2); - - result = 3; - a.reportResult(&result); - - result = 4; - a.reportFinished(&result); - - QCOMPARE(f.results(), QList() << 1 << 2 << 3 << 4); - - // test foreach - QList fasit = QList() << 1 << 2 << 3 << 4; - { - QList results; - foreach(int result, f) - results.append(result); - QCOMPARE(results, fasit); - } - { - QList results; - foreach(int result, copy) - results.append(result); - QCOMPARE(results, fasit); - } -} - -/* - Test out-of-order result reporting using indexes -*/ -void tst_QFuture::indexedResults() -{ - { - QFutureInterface Interface; - QFuture f; - QVERIFY(f.isStarted() == true); - - Interface.reportStarted(); - f = Interface.future(); - - QVERIFY(f.isStarted() == true); - - QChar result; - - result = 'B'; - Interface.reportResult(&result, 1); - - QCOMPARE(f.resultAt(1), result); - - result = 'A'; - Interface.reportResult(&result, 0); - QCOMPARE(f.resultAt(0), result); - - result = 'C'; - Interface.reportResult(&result); // no index - QCOMPARE(f.resultAt(2), result); - - Interface.reportFinished(); - - QCOMPARE(f.results(), QList() << 'A' << 'B' << 'C'); - } - - { - // Test result reporting with a missing result in the middle - QFutureInterface Interface; - Interface.reportStarted(); - QFuture f = Interface.future(); - int result; - - result = 0; - Interface.reportResult(&result, 0); - QVERIFY(f.isResultReadyAt(0)); - QCOMPARE(f.resultAt(0), 0); - - result = 3; - Interface.reportResult(&result, 3); - QVERIFY(f.isResultReadyAt(3)); - QCOMPARE(f.resultAt(3), 3); - - result = 2; - Interface.reportResult(&result, 2); - QVERIFY(f.isResultReadyAt(2)); - QCOMPARE(f.resultAt(2), 2); - - result = 4; - Interface.reportResult(&result); // no index - QVERIFY(f.isResultReadyAt(4)); - QCOMPARE(f.resultAt(4), 4); - - Interface.reportFinished(); - - QCOMPARE(f.results(), QList() << 0 << 2 << 3 << 4); - } -} - -void tst_QFuture::progress() -{ - QFutureInterface result; - QFuture f; - - QCOMPARE (f.progressValue(), 0); - - result.reportStarted(); - f = result.future(); - - QCOMPARE (f.progressValue(), 0); - - result.setProgressValue(50); - - QCOMPARE (f.progressValue(), 50); - - result.reportFinished(); - - QCOMPARE (f.progressValue(), 50); -} - -void tst_QFuture::progressText() -{ - QFutureInterface i; - i.reportStarted(); - QFuture f = i.future(); - - QCOMPARE(f.progressText(), QLatin1String("")); - i.setProgressValueAndText(1, QLatin1String("foo")); - QCOMPARE(f.progressText(), QLatin1String("foo")); - i.reportFinished(); -} - -/* - Test that results reported after finished are ignored. -*/ -void tst_QFuture::resultsAfterFinished() -{ - { - IntResult a; - a.reportStarted(); - QFuture f = a.future(); - int result; - - QCOMPARE(f.resultCount(), 0); - - result = 1; - a.reportResult(&result); - QCOMPARE(f.resultAt(0), 1); - - a.reportFinished(); - - QCOMPARE(f.resultAt(0), 1); - QCOMPARE(f.resultCount(), 1); - result = 2; - a.reportResult(&result); - QCOMPARE(f.resultCount(), 1); - } - // cancel it - { - IntResult a; - a.reportStarted(); - QFuture f = a.future(); - int result; - - QCOMPARE(f.resultCount(), 0); - - result = 1; - a.reportResult(&result); - QCOMPARE(f.resultAt(0), 1); - QCOMPARE(f.resultCount(), 1); - - a.reportCanceled(); - - QCOMPARE(f.resultAt(0), 1); - QCOMPARE(f.resultCount(), 1); - - result = 2; - a.reportResult(&result); - a.reportFinished(); - } -} - -void tst_QFuture::resultsAsList() -{ - IntResult a; - a.reportStarted(); - QFuture f = a.future(); - - int result; - result = 1; - a.reportResult(&result); - result = 2; - a.reportResult(&result); - - a.reportFinished(); - - QList results = f.results(); - QCOMPARE(results, QList() << 1 << 2); -} - -/* - Test that QFuture can be implicitly converted to T -*/ -void tst_QFuture::implicitConversions() -{ - QFutureInterface iface; - iface.reportStarted(); - - QFuture f(&iface); - - const QString input("FooBar 2000"); - iface.reportFinished(&input); - - const QString result = f; - QCOMPARE(result, input); - QCOMPARE(QString(f), input); - QCOMPARE(static_cast(f), input); -} - -void tst_QFuture::iterators() -{ - { - QFutureInterface e; - e.reportStarted(); - QFuture f = e.future(); - - int result; - result = 1; - e.reportResult(&result); - result = 2; - e.reportResult(&result); - result = 3; - e.reportResult(&result); - e.reportFinished(); - - QList results; - QFutureIterator i(f); - while (i.hasNext()) { - results.append(i.next()); - } - - QCOMPARE(results, f.results()); - - QFuture::const_iterator i1 = f.begin(), i2 = i1 + 1; - QFuture::const_iterator c1 = i1, c2 = c1 + 1; - - QVERIFY(i1 == i1); - QVERIFY(i1 == c1); - QVERIFY(c1 == i1); - QVERIFY(c1 == c1); - QVERIFY(i2 == i2); - QVERIFY(i2 == c2); - QVERIFY(c2 == i2); - QVERIFY(c2 == c2); - - QVERIFY(i1 != i2); - QVERIFY(i1 != c2); - QVERIFY(c1 != i2); - QVERIFY(c1 != c2); - QVERIFY(i2 != i1); - QVERIFY(i2 != c1); - QVERIFY(c2 != i1); - QVERIFY(c2 != c1); - - int x1 = *i1; - Q_UNUSED(x1); - int x2 = *i2; - Q_UNUSED(x2); - int y1 = *c1; - Q_UNUSED(y1); - int y2 = *c2; - Q_UNUSED(y2); - } - - { - QFutureInterface e; - e.reportStarted(); - QFuture f = e.future(); - - e.reportResult(QString("one")); - e.reportResult(QString("two")); - e.reportResult(QString("three")); - e.reportFinished(); - - QList results; - QFutureIterator i(f); - while (i.hasNext()) { - results.append(i.next()); - } - - QCOMPARE(results, f.results()); - - QFuture::const_iterator i1 = f.begin(), i2 = i1 + 1; - QFuture::const_iterator c1 = i1, c2 = c1 + 1; - - QVERIFY(i1 == i1); - QVERIFY(i1 == c1); - QVERIFY(c1 == i1); - QVERIFY(c1 == c1); - QVERIFY(i2 == i2); - QVERIFY(i2 == c2); - QVERIFY(c2 == i2); - QVERIFY(c2 == c2); - - QVERIFY(i1 != i2); - QVERIFY(i1 != c2); - QVERIFY(c1 != i2); - QVERIFY(c1 != c2); - QVERIFY(i2 != i1); - QVERIFY(i2 != c1); - QVERIFY(c2 != i1); - QVERIFY(c2 != c1); - - QString x1 = *i1; - QString x2 = *i2; - QString y1 = *c1; - QString y2 = *c2; - - QCOMPARE(x1, y1); - QCOMPARE(x2, y2); - - int i1Size = i1->size(); - int i2Size = i2->size(); - int c1Size = c1->size(); - int c2Size = c2->size(); - - QCOMPARE(i1Size, c1Size); - QCOMPARE(i2Size, c2Size); - } - - { - const int resultCount = 20; - - QFutureInterface e; - e.reportStarted(); - QFuture f = e.future(); - - for (int i = 0; i < resultCount; ++i) { - e.reportResult(i); - } - - e.reportFinished(); - - { - QFutureIterator it(f); - QFutureIterator it2(it); - } - - { - QFutureIterator it(f); - - for (int i = 0; i < resultCount - 1; ++i) { - QVERIFY(it.hasNext()); - QCOMPARE(it.peekNext(), i); - QCOMPARE(it.next(), i); - } - - QVERIFY(it.hasNext()); - QCOMPARE(it.peekNext(), resultCount - 1); - QCOMPARE(it.next(), resultCount - 1); - QVERIFY(it.hasNext() == false); - } - - { - QFutureIterator it(f); - QVERIFY(it.hasNext()); - it.toBack(); - QVERIFY(it.hasNext() == false); - it.toFront(); - QVERIFY(it.hasNext()); - } - } -} - -class SignalSlotObject : public QObject -{ -Q_OBJECT -public: - SignalSlotObject() - : finishedCalled(false), - canceledCalled(false), - rangeBegin(0), - rangeEnd(0) { } - -public slots: - void finished() - { - finishedCalled = true; - } - - void canceled() - { - canceledCalled = true; - } - - void resultReady(int index) - { - results.insert(index); - } - - void progressRange(int begin, int end) - { - rangeBegin = begin; - rangeEnd = end; - } - - void progress(int progress) - { - reportedProgress.insert(progress); - } -public: - bool finishedCalled; - bool canceledCalled; - QSet results; - int rangeBegin; - int rangeEnd; - QSet reportedProgress; -}; - -void tst_QFuture::pause() -{ - QFutureInterface Interface; - - Interface.reportStarted(); - QFuture f = Interface.future(); - - QVERIFY(Interface.isPaused() == false); - f.pause(); - QVERIFY(Interface.isPaused() == true); - f.resume(); - QVERIFY(Interface.isPaused() == false); - f.togglePaused(); - QVERIFY(Interface.isPaused() == true); - f.togglePaused(); - QVERIFY(Interface.isPaused() == false); - - Interface.reportFinished(); -} - -const int resultCount = 1000; - -class ResultObject : public QObject -{ -Q_OBJECT -public slots: - void resultReady(int) - { - - } -public: -}; - -// Test that that the isPaused() on future result interface returns true -// if we report a lot of results that are not handled. -void tst_QFuture::throttling() -{ - { - QFutureInterface i; - - i.reportStarted(); - QFuture f = i.future(); - - QVERIFY(i.isThrottled() == false); - - i.setThrottled(true); - QVERIFY(i.isThrottled()); - - i.setThrottled(false); - QVERIFY(i.isThrottled() == false); - - i.setThrottled(true); - QVERIFY(i.isThrottled()); - - i.reportFinished(); - } -} - -void tst_QFuture::voidConversions() -{ - QFutureInterface iface; - iface.reportStarted(); - - QFuture intFuture(&iface); - - int value = 10; - iface.reportFinished(&value); - - QFuture voidFuture(intFuture); - voidFuture = intFuture; - - QVERIFY(voidFuture == intFuture); -} - - -#ifndef QT_NO_EXCEPTIONS - -QFuture createExceptionFuture() -{ - QFutureInterface i; - i.reportStarted(); - QFuture f = i.future(); - - QException e; - i.reportException(e); - i.reportFinished(); - return f; -} - -QFuture createExceptionResultFuture() -{ - QFutureInterface i; - i.reportStarted(); - QFuture f = i.future(); - int r = 0; - i.reportResult(r); - - QException e; - i.reportException(e); - i.reportFinished(); - return f; -} - -class DerivedException : public QException -{ -public: - void raise() const { throw *this; } - DerivedException *clone() const { return new DerivedException(*this); } -}; - -QFuture createDerivedExceptionFuture() -{ - QFutureInterface i; - i.reportStarted(); - QFuture f = i.future(); - - DerivedException e; - i.reportException(e); - i.reportFinished(); - return f; -} - -void tst_QFuture::exceptions() -{ - // test throwing from waitForFinished - { - QFuture f = createExceptionFuture(); - bool caught = false; - try { - f.waitForFinished(); - } catch (QException &) { - caught = true; - } - QVERIFY(caught); - } - - // test result() - { - QFuture f = createExceptionResultFuture(); - bool caught = false; - try { - f.result(); - } catch (QException &) { - caught = true; - } - QVERIFY(caught); - } - - // test result() and destroy - { - bool caught = false; - try { - createExceptionResultFuture().result(); - } catch (QException &) { - caught = true; - } - QVERIFY(caught); - } - - // test results() - { - QFuture f = createExceptionResultFuture(); - bool caught = false; - try { - f.results(); - } catch (QException &) { - caught = true; - } - QVERIFY(caught); - } - - // test foreach - { - QFuture f = createExceptionResultFuture(); - bool caught = false; - try { - foreach (int e, f.results()) { - Q_UNUSED(e); - QFAIL("did not get exception"); - } - } catch (QException &) { - caught = true; - } - QVERIFY(caught); - } - - // catch derived exceptions - { - bool caught = false; - try { - createDerivedExceptionFuture().waitForFinished(); - } catch (QException &) { - caught = true; - } - QVERIFY(caught); - } - - { - bool caught = false; - try { - createDerivedExceptionFuture().waitForFinished(); - } catch (DerivedException &) { - caught = true; - } - QVERIFY(caught); - } -} - -class MyClass -{ -public: - ~MyClass() - { - QFuture f = createExceptionFuture(); - try { - f.waitForFinished(); - } catch (QException &) { - caught = true; - } - } - static bool caught; -}; - -bool MyClass::caught = false; - -// This is a regression test for QTBUG-18149. where QFuture did not throw -// exceptions if called from destructors when the stack was already unwinding -// due to an exception having been thrown. -void tst_QFuture::nestedExceptions() -{ - try { - MyClass m; - Q_UNUSED(m); - throw 0; - } catch (int) {} - - QVERIFY(MyClass::caught); -} - -#endif // QT_NO_EXCEPTIONS - -QTEST_MAIN(tst_QFuture) -#include "tst_qfuture.moc" diff --git a/tests/auto/concurrent/qfuturesynchronizer/tst_qfuturesynchronizer.cpp b/tests/auto/concurrent/qfuturesynchronizer/tst_qfuturesynchronizer.cpp index dbf957c85b..5d643a47f1 100644 --- a/tests/auto/concurrent/qfuturesynchronizer/tst_qfuturesynchronizer.cpp +++ b/tests/auto/concurrent/qfuturesynchronizer/tst_qfuturesynchronizer.cpp @@ -42,7 +42,7 @@ #include #include -#include +#include class tst_QFutureSynchronizer : public QObject { diff --git a/tests/auto/concurrent/qfuturewatcher/qfuturewatcher.pro b/tests/auto/concurrent/qfuturewatcher/qfuturewatcher.pro index fbe577670f..17c0363ce4 100644 --- a/tests/auto/concurrent/qfuturewatcher/qfuturewatcher.pro +++ b/tests/auto/concurrent/qfuturewatcher/qfuturewatcher.pro @@ -1,5 +1,5 @@ CONFIG += testcase parallel_test TARGET = tst_qfuturewatcher -QT = core concurrent-private testlib concurrent +QT = core core-private testlib concurrent SOURCES = tst_qfuturewatcher.cpp DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 diff --git a/tests/auto/corelib/thread/qfuture/.gitignore b/tests/auto/corelib/thread/qfuture/.gitignore new file mode 100644 index 0000000000..77c055c874 --- /dev/null +++ b/tests/auto/corelib/thread/qfuture/.gitignore @@ -0,0 +1 @@ +tst_qfuture diff --git a/tests/auto/corelib/thread/qfuture/qfuture.pro b/tests/auto/corelib/thread/qfuture/qfuture.pro new file mode 100644 index 0000000000..36853de2f0 --- /dev/null +++ b/tests/auto/corelib/thread/qfuture/qfuture.pro @@ -0,0 +1,6 @@ +CONFIG += testcase parallel_test +TARGET = tst_qfuture +QT = core core-private testlib concurrent +SOURCES = tst_qfuture.cpp +DEFINES += QT_STRICT_ITERATORS +DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 diff --git a/tests/auto/corelib/thread/qfuture/tst_qfuture.cpp b/tests/auto/corelib/thread/qfuture/tst_qfuture.cpp new file mode 100644 index 0000000000..81ba1b0fd9 --- /dev/null +++ b/tests/auto/corelib/thread/qfuture/tst_qfuture.cpp @@ -0,0 +1,1436 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include +#include + +#define QFUTURE_TEST + +#include +#include +#include +#include +#include +#include + +// COM interface macro. +#if defined(Q_OS_WIN) && defined(interface) +# undef interface +#endif + +class tst_QFuture: public QObject +{ + Q_OBJECT +private slots: + void resultStore(); + void future(); + void futureInterface(); + void refcounting(); + void cancel(); + void statePropagation(); + void multipleResults(); + void indexedResults(); + void progress(); + void progressText(); + void resultsAfterFinished(); + void resultsAsList(); + void implicitConversions(); + void iterators(); + void pause(); + void throttling(); + void voidConversions(); +#ifndef QT_NO_EXCEPTIONS + void exceptions(); + void nestedExceptions(); +#endif +}; + +void tst_QFuture::resultStore() +{ + int int0 = 0; + int int1 = 1; + int int2 = 2; + + { + QtPrivate::ResultStore store; + QVERIFY(store.begin() == store.end()); + QVERIFY(store.resultAt(0) == store.end()); + QVERIFY(store.resultAt(1) == store.end()); + } + + + { + QtPrivate::ResultStoreBase store; + store.addResult(-1, &int0); // note to self: adding a pointer to the stack here is ok since + store.addResult(1, &int1); // ResultStoreBase does not take ownership, only ResultStore<> does. + QtPrivate::ResultIteratorBase it = store.begin(); + QCOMPARE(it.resultIndex(), 0); + QVERIFY(it == store.begin()); + QVERIFY(it != store.end()); + + ++it; + QCOMPARE(it.resultIndex(), 1); + QVERIFY(it != store.begin()); + QVERIFY(it != store.end()); + + ++it; + QVERIFY(it != store.begin()); + QVERIFY(it == store.end()); + } + + QVector vec0 = QVector() << 2 << 3; + QVector vec1 = QVector() << 4 << 5; + + { + QtPrivate::ResultStoreBase store; + store.addResults(-1, &vec0, 2, 2); + store.addResults(-1, &vec1, 2, 2); + QtPrivate::ResultIteratorBase it = store.begin(); + QCOMPARE(it.resultIndex(), 0); + QVERIFY(it == store.begin()); + QVERIFY(it != store.end()); + + ++it; + QCOMPARE(it.resultIndex(), 1); + QVERIFY(it != store.begin()); + QVERIFY(it != store.end()); + + ++it; + QCOMPARE(it.resultIndex(), 2); + + ++it; + QCOMPARE(it.resultIndex(), 3); + + ++it; + QVERIFY(it == store.end()); + } + { + QtPrivate::ResultStoreBase store; + store.addResult(-1, &int0); + store.addResults(-1, &vec1, 2, 2); + store.addResult(-1, &int1); + + QtPrivate::ResultIteratorBase it = store.begin(); + QCOMPARE(it.resultIndex(), 0); + QVERIFY(it == store.begin()); + QVERIFY(it != store.end()); + + ++it; + QCOMPARE(it.resultIndex(), 1); + QVERIFY(it != store.begin()); + QVERIFY(it != store.end()); + + ++it; + QCOMPARE(it.resultIndex(), 2); + QVERIFY(it != store.end()); + ++it; + QCOMPARE(it.resultIndex(), 3); + QVERIFY(it != store.end()); + ++it; + QVERIFY(it == store.end()); + + QCOMPARE(store.resultAt(0).resultIndex(), 0); + QCOMPARE(store.resultAt(1).resultIndex(), 1); + QCOMPARE(store.resultAt(2).resultIndex(), 2); + QCOMPARE(store.resultAt(3).resultIndex(), 3); + QCOMPARE(store.resultAt(4), store.end()); + } + { + QtPrivate::ResultStore store; + store.addResult(-1, &int0); + store.addResults(-1, &vec0); + store.addResult(-1, &int1); + + QtPrivate::ResultIteratorBase it = store.begin(); + QCOMPARE(it.resultIndex(), 0); + QVERIFY(it == store.begin()); + QVERIFY(it != store.end()); + + ++it; + QCOMPARE(it.resultIndex(), 1); + QVERIFY(it != store.begin()); + QVERIFY(it != store.end()); + + ++it; + QCOMPARE(it.resultIndex(), 2); + QVERIFY(it != store.end()); + ++it; + QCOMPARE(it.resultIndex(), 3); + QVERIFY(it != store.end()); + ++it; + QVERIFY(it == store.end()); + + QCOMPARE(store.resultAt(0).value(), int0); + QCOMPARE(store.resultAt(1).value(), vec0[0]); + QCOMPARE(store.resultAt(2).value(), vec0[1]); + QCOMPARE(store.resultAt(3).value(), int1); + } + { + QtPrivate::ResultStore store; + store.addResult(-1, &int0); + store.addResults(-1, &vec0); + store.addResult(200, &int1); + + QCOMPARE(store.resultAt(0).value(), int0); + QCOMPARE(store.resultAt(1).value(), vec0[0]); + QCOMPARE(store.resultAt(2).value(), vec0[1]); + QCOMPARE(store.resultAt(200).value(), int1); + } + + { + QtPrivate::ResultStore store; + store.addResult(1, &int1); + store.addResult(0, &int0); + store.addResult(-1, &int2); + + QCOMPARE(store.resultAt(0).value(), int0); + QCOMPARE(store.resultAt(1).value(), int1); + QCOMPARE(store.resultAt(2).value(), int2); + } + + { + QtPrivate::ResultStore store; + QCOMPARE(store.contains(0), false); + QCOMPARE(store.contains(1), false); + QCOMPARE(store.contains(INT_MAX), false); + } + + { + // Test filter mode, where "gaps" in the result array aren't allowed. + QtPrivate::ResultStore store; + store.setFilterMode(true); + + store.addResult(0, &int0); + QCOMPARE(store.contains(0), true); + + store.addResult(2, &int2); // add result at index 2 + QCOMPARE(store.contains(2), false); // but 1 is missing, so this 2 won't be reported yet. + + store.addResult(1, &int1); + QCOMPARE(store.contains(1), true); + QCOMPARE(store.contains(2), true); // 2 should be visible now. + + store.addResult(4, &int0); + store.addResult(5, &int0); + store.addResult(7, &int0); + QCOMPARE(store.contains(4), false); + QCOMPARE(store.contains(5), false); + QCOMPARE(store.contains(7), false); + + store.addResult(3, &int0); // adding 3 makes 4 and 5 visible + QCOMPARE(store.contains(4), true); + QCOMPARE(store.contains(5), true); + QCOMPARE(store.contains(7), false); + + store.addResult(6, &int0); // adding 6 makes 7 visible + + QCOMPARE(store.contains(6), true); + QCOMPARE(store.contains(7), true); + QCOMPARE(store.contains(8), false); + } + + { + // test canceled results + QtPrivate::ResultStore store; + store.setFilterMode(true); + + store.addResult(0, &int0); + QCOMPARE(store.contains(0), true); + + store.addResult(2, &int0); + QCOMPARE(store.contains(2), false); + + store.addCanceledResult(1); // report no result at 1 + + QCOMPARE(store.contains(0), true); + QCOMPARE(store.contains(1), true); // 2 gets renamed to 1 + QCOMPARE(store.contains(2), false); + + store.addResult(3, &int0); + QCOMPARE(store.contains(2), true); //3 gets renamed to 2 + + store.addResult(6, &int0); + store.addResult(7, &int0); + QCOMPARE(store.contains(3), false); + + store.addCanceledResult(4); + store.addCanceledResult(5); + + QCOMPARE(store.contains(3), true); //6 gets renamed to 3 + QCOMPARE(store.contains(4), true); //7 gets renamed to 4 + + store.addResult(8, &int0); + QCOMPARE(store.contains(5), true); //8 gets renamed to 4 + + QCOMPARE(store.contains(6), false); + QCOMPARE(store.contains(7), false); + } + + { + // test addResult return value + QtPrivate::ResultStore store; + store.setFilterMode(true); + + store.addResult(0, &int0); + QCOMPARE(store.count(), 1); // result 0 becomes available + QCOMPARE(store.contains(0), true); + + store.addResult(2, &int0); + QCOMPARE(store.count(), 1); + QCOMPARE(store.contains(2), false); + + store.addCanceledResult(1); + QCOMPARE(store.count(), 2); // result 2 is renamed to 1 and becomes available + + QCOMPARE(store.contains(0), true); + QCOMPARE(store.contains(1), true); + QCOMPARE(store.contains(2), false); + + store.addResult(3, &int0); + QCOMPARE(store.count(), 3); + QCOMPARE(store.contains(2), true); + + store.addResult(6, &int0); + QCOMPARE(store.count(), 3); + store.addResult(7, &int0); + QCOMPARE(store.count(), 3); + QCOMPARE(store.contains(3), false); + + store.addCanceledResult(4); + store.addCanceledResult(5); + QCOMPARE(store.count(), 5); // 6 and 7 is renamed to 3 and 4 and becomes available + + QCOMPARE(store.contains(3), true); + QCOMPARE(store.contains(4), true); + + store.addResult(8, &int0); + QCOMPARE(store.contains(5), true); + QCOMPARE(store.count(), 6); + + QCOMPARE(store.contains(6), false); + QCOMPARE(store.contains(7), false); + } + + { + // test resultCount in non-filtered mode. It should always be possible + // to iterate through the results 0 to resultCount. + QtPrivate::ResultStore store; + store.addResult(0, &int0); + + QCOMPARE(store.count(), 1); + + store.addResult(2, &int0); + + QCOMPARE(store.count(), 1); + + store.addResult(1, &int0); + QCOMPARE(store.count(), 3); + } + + { + QtPrivate::ResultStore store; + store.addResult(2, &int0); + QCOMPARE(store.count(), 0); + + store.addResult(1, &int0); + QCOMPARE(store.count(), 0); + + store.addResult(0, &int0); + QCOMPARE(store.count(), 3); + } + + { + QtPrivate::ResultStore store; + store.addResults(2, &vec1); + QCOMPARE(store.count(), 0); + + store.addResult(1, &int0); + QCOMPARE(store.count(), 0); + + store.addResult(0, &int0); + QCOMPARE(store.count(), 4); + } + + { + QtPrivate::ResultStore store; + store.addResults(2, &vec1); + QCOMPARE(store.count(), 0); + + store.addResults(0, &vec0); + QCOMPARE(store.count(), 4); + } + { + QtPrivate::ResultStore store; + store.addResults(3, &vec1); + QCOMPARE(store.count(), 0); + + store.addResults(0, &vec0); + QCOMPARE(store.count(), 2); + + store.addResult(2, &int0); + QCOMPARE(store.count(), 5); + } + + { + QtPrivate::ResultStore store; + store.setFilterMode(true); + store.addResults(3, &vec1); + QCOMPARE(store.count(), 0); + + store.addResults(0, &vec0); + QCOMPARE(store.count(), 2); + + store.addCanceledResult(2); + QCOMPARE(store.count(), 4); + } + + { + QtPrivate::ResultStore store; + store.setFilterMode(true); + store.addResults(3, &vec1); + QCOMPARE(store.count(), 0); + + store.addCanceledResults(0, 3); + QCOMPARE(store.count(), 2); + } + + { + QtPrivate::ResultStore store; + store.setFilterMode(true); + store.addResults(3, &vec1); + QCOMPARE(store.count(), 0); + + store.addCanceledResults(0, 3); + QCOMPARE(store.count(), 2); // results at 3 and 4 become available at index 0, 1 + + store.addResult(5, &int0); + QCOMPARE(store.count(), 3);// result 5 becomes available at index 2 + } + + { + QtPrivate::ResultStore store; + store.addResult(1, &int0); + store.addResult(3, &int0); + store.addResults(6, &vec0); + QCOMPARE(store.contains(0), false); + QCOMPARE(store.contains(1), true); + QCOMPARE(store.contains(2), false); + QCOMPARE(store.contains(3), true); + QCOMPARE(store.contains(4), false); + QCOMPARE(store.contains(5), false); + QCOMPARE(store.contains(6), true); + QCOMPARE(store.contains(7), true); + } + + { + QtPrivate::ResultStore store; + store.setFilterMode(true); + store.addResult(1, &int0); + store.addResult(3, &int0); + store.addResults(6, &vec0); + QCOMPARE(store.contains(0), false); + QCOMPARE(store.contains(1), false); + QCOMPARE(store.contains(2), false); + QCOMPARE(store.contains(3), false); + QCOMPARE(store.contains(4), false); + QCOMPARE(store.contains(5), false); + QCOMPARE(store.contains(6), false); + QCOMPARE(store.contains(7), false); + + store.addCanceledResult(0); + store.addCanceledResult(2); + store.addCanceledResults(4, 2); + + QCOMPARE(store.contains(0), true); + QCOMPARE(store.contains(1), true); + QCOMPARE(store.contains(2), true); + QCOMPARE(store.contains(3), true); + QCOMPARE(store.contains(4), false); + QCOMPARE(store.contains(5), false); + QCOMPARE(store.contains(6), false); + QCOMPARE(store.contains(7), false); + } + { + QtPrivate::ResultStore store; + store.setFilterMode(true); + store.addCanceledResult(0); + QCOMPARE(store.contains(0), false); + + store.addResult(1, &int0); + QCOMPARE(store.contains(0), true); + QCOMPARE(store.contains(1), false); + } +} + +void tst_QFuture::future() +{ + // default constructors + QFuture intFuture; + intFuture.waitForFinished(); + QFuture stringFuture; + stringFuture.waitForFinished(); + QFuture voidFuture; + voidFuture.waitForFinished(); + QFuture defaultVoidFuture; + defaultVoidFuture.waitForFinished(); + + // copy constructor + QFuture intFuture2(intFuture); + QFuture voidFuture2(defaultVoidFuture); + + // assigmnent operator + intFuture2 = QFuture(); + voidFuture2 = QFuture(); + + // state + QCOMPARE(intFuture2.isStarted(), true); + QCOMPARE(intFuture2.isFinished(), true); +} + +class IntResult : public QFutureInterface +{ +public: + QFuture run() + { + this->reportStarted(); + QFuture future = QFuture(this); + + int res = 10; + reportFinished(&res); + return future; + } +}; + +int value = 10; + +class VoidResult : public QFutureInterfaceBase +{ +public: + QFuture run() + { + this->reportStarted(); + QFuture future = QFuture(this); + reportFinished(); + return future; + } +}; + +void tst_QFuture::futureInterface() +{ + { + QFuture future; + { + QFutureInterface i; + i.reportStarted(); + future = i.future(); + i.reportFinished(); + } + } + { + QFuture future; + { + QFutureInterface i; + i.reportStarted(); + i.reportResult(10); + future = i.future(); + i.reportFinished(); + } + QCOMPARE(future.resultAt(0), 10); + } + + { + QFuture intFuture; + + QCOMPARE(intFuture.isStarted(), true); + QCOMPARE(intFuture.isFinished(), true); + + IntResult result; + + result.reportStarted(); + intFuture = result.future(); + + QCOMPARE(intFuture.isStarted(), true); + QCOMPARE(intFuture.isFinished(), false); + + result.reportFinished(&value); + + QCOMPARE(intFuture.isStarted(), true); + QCOMPARE(intFuture.isFinished(), true); + + int e = intFuture.result(); + + QCOMPARE(intFuture.isStarted(), true); + QCOMPARE(intFuture.isFinished(), true); + QCOMPARE(intFuture.isCanceled(), false); + + QCOMPARE(e, value); + intFuture.waitForFinished(); + + IntResult intAlgo; + intFuture = intAlgo.run(); + QFuture intFuture2(intFuture); + QCOMPARE(intFuture.result(), value); + QCOMPARE(intFuture2.result(), value); + intFuture.waitForFinished(); + + VoidResult a; + a.run().waitForFinished(); + } +} + +template +void testRefCounting() +{ + QFutureInterface interface; + QCOMPARE(interface.d->refCount.load(), 1); + + { + interface.reportStarted(); + + QFuture f = interface.future(); + QCOMPARE(interface.d->refCount.load(), 2); + + QFuture f2(f); + QCOMPARE(interface.d->refCount.load(), 3); + + QFuture f3; + f3 = f2; + QCOMPARE(interface.d->refCount.load(), 4); + + interface.reportFinished(0); + QCOMPARE(interface.d->refCount.load(), 4); + } + + QCOMPARE(interface.d->refCount.load(), 1); +} + +void tst_QFuture::refcounting() +{ + testRefCounting(); +} + +void tst_QFuture::cancel() +{ + { + QFuture f; + QFutureInterface result; + + result.reportStarted(); + f = result.future(); + QVERIFY(f.isCanceled() == false); + result.reportCanceled(); + QVERIFY(f.isCanceled()); + result.reportFinished(); + QVERIFY(f.isCanceled()); + f.waitForFinished(); + QVERIFY(f.isCanceled()); + } + + // Cancel from the QFuture side and test if the result + // interface detects it. + { + QFutureInterface result; + + QFuture f; + QVERIFY(f.isStarted() == true); + + result.reportStarted(); + f = result.future(); + + QVERIFY(f.isStarted() == true); + + QVERIFY(result.isCanceled() == false); + f.cancel(); + + QVERIFY(result.isCanceled()); + + result.reportFinished(); + } + + // Test that finished futures can be canceled. + { + QFutureInterface result; + + QFuture f; + QVERIFY(f.isStarted() == true); + + result.reportStarted(); + f = result.future(); + + QVERIFY(f.isStarted() == true); + + result.reportFinished(); + + f.cancel(); + + QVERIFY(result.isCanceled()); + QVERIFY(f.isCanceled()); + } + + // Results reported after canceled is called should not be propagated. + { + + QFutureInterface futureInterface; + futureInterface.reportStarted(); + QFuture f = futureInterface.future(); + + int result = 0; + futureInterface.reportResult(&result); + result = 1; + futureInterface.reportResult(&result); + f.cancel(); + result = 2; + futureInterface.reportResult(&result); + result = 3; + futureInterface.reportResult(&result); + futureInterface.reportFinished(); + QCOMPARE(f.results(), QList()); + } +} + +void tst_QFuture::statePropagation() +{ + QFuture f1; + QFuture f2; + + QCOMPARE(f1.isStarted(), true); + + QFutureInterface result; + result.reportStarted(); + f1 = result.future(); + + f2 = f1; + + QCOMPARE(f2.isStarted(), true); + + result.reportCanceled(); + + QCOMPARE(f2.isStarted(), true); + QCOMPARE(f2.isCanceled(), true); + + QFuture f3 = f2; + + QCOMPARE(f3.isStarted(), true); + QCOMPARE(f3.isCanceled(), true); + + result.reportFinished(); + + QCOMPARE(f2.isStarted(), true); + QCOMPARE(f2.isCanceled(), true); + + QCOMPARE(f3.isStarted(), true); + QCOMPARE(f3.isCanceled(), true); +} + +/* + Tests that a QFuture can return multiple results. +*/ +void tst_QFuture::multipleResults() +{ + IntResult a; + a.reportStarted(); + QFuture f = a.future(); + + QFuture copy = f; + int result; + + result = 1; + a.reportResult(&result); + QCOMPARE(f.resultAt(0), 1); + + result = 2; + a.reportResult(&result); + QCOMPARE(f.resultAt(1), 2); + + result = 3; + a.reportResult(&result); + + result = 4; + a.reportFinished(&result); + + QCOMPARE(f.results(), QList() << 1 << 2 << 3 << 4); + + // test foreach + QList fasit = QList() << 1 << 2 << 3 << 4; + { + QList results; + foreach(int result, f) + results.append(result); + QCOMPARE(results, fasit); + } + { + QList results; + foreach(int result, copy) + results.append(result); + QCOMPARE(results, fasit); + } +} + +/* + Test out-of-order result reporting using indexes +*/ +void tst_QFuture::indexedResults() +{ + { + QFutureInterface Interface; + QFuture f; + QVERIFY(f.isStarted() == true); + + Interface.reportStarted(); + f = Interface.future(); + + QVERIFY(f.isStarted() == true); + + QChar result; + + result = 'B'; + Interface.reportResult(&result, 1); + + QCOMPARE(f.resultAt(1), result); + + result = 'A'; + Interface.reportResult(&result, 0); + QCOMPARE(f.resultAt(0), result); + + result = 'C'; + Interface.reportResult(&result); // no index + QCOMPARE(f.resultAt(2), result); + + Interface.reportFinished(); + + QCOMPARE(f.results(), QList() << 'A' << 'B' << 'C'); + } + + { + // Test result reporting with a missing result in the middle + QFutureInterface Interface; + Interface.reportStarted(); + QFuture f = Interface.future(); + int result; + + result = 0; + Interface.reportResult(&result, 0); + QVERIFY(f.isResultReadyAt(0)); + QCOMPARE(f.resultAt(0), 0); + + result = 3; + Interface.reportResult(&result, 3); + QVERIFY(f.isResultReadyAt(3)); + QCOMPARE(f.resultAt(3), 3); + + result = 2; + Interface.reportResult(&result, 2); + QVERIFY(f.isResultReadyAt(2)); + QCOMPARE(f.resultAt(2), 2); + + result = 4; + Interface.reportResult(&result); // no index + QVERIFY(f.isResultReadyAt(4)); + QCOMPARE(f.resultAt(4), 4); + + Interface.reportFinished(); + + QCOMPARE(f.results(), QList() << 0 << 2 << 3 << 4); + } +} + +void tst_QFuture::progress() +{ + QFutureInterface result; + QFuture f; + + QCOMPARE (f.progressValue(), 0); + + result.reportStarted(); + f = result.future(); + + QCOMPARE (f.progressValue(), 0); + + result.setProgressValue(50); + + QCOMPARE (f.progressValue(), 50); + + result.reportFinished(); + + QCOMPARE (f.progressValue(), 50); +} + +void tst_QFuture::progressText() +{ + QFutureInterface i; + i.reportStarted(); + QFuture f = i.future(); + + QCOMPARE(f.progressText(), QLatin1String("")); + i.setProgressValueAndText(1, QLatin1String("foo")); + QCOMPARE(f.progressText(), QLatin1String("foo")); + i.reportFinished(); +} + +/* + Test that results reported after finished are ignored. +*/ +void tst_QFuture::resultsAfterFinished() +{ + { + IntResult a; + a.reportStarted(); + QFuture f = a.future(); + int result; + + QCOMPARE(f.resultCount(), 0); + + result = 1; + a.reportResult(&result); + QCOMPARE(f.resultAt(0), 1); + + a.reportFinished(); + + QCOMPARE(f.resultAt(0), 1); + QCOMPARE(f.resultCount(), 1); + result = 2; + a.reportResult(&result); + QCOMPARE(f.resultCount(), 1); + } + // cancel it + { + IntResult a; + a.reportStarted(); + QFuture f = a.future(); + int result; + + QCOMPARE(f.resultCount(), 0); + + result = 1; + a.reportResult(&result); + QCOMPARE(f.resultAt(0), 1); + QCOMPARE(f.resultCount(), 1); + + a.reportCanceled(); + + QCOMPARE(f.resultAt(0), 1); + QCOMPARE(f.resultCount(), 1); + + result = 2; + a.reportResult(&result); + a.reportFinished(); + } +} + +void tst_QFuture::resultsAsList() +{ + IntResult a; + a.reportStarted(); + QFuture f = a.future(); + + int result; + result = 1; + a.reportResult(&result); + result = 2; + a.reportResult(&result); + + a.reportFinished(); + + QList results = f.results(); + QCOMPARE(results, QList() << 1 << 2); +} + +/* + Test that QFuture can be implicitly converted to T +*/ +void tst_QFuture::implicitConversions() +{ + QFutureInterface iface; + iface.reportStarted(); + + QFuture f(&iface); + + const QString input("FooBar 2000"); + iface.reportFinished(&input); + + const QString result = f; + QCOMPARE(result, input); + QCOMPARE(QString(f), input); + QCOMPARE(static_cast(f), input); +} + +void tst_QFuture::iterators() +{ + { + QFutureInterface e; + e.reportStarted(); + QFuture f = e.future(); + + int result; + result = 1; + e.reportResult(&result); + result = 2; + e.reportResult(&result); + result = 3; + e.reportResult(&result); + e.reportFinished(); + + QList results; + QFutureIterator i(f); + while (i.hasNext()) { + results.append(i.next()); + } + + QCOMPARE(results, f.results()); + + QFuture::const_iterator i1 = f.begin(), i2 = i1 + 1; + QFuture::const_iterator c1 = i1, c2 = c1 + 1; + + QVERIFY(i1 == i1); + QVERIFY(i1 == c1); + QVERIFY(c1 == i1); + QVERIFY(c1 == c1); + QVERIFY(i2 == i2); + QVERIFY(i2 == c2); + QVERIFY(c2 == i2); + QVERIFY(c2 == c2); + + QVERIFY(i1 != i2); + QVERIFY(i1 != c2); + QVERIFY(c1 != i2); + QVERIFY(c1 != c2); + QVERIFY(i2 != i1); + QVERIFY(i2 != c1); + QVERIFY(c2 != i1); + QVERIFY(c2 != c1); + + int x1 = *i1; + Q_UNUSED(x1); + int x2 = *i2; + Q_UNUSED(x2); + int y1 = *c1; + Q_UNUSED(y1); + int y2 = *c2; + Q_UNUSED(y2); + } + + { + QFutureInterface e; + e.reportStarted(); + QFuture f = e.future(); + + e.reportResult(QString("one")); + e.reportResult(QString("two")); + e.reportResult(QString("three")); + e.reportFinished(); + + QList results; + QFutureIterator i(f); + while (i.hasNext()) { + results.append(i.next()); + } + + QCOMPARE(results, f.results()); + + QFuture::const_iterator i1 = f.begin(), i2 = i1 + 1; + QFuture::const_iterator c1 = i1, c2 = c1 + 1; + + QVERIFY(i1 == i1); + QVERIFY(i1 == c1); + QVERIFY(c1 == i1); + QVERIFY(c1 == c1); + QVERIFY(i2 == i2); + QVERIFY(i2 == c2); + QVERIFY(c2 == i2); + QVERIFY(c2 == c2); + + QVERIFY(i1 != i2); + QVERIFY(i1 != c2); + QVERIFY(c1 != i2); + QVERIFY(c1 != c2); + QVERIFY(i2 != i1); + QVERIFY(i2 != c1); + QVERIFY(c2 != i1); + QVERIFY(c2 != c1); + + QString x1 = *i1; + QString x2 = *i2; + QString y1 = *c1; + QString y2 = *c2; + + QCOMPARE(x1, y1); + QCOMPARE(x2, y2); + + int i1Size = i1->size(); + int i2Size = i2->size(); + int c1Size = c1->size(); + int c2Size = c2->size(); + + QCOMPARE(i1Size, c1Size); + QCOMPARE(i2Size, c2Size); + } + + { + const int resultCount = 20; + + QFutureInterface e; + e.reportStarted(); + QFuture f = e.future(); + + for (int i = 0; i < resultCount; ++i) { + e.reportResult(i); + } + + e.reportFinished(); + + { + QFutureIterator it(f); + QFutureIterator it2(it); + } + + { + QFutureIterator it(f); + + for (int i = 0; i < resultCount - 1; ++i) { + QVERIFY(it.hasNext()); + QCOMPARE(it.peekNext(), i); + QCOMPARE(it.next(), i); + } + + QVERIFY(it.hasNext()); + QCOMPARE(it.peekNext(), resultCount - 1); + QCOMPARE(it.next(), resultCount - 1); + QVERIFY(it.hasNext() == false); + } + + { + QFutureIterator it(f); + QVERIFY(it.hasNext()); + it.toBack(); + QVERIFY(it.hasNext() == false); + it.toFront(); + QVERIFY(it.hasNext()); + } + } +} + +class SignalSlotObject : public QObject +{ +Q_OBJECT +public: + SignalSlotObject() + : finishedCalled(false), + canceledCalled(false), + rangeBegin(0), + rangeEnd(0) { } + +public slots: + void finished() + { + finishedCalled = true; + } + + void canceled() + { + canceledCalled = true; + } + + void resultReady(int index) + { + results.insert(index); + } + + void progressRange(int begin, int end) + { + rangeBegin = begin; + rangeEnd = end; + } + + void progress(int progress) + { + reportedProgress.insert(progress); + } +public: + bool finishedCalled; + bool canceledCalled; + QSet results; + int rangeBegin; + int rangeEnd; + QSet reportedProgress; +}; + +void tst_QFuture::pause() +{ + QFutureInterface Interface; + + Interface.reportStarted(); + QFuture f = Interface.future(); + + QVERIFY(Interface.isPaused() == false); + f.pause(); + QVERIFY(Interface.isPaused() == true); + f.resume(); + QVERIFY(Interface.isPaused() == false); + f.togglePaused(); + QVERIFY(Interface.isPaused() == true); + f.togglePaused(); + QVERIFY(Interface.isPaused() == false); + + Interface.reportFinished(); +} + +const int resultCount = 1000; + +class ResultObject : public QObject +{ +Q_OBJECT +public slots: + void resultReady(int) + { + + } +public: +}; + +// Test that that the isPaused() on future result interface returns true +// if we report a lot of results that are not handled. +void tst_QFuture::throttling() +{ + { + QFutureInterface i; + + i.reportStarted(); + QFuture f = i.future(); + + QVERIFY(i.isThrottled() == false); + + i.setThrottled(true); + QVERIFY(i.isThrottled()); + + i.setThrottled(false); + QVERIFY(i.isThrottled() == false); + + i.setThrottled(true); + QVERIFY(i.isThrottled()); + + i.reportFinished(); + } +} + +void tst_QFuture::voidConversions() +{ + QFutureInterface iface; + iface.reportStarted(); + + QFuture intFuture(&iface); + + int value = 10; + iface.reportFinished(&value); + + QFuture voidFuture(intFuture); + voidFuture = intFuture; + + QVERIFY(voidFuture == intFuture); +} + + +#ifndef QT_NO_EXCEPTIONS + +QFuture createExceptionFuture() +{ + QFutureInterface i; + i.reportStarted(); + QFuture f = i.future(); + + QException e; + i.reportException(e); + i.reportFinished(); + return f; +} + +QFuture createExceptionResultFuture() +{ + QFutureInterface i; + i.reportStarted(); + QFuture f = i.future(); + int r = 0; + i.reportResult(r); + + QException e; + i.reportException(e); + i.reportFinished(); + return f; +} + +class DerivedException : public QException +{ +public: + void raise() const Q_DECL_OVERRIDE { throw *this; } + DerivedException *clone() const Q_DECL_OVERRIDE { return new DerivedException(*this); } +}; + +QFuture createDerivedExceptionFuture() +{ + QFutureInterface i; + i.reportStarted(); + QFuture f = i.future(); + + DerivedException e; + i.reportException(e); + i.reportFinished(); + return f; +} + +void tst_QFuture::exceptions() +{ + // test throwing from waitForFinished + { + QFuture f = createExceptionFuture(); + bool caught = false; + try { + f.waitForFinished(); + } catch (QException &) { + caught = true; + } + QVERIFY(caught); + } + + // test result() + { + QFuture f = createExceptionResultFuture(); + bool caught = false; + try { + f.result(); + } catch (QException &) { + caught = true; + } + QVERIFY(caught); + } + + // test result() and destroy + { + bool caught = false; + try { + createExceptionResultFuture().result(); + } catch (QException &) { + caught = true; + } + QVERIFY(caught); + } + + // test results() + { + QFuture f = createExceptionResultFuture(); + bool caught = false; + try { + f.results(); + } catch (QException &) { + caught = true; + } + QVERIFY(caught); + } + + // test foreach + { + QFuture f = createExceptionResultFuture(); + bool caught = false; + try { + foreach (int e, f.results()) { + Q_UNUSED(e); + QFAIL("did not get exception"); + } + } catch (QException &) { + caught = true; + } + QVERIFY(caught); + } + + // catch derived exceptions + { + bool caught = false; + try { + createDerivedExceptionFuture().waitForFinished(); + } catch (QException &) { + caught = true; + } + QVERIFY(caught); + } + + { + bool caught = false; + try { + createDerivedExceptionFuture().waitForFinished(); + } catch (DerivedException &) { + caught = true; + } + QVERIFY(caught); + } +} + +class MyClass +{ +public: + ~MyClass() + { + QFuture f = createExceptionFuture(); + try { + f.waitForFinished(); + } catch (QException &) { + caught = true; + } + } + static bool caught; +}; + +bool MyClass::caught = false; + +// This is a regression test for QTBUG-18149. where QFuture did not throw +// exceptions if called from destructors when the stack was already unwinding +// due to an exception having been thrown. +void tst_QFuture::nestedExceptions() +{ + try { + MyClass m; + Q_UNUSED(m); + throw 0; + } catch (int) {} + + QVERIFY(MyClass::caught); +} + +#endif // QT_NO_EXCEPTIONS + +QTEST_MAIN(tst_QFuture) +#include "tst_qfuture.moc" diff --git a/tests/auto/corelib/thread/thread.pro b/tests/auto/corelib/thread/thread.pro index 8f063ee746..0d3f442542 100644 --- a/tests/auto/corelib/thread/thread.pro +++ b/tests/auto/corelib/thread/thread.pro @@ -3,6 +3,7 @@ SUBDIRS=\ qatomicint \ qatomicpointer \ qresultstore \ + qfuture \ qmutex \ qmutexlocker \ qreadlocker \ -- cgit v1.2.3