diff options
Diffstat (limited to 'src/corelib/thread/qfuture.qdoc')
-rw-r--r-- | src/corelib/thread/qfuture.qdoc | 322 |
1 files changed, 306 insertions, 16 deletions
diff --git a/src/corelib/thread/qfuture.qdoc b/src/corelib/thread/qfuture.qdoc index 076725e19c..8b354fe114 100644 --- a/src/corelib/thread/qfuture.qdoc +++ b/src/corelib/thread/qfuture.qdoc @@ -37,14 +37,17 @@ 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(). + that has default, copy and possibly move constructors. If + a result is not available at the time of calling the result(), resultAt(), + results(), takeResult(), or takeResults() 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(). takeResult() and takeResults() + invalidate a future and any subsequent attempt to access result or results + from the future leads to undefined behavior. isValid() tells you if + results can be accessed. QFuture provides a \l{Java-style iterators}{Java-style iterator} (QFutureIterator) and an \l{STL-style iterators}{STL-style iterator} @@ -227,7 +230,7 @@ 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() + \sa result(), resultAt(), results(), takeResult(), takeResults() */ /*! \fn template <typename T> int QFuture<T>::progressValue() const @@ -273,7 +276,10 @@ 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() + \note Calling result() leads to undefined behavior if isValid() + returns \c false for this QFuture. + + \sa resultAt(), results(), takeResult(), takeResults() */ /*! \fn template <typename T> T QFuture<T>::resultAt(int index) const @@ -282,7 +288,10 @@ immediately available, this function will block and wait for the result to become available. - \sa result(), results(), resultCount() + \note Calling resultAt() leads to undefined behavior if isValid() + returns \c false for this QFuture. + + \sa result(), results(), takeResult(), takeResults(), resultCount() */ /*! \fn template <typename T> bool QFuture<T>::isResultReadyAt(int index) const @@ -290,7 +299,10 @@ Returns \c true if the result at \a index is immediately available; otherwise returns \c false. - \sa resultAt(), resultCount() + \note Calling isResultReadyAt() leads to undefined behavior if isValid() + returns \c false for this QFuture. + + \sa resultAt(), resultCount(), takeResult(), takeResults() */ /*! \fn template <typename T> QFuture<T>::operator T() const @@ -300,15 +312,69 @@ available. This is a convenience method for calling result() or resultAt(0). - \sa result(), resultAt(), results() + \note Calling this function leads to undefined behavior if isValid() + returns \c false for this QFuture. + + \sa result(), resultAt(), results(), takeResult(), takeResults(), isValid() */ /*! \fn template <typename T> QList<T> QFuture<T>::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. + Returns all results from the future. If the results are not immediately available, + this function will block and wait for them to become available. + + \note Calling results() leads to undefined behavior if isValid() + returns \c false for this QFuture. + + \sa result(), resultAt(), takeResult(), takeResults(), resultCount(), isValid() +*/ + +/*! \fn template <typename T> std::vector<T> QFuture<T>::takeResults() + + If isValid() returns \c false, calling this function leads to undefined behavior. + takeResults() takes all results from the QFuture object and invalidates it + (isValid() will return \c false for this future). If the results are + not immediately available, this function will block and wait for them to + become available. This function tries to use move semantics for the results + if available and falls back to copy construction if the type is not movable. - \sa result(), resultAt(), resultCount() + \note QFuture in general allows sharing the results between different QFuture + objects (and potentially between different threads). takeResults() was introduced + to make QFuture also work with move-only types (like std::unique_ptr), so it + assumes that only one thread can move the results out of the future, and only + once. + + \sa takeResult(), result(), resultAt(), results(), resultCount(), isValid() +*/ + +/* \fn template <typename T> std::vector<T> QFuture<T>::takeResult() + + Call this function only if isValid() returns \c true, otherwise + the behavior is undefined. This function takes the first result from + the QFuture object, for convenience when only one result is expected. + If there are any other results, they are discarded after taking the + first one (if such behavior is undesired, use takeResults() instead). + If the result is not immediately available, this function will block and + wait for the result to become available. The QFuture will try to use move + semantics if possible, and will fall back to copy construction if the type + is not movable. After the result was taken, isValid() will evaluate + as \c false. + + \note QFuture in general allows sharing the results between different QFuture + objects (and potentially between different threads). takeResult() was introduced + to make QFuture also work with move-only types (like std::unique_ptr), so it + assumes that only one thread can move the results out of the future, and + do it only once. + + \sa takeResults(), result(), results(), resultAt(), isValid() +*/ + +/* \fn template <typename T> std::vector<T> QFuture<T>::isValid() const + + Returns true if a result or results can be accessed or taken from this + QFuture object. Returns false after the result was taken from the future. + + \sa takeResults(), takeResult(), result(), results(), resultAt() */ /*! \fn template <typename T> QFuture<T>::const_iterator QFuture<T>::begin() const @@ -682,3 +748,227 @@ \sa findNext() */ + +/*! + \namespace QtFuture + + \inmodule QtCore + \brief Contains miscellaneous identifiers used by the QFuture class. +*/ + + +/*! + \enum QtFuture::Launch + + \since 6.0 + + Represents execution policies for running a QFuture continuation. + + \value Sync The continuation will be launched in the same thread in + which the parent has been executing. + + \value Async The continuation will be launched in in a separate thread taken from + the global QThreadPool. + + \value Inherit The continuation will inherit the launch policy of the parent or its + thread pool, if it was using a custom one. + + \sa QFuture::then(), QThreadPool::globalInstance() + +*/ + +/*! \fn template<class T> template<class Function> QFuture<typename QFuture<T>::ResultType<Function>> QFuture<T>::then(Function &&function) + + \since 6.0 + \overload + + Attaches a continuation to this future, allowing to chain multiple asynchronous + computations if desired. When the asynchronous computation represented by this + future finishes, \a function will be invoked in the same thread in which this + future has been running. A new QFuture representing the result of the continuation + is returned. + + \note Use other overloads of this method if you need to launch the continuation in + a separate thread. + + If this future has a result (is not a QFuture<void>), \a function takes the result + of this future as its argument. + + You can chain multiple operations like this: + + \code + QFuture<int> future = ...; + future.then([](int res1){ ... }).then([](int res2){ ... })... + \endcode + + Or: + \code + QFuture<void> future = ...; + future.then([](){ ... }).then([](){ ... })... + \endcode + + The continuation can also take a QFuture argument (instead of its value), representing + the previous future. This can be useful if, for example, QFuture has multiple results, + and the user wants to access them inside the continuation. Or the user needs to handle + the exception of the previous future inside the continuation, to not interrupt the chain + of multiple continuations. For example: + + \code + QFuture<int> future = ...; + future.then([](QFuture<int> f) { + try { + ... + auto result = f.result(); + ... + } catch (QException &e) { + // handle the exception + } + }).then(...); + \endcode + + If the previous future throws an exception and it is not handled inside the + continuation, the exception will be propagated to the continuation future, to + allow the caller to handle it: + + \code + QFuture<int> parentFuture = ...; + auto continuation = parentFuture.then([](int res1){ ... }).then([](int res2){ ... })... + ... + // parentFuture throws an exception + try { + auto result = continuation.result(); + } catch (QException &e) { + // handle the exception + } + \endcode + + In this case the whole chain of continuations will be interrupted. + + \note If the parent future gets canceled, its continuations will + also be canceled. + + \sa onFailed() +*/ + +/*! \fn template<class T> template<class Function> QFuture<typename QFuture<T>::ResultType<Function>> QFuture<T>::then(QtFuture::Launch policy, Function &&function) + + \since 6.0 + \overload + + Attaches a continuation to this future, allowing to chain multiple asynchronous + computations. When the asynchronous computation represented by this future + finishes, \a function will be invoked according to the given launch \a policy. + A new QFuture representing the result of the continuation is returned. + + Depending on the \a policy, continuation will run in the same thread as the parent, + run in a new thread, or inherit the launch policy and thread pool of the parent. + + In the following example both continuations will run in a new thread (but in + the same one). + + \code + QFuture<int> future = ...; + future.then(QtFuture::Launch::Async, [](int res){ ... }).then([](int res2){ ... }); + \endcode + + In the following example both continuations will run in new threads using the same + thread pool. + + \code + QFuture<int> future = ...; + future.then(QtFuture::Launch::Async, [](int res){ ... }) + .then(QtFuture::Launch::Inherit, [](int res2){ ... }); + \endcode + + \sa onFailed() +*/ + +/*! \fn template<class T> template<class Function> QFuture<typename QFuture<T>::ResultType<Function>> QFuture<T>::then(QThreadPool *pool, Function &&function) + + \since 6.0 + \overload + + Attaches a continuation to this future, allowing to chain multiple asynchronous + computations if desired. When the asynchronous computation represented by this + future finishes, \a function will be invoked in a separate thread taken from the + QThreadPool \a pool. + + \sa onFailed() +*/ + +/*! \fn template<class T> template<class Function> QFuture<T> QFuture<T>::onFailed(Function &&handler) + + \since 6.0 + + Attaches a failure handler to this future, to handle any exceptions that may + have been generated. Returns a QFuture of the parent type. The handler will + be invoked only in case of an exception, in the same thread as the parent + future has been running. \a handler is a callable which takes either no argument + or one argument, to filter by specific error types similar to + \l {https://en.cppreference.com/w/cpp/language/try_catch} {catch} statement. + + For example: + + \code + QFuture<int> future = ...; + auto resultFuture = future.then([](int res) { + ... + throw Error(); + ... + }).onFailed([](const Error &e) { + // Handle exceptions of type Error + ... + return -1; + }).onFailed([] { + // Handle all other types of errors + ... + return -1; + }); + + auto result = resultFuture.result(); // result is -1 + + \endcode + + If there are multiple handlers attached, the first handler that matches with the + thrown exception type will be invoked. For example: + + \code + QFuture<int> future = ...; + future.then([](int res) { + ... + throw std::runtime_error("message"); + ... + }).onFailed([](const std::exception &e) { + // This handler will be invoked + }).onFailed([](const std::runtime_error &e) { + // This handler won't be invoked, because of the handler above. + }); + \endcode + + If none of the handlers matches with the thrown exception type, the exception + will be propagated to the resulted future: + + \code + QFuture<int> future = ...; + auto resultFuture = future.then([](int res) { + ... + throw Error("message"); + ... + }).onFailed([](const std::exception &e) { + // Won't be invoked + }).onFailed([](const QException &e) { + // Won't be invoked + }); + + try { + auto result = resultFuture.result(); + } catch(...) { + // Handle the exception + } + \endcode + + \note You can always attach a handler taking no argument, to handle all exception + types and avoid writing the try-catch block. + + \sa then() +*/ |