Add support of cancellation handler callbacks to QFuture
Added QFuture::onCanceled() method, for attaching handlers to be called when the QFuture gets canceled. Change-Id: I1f01647d6173ba0c1db6641e14140108b33ac7c4 Reviewed-by: Leena Miettinen <riitta-leena.miettinen@qt.io> Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
This commit is contained in:
parent
612f6999c8
commit
1a5cc8d13d
@ -220,3 +220,35 @@ QtFuture::connect(&object, &Object::singleArgSignal).then([](int value) {
|
|||||||
// handle other exceptions
|
// handle other exceptions
|
||||||
});
|
});
|
||||||
//! [14]
|
//! [14]
|
||||||
|
|
||||||
|
//! [15]
|
||||||
|
QFuture<int> testFuture = ...;
|
||||||
|
auto resultFuture = testFuture.then([](int res) {
|
||||||
|
// Block 1
|
||||||
|
}).onCanceled([] {
|
||||||
|
// Block 2
|
||||||
|
}).onFailed([] {
|
||||||
|
// Block 3
|
||||||
|
}).then([] {
|
||||||
|
// Block 4
|
||||||
|
}).onFailed([] {
|
||||||
|
// Block 5
|
||||||
|
}).onCanceled([] {
|
||||||
|
// Block 6
|
||||||
|
});
|
||||||
|
//! [15]
|
||||||
|
|
||||||
|
//! [16]
|
||||||
|
QFuture<int> testFuture = ...;
|
||||||
|
auto resultFuture = testFuture.then([](int res) {
|
||||||
|
// Block 1
|
||||||
|
}).onFailed([] {
|
||||||
|
// Block 3
|
||||||
|
}).then([] {
|
||||||
|
// Block 4
|
||||||
|
}).onFailed([] {
|
||||||
|
// Block 5
|
||||||
|
}).onCanceled([] {
|
||||||
|
// Block 6
|
||||||
|
});
|
||||||
|
//! [16]
|
||||||
|
@ -168,6 +168,9 @@ public:
|
|||||||
QFuture<T> onFailed(Function &&handler);
|
QFuture<T> onFailed(Function &&handler);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
template<class Function, typename = std::enable_if_t<std::is_invocable_r_v<T, Function>>>
|
||||||
|
QFuture<T> onCanceled(Function &&handler);
|
||||||
|
|
||||||
class const_iterator
|
class const_iterator
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -281,6 +284,9 @@ private:
|
|||||||
template<class Function, class ResultType, class ParentResultType>
|
template<class Function, class ResultType, class ParentResultType>
|
||||||
friend class QtPrivate::Continuation;
|
friend class QtPrivate::Continuation;
|
||||||
|
|
||||||
|
template<class Function, class ResultType>
|
||||||
|
friend class QtPrivate::CanceledHandler;
|
||||||
|
|
||||||
#ifndef QT_NO_EXCEPTIONS
|
#ifndef QT_NO_EXCEPTIONS
|
||||||
template<class Function, class ResultType>
|
template<class Function, class ResultType>
|
||||||
friend class QtPrivate::FailureHandler;
|
friend class QtPrivate::FailureHandler;
|
||||||
@ -359,6 +365,15 @@ QFuture<T> QFuture<T>::onFailed(Function &&handler)
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
template<class Function, typename>
|
||||||
|
QFuture<T> QFuture<T>::onCanceled(Function &&handler)
|
||||||
|
{
|
||||||
|
QFutureInterface<T> promise(QFutureInterfaceBase::State::Pending);
|
||||||
|
QtPrivate::CanceledHandler<Function, T>::create(std::forward<Function>(handler), this, promise);
|
||||||
|
return promise.future();
|
||||||
|
}
|
||||||
|
|
||||||
inline QFuture<void> QFutureInterface<void>::future()
|
inline QFuture<void> QFutureInterface<void>::future()
|
||||||
{
|
{
|
||||||
return QFuture<void>(this);
|
return QFuture<void>(this);
|
||||||
|
@ -55,8 +55,9 @@
|
|||||||
results in the future.
|
results in the future.
|
||||||
|
|
||||||
If the result of one asynchronous computation needs to be passed
|
If the result of one asynchronous computation needs to be passed
|
||||||
to another, QFuture provides a convenient way of chaining multiple
|
to another, QFuture provides a convenient way of chaining multiple sequential
|
||||||
sequential computations using then(). Additionally, onFailed() can be used
|
computations using then(). onCanceled() can be used for adding a handler to
|
||||||
|
be called if the QFuture is canceled. Additionally, onFailed() can be used
|
||||||
to handle any failures that occurred in the chain. Note that QFuture relies
|
to handle any failures that occurred in the chain. Note that QFuture relies
|
||||||
on exceptions for the error handling. If using exceptions is not an option,
|
on exceptions for the error handling. If using exceptions is not an option,
|
||||||
you can still indicate the error state of QFuture, by making the error type
|
you can still indicate the error state of QFuture, by making the error type
|
||||||
@ -76,6 +77,31 @@
|
|||||||
|
|
||||||
\snippet code/src_corelib_thread_qfuture.cpp 4
|
\snippet code/src_corelib_thread_qfuture.cpp 4
|
||||||
|
|
||||||
|
It's possible to chain multiple continuations and handlers in any order.
|
||||||
|
The first handler that can handle the state of its parent is invoked first.
|
||||||
|
If there's no proper handler, the state is propagated to the next continuation
|
||||||
|
or handler. For example:
|
||||||
|
|
||||||
|
\snippet code/src_corelib_thread_qfuture.cpp 15
|
||||||
|
|
||||||
|
If \c testFuture is successfully fulfilled \c {Block 1} will be called. If
|
||||||
|
it succeeds as well, the next then() (\c {Block 4}) is called. If \c testFuture
|
||||||
|
gets canceled or fails with an exception, either \c {Block 2} or \c {Block 3}
|
||||||
|
will be called respectively. The next then() will be called afterwards, and the
|
||||||
|
story repeats.
|
||||||
|
|
||||||
|
\note If \c {Block 2} is invoked and throws an exception, the following
|
||||||
|
onFailed() (\c {Block 3}) will handle it. If the order of onFailed() and
|
||||||
|
onCanceled() were reversed, the exception state would propagate to the
|
||||||
|
next continuations and eventually would be caught in \c {Block 5}.
|
||||||
|
|
||||||
|
In the next example the first onCanceled() (\c {Block 2}) is removed:
|
||||||
|
|
||||||
|
\snippet code/src_corelib_thread_qfuture.cpp 16
|
||||||
|
|
||||||
|
If \c testFuture gets canceled, its state is propagated to the next then(),
|
||||||
|
which will be also canceled. So in this case \c {Block 6} will be called.
|
||||||
|
|
||||||
QFuture also offers ways to interact with a runnning computation. For
|
QFuture also offers ways to interact with a runnning computation. For
|
||||||
instance, the computation can be canceled with the cancel() function. To
|
instance, the computation can be canceled with the cancel() function. To
|
||||||
pause the computation, use the setPaused() function or one of the pause(),
|
pause the computation, use the setPaused() function or one of the pause(),
|
||||||
@ -893,7 +919,7 @@
|
|||||||
\note If the parent future gets canceled, its continuations will
|
\note If the parent future gets canceled, its continuations will
|
||||||
also be canceled.
|
also be canceled.
|
||||||
|
|
||||||
\sa onFailed()
|
\sa onFailed(), onCanceled()
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*! \fn template<class T> template<class Function> QFuture<typename QFuture<T>::ResultType<Function>> QFuture<T>::then(QtFuture::Launch policy, Function &&function)
|
/*! \fn template<class T> template<class Function> QFuture<typename QFuture<T>::ResultType<Function>> QFuture<T>::then(QtFuture::Launch policy, Function &&function)
|
||||||
@ -926,7 +952,7 @@
|
|||||||
.then(QtFuture::Launch::Inherit, [](int res2){ ... });
|
.then(QtFuture::Launch::Inherit, [](int res2){ ... });
|
||||||
\endcode
|
\endcode
|
||||||
|
|
||||||
\sa onFailed()
|
\sa onFailed(), onCanceled()
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*! \fn template<class T> template<class Function> QFuture<typename QFuture<T>::ResultType<Function>> QFuture<T>::then(QThreadPool *pool, Function &&function)
|
/*! \fn template<class T> template<class Function> QFuture<typename QFuture<T>::ResultType<Function>> QFuture<T>::then(QThreadPool *pool, Function &&function)
|
||||||
@ -939,7 +965,7 @@
|
|||||||
future finishes, \a function will be invoked in a separate thread taken from the
|
future finishes, \a function will be invoked in a separate thread taken from the
|
||||||
QThreadPool \a pool.
|
QThreadPool \a pool.
|
||||||
|
|
||||||
\sa onFailed()
|
\sa onFailed(), onCanceled()
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*! \fn template<class T> template<class Function> QFuture<T> QFuture<T>::onFailed(Function &&handler)
|
/*! \fn template<class T> template<class Function> QFuture<T> QFuture<T>::onFailed(Function &&handler)
|
||||||
@ -970,5 +996,16 @@
|
|||||||
\note You can always attach a handler taking no argument, to handle all exception
|
\note You can always attach a handler taking no argument, to handle all exception
|
||||||
types and avoid writing the try-catch block.
|
types and avoid writing the try-catch block.
|
||||||
|
|
||||||
\sa then()
|
\sa then(), onCanceled()
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*! \fn template<class T> template<class Function> QFuture<T> QFuture<T>::onCanceled(Function &&handler)
|
||||||
|
|
||||||
|
\since 6.0
|
||||||
|
|
||||||
|
Attaches a cancellation \a handler to this future, to be called when the future is
|
||||||
|
canceled. The \a handler is a callable which doesn't take any arguments. It will be
|
||||||
|
invoked in the same thread in which this future has been running.
|
||||||
|
|
||||||
|
\sa then(), onFailed()
|
||||||
*/
|
*/
|
||||||
|
@ -495,6 +495,28 @@ void Continuation<Function, ResultType, ParentResultType>::fulfillPromise(Args &
|
|||||||
promise.reportAndMoveResult(std::invoke(function, std::forward<Args>(args)...));
|
promise.reportAndMoveResult(std::invoke(function, std::forward<Args>(args)...));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
void fulfillPromise(QFutureInterface<T> &promise, QFuture<T> &future)
|
||||||
|
{
|
||||||
|
if constexpr (!std::is_void_v<T>) {
|
||||||
|
if constexpr (std::is_copy_constructible_v<T>)
|
||||||
|
promise.reportResult(future.result());
|
||||||
|
else
|
||||||
|
promise.reportAndMoveResult(future.takeResult());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T, class Function>
|
||||||
|
void fulfillPromise(QFutureInterface<T> &promise, Function &&handler)
|
||||||
|
{
|
||||||
|
if constexpr (std::is_void_v<T>)
|
||||||
|
handler();
|
||||||
|
else if constexpr (std::is_copy_constructible_v<T>)
|
||||||
|
promise.reportResult(handler());
|
||||||
|
else
|
||||||
|
promise.reportAndMoveResult(handler());
|
||||||
|
}
|
||||||
|
|
||||||
#ifndef QT_NO_EXCEPTIONS
|
#ifndef QT_NO_EXCEPTIONS
|
||||||
|
|
||||||
template<class Function, class ResultType>
|
template<class Function, class ResultType>
|
||||||
@ -529,12 +551,7 @@ void FailureHandler<Function, ResultType>::run()
|
|||||||
handleException<ArgType>();
|
handleException<ArgType>();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if constexpr (!std::is_void_v<ResultType>) {
|
QtPrivate::fulfillPromise(promise, parentFuture);
|
||||||
if constexpr (std::is_copy_constructible_v<ResultType>)
|
|
||||||
promise.reportResult(parentFuture.result());
|
|
||||||
else
|
|
||||||
promise.reportAndMoveResult(parentFuture.takeResult());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
promise.reportFinished();
|
promise.reportFinished();
|
||||||
}
|
}
|
||||||
@ -573,12 +590,7 @@ void FailureHandler<Function, ResultType>::handleAllExceptions()
|
|||||||
parentFuture.d.exceptionStore().throwPossibleException();
|
parentFuture.d.exceptionStore().throwPossibleException();
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
try {
|
try {
|
||||||
if constexpr (std::is_void_v<ResultType>)
|
QtPrivate::fulfillPromise(promise, std::forward<Function>(handler));
|
||||||
handler();
|
|
||||||
else if constexpr (std::is_copy_constructible_v<ResultType>)
|
|
||||||
promise.reportResult(handler());
|
|
||||||
else
|
|
||||||
promise.reportAndMoveResult(handler());
|
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
promise.reportException(std::current_exception());
|
promise.reportException(std::current_exception());
|
||||||
}
|
}
|
||||||
@ -587,6 +599,45 @@ void FailureHandler<Function, ResultType>::handleAllExceptions()
|
|||||||
|
|
||||||
#endif // QT_NO_EXCEPTIONS
|
#endif // QT_NO_EXCEPTIONS
|
||||||
|
|
||||||
|
template<class Function, class ResultType>
|
||||||
|
class CanceledHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static QFuture<ResultType> create(Function &&handler, QFuture<ResultType> *future,
|
||||||
|
QFutureInterface<ResultType> promise)
|
||||||
|
{
|
||||||
|
Q_ASSERT(future);
|
||||||
|
|
||||||
|
auto canceledContinuation = [parentFuture = *future, promise,
|
||||||
|
handler = std::move(handler)]() mutable {
|
||||||
|
promise.reportStarted();
|
||||||
|
|
||||||
|
if (parentFuture.isCanceled()) {
|
||||||
|
#ifndef QT_NO_EXCEPTIONS
|
||||||
|
if (parentFuture.d.exceptionStore().hasException()) {
|
||||||
|
// Propagate the exception to the result future
|
||||||
|
promise.reportException(parentFuture.d.exceptionStore().exception());
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
#endif
|
||||||
|
QtPrivate::fulfillPromise(promise, std::forward<Function>(handler));
|
||||||
|
#ifndef QT_NO_EXCEPTIONS
|
||||||
|
} catch (...) {
|
||||||
|
promise.reportException(std::current_exception());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
} else {
|
||||||
|
QtPrivate::fulfillPromise(promise, parentFuture);
|
||||||
|
}
|
||||||
|
|
||||||
|
promise.reportFinished();
|
||||||
|
};
|
||||||
|
future->d.setContinuation(std::move(canceledContinuation));
|
||||||
|
return promise.future();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace QtPrivate
|
} // namespace QtPrivate
|
||||||
|
|
||||||
namespace QtFuture {
|
namespace QtFuture {
|
||||||
|
@ -64,6 +64,9 @@ namespace QtPrivate {
|
|||||||
template<typename Function, typename ResultType, typename ParentResultType>
|
template<typename Function, typename ResultType, typename ParentResultType>
|
||||||
class Continuation;
|
class Continuation;
|
||||||
|
|
||||||
|
template<class Function, class ResultType>
|
||||||
|
class CanceledHandler;
|
||||||
|
|
||||||
#ifndef QT_NO_EXCEPTIONS
|
#ifndef QT_NO_EXCEPTIONS
|
||||||
template<class Function, class ResultType>
|
template<class Function, class ResultType>
|
||||||
class FailureHandler;
|
class FailureHandler;
|
||||||
@ -163,6 +166,9 @@ private:
|
|||||||
template<typename Function, typename ResultType, typename ParentResultType>
|
template<typename Function, typename ResultType, typename ParentResultType>
|
||||||
friend class QtPrivate::Continuation;
|
friend class QtPrivate::Continuation;
|
||||||
|
|
||||||
|
template<class Function, class ResultType>
|
||||||
|
friend class QtPrivate::CanceledHandler;
|
||||||
|
|
||||||
#ifndef QT_NO_EXCEPTIONS
|
#ifndef QT_NO_EXCEPTIONS
|
||||||
template<class Function, class ResultType>
|
template<class Function, class ResultType>
|
||||||
friend class QtPrivate::FailureHandler;
|
friend class QtPrivate::FailureHandler;
|
||||||
|
@ -131,6 +131,7 @@ private slots:
|
|||||||
void onFailedTestCallables();
|
void onFailedTestCallables();
|
||||||
void onFailedForMoveOnlyTypes();
|
void onFailedForMoveOnlyTypes();
|
||||||
#endif
|
#endif
|
||||||
|
void onCanceled();
|
||||||
void takeResults();
|
void takeResults();
|
||||||
void takeResult();
|
void takeResult();
|
||||||
void runAndTake();
|
void runAndTake();
|
||||||
@ -1933,18 +1934,24 @@ void tst_QFuture::thenForMoveOnlyTypes()
|
|||||||
QVERIFY(runThenForMoveOnly<void>([] { return std::make_unique<int>(42); }));
|
QVERIFY(runThenForMoveOnly<void>([] { return std::make_unique<int>(42); }));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
QFuture<T> createCanceledFuture()
|
||||||
|
{
|
||||||
|
QFutureInterface<T> promise;
|
||||||
|
promise.reportStarted();
|
||||||
|
promise.reportCanceled();
|
||||||
|
promise.reportFinished();
|
||||||
|
return promise.future();
|
||||||
|
}
|
||||||
|
|
||||||
void tst_QFuture::thenOnCanceledFuture()
|
void tst_QFuture::thenOnCanceledFuture()
|
||||||
{
|
{
|
||||||
// Continuations on a canceled future
|
// Continuations on a canceled future
|
||||||
{
|
{
|
||||||
QFutureInterface<void> promise;
|
|
||||||
promise.reportStarted();
|
|
||||||
promise.reportCanceled();
|
|
||||||
promise.reportFinished();
|
|
||||||
|
|
||||||
int thenResult = 0;
|
int thenResult = 0;
|
||||||
QFuture<void> then =
|
QFuture<void> then = createCanceledFuture<void>().then([&]() { ++thenResult; }).then([&]() {
|
||||||
promise.future().then([&]() { ++thenResult; }).then([&]() { ++thenResult; });
|
++thenResult;
|
||||||
|
});
|
||||||
|
|
||||||
QVERIFY(then.isCanceled());
|
QVERIFY(then.isCanceled());
|
||||||
QCOMPARE(thenResult, 0);
|
QCOMPARE(thenResult, 0);
|
||||||
@ -1970,16 +1977,10 @@ void tst_QFuture::thenOnCanceledFuture()
|
|||||||
|
|
||||||
// Continuations on a canceled future
|
// Continuations on a canceled future
|
||||||
{
|
{
|
||||||
QFutureInterface<void> promise;
|
|
||||||
promise.reportStarted();
|
|
||||||
promise.reportCanceled();
|
|
||||||
promise.reportFinished();
|
|
||||||
|
|
||||||
int thenResult = 0;
|
int thenResult = 0;
|
||||||
QFuture<void> then =
|
QFuture<void> then = createCanceledFuture<void>()
|
||||||
promise.future().then(QtFuture::Launch::Async, [&]() { ++thenResult; }).then([&]() {
|
.then(QtFuture::Launch::Async, [&]() { ++thenResult; })
|
||||||
++thenResult;
|
.then([&]() { ++thenResult; });
|
||||||
});
|
|
||||||
|
|
||||||
QVERIFY(then.isCanceled());
|
QVERIFY(then.isCanceled());
|
||||||
QCOMPARE(thenResult, 0);
|
QCOMPARE(thenResult, 0);
|
||||||
@ -2486,6 +2487,15 @@ void tst_QFuture::onFailed()
|
|||||||
QCOMPARE(checkpoint, 3);
|
QCOMPARE(checkpoint, 3);
|
||||||
QCOMPARE(res, 0);
|
QCOMPARE(res, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// onFailed on a canceled future
|
||||||
|
{
|
||||||
|
auto future = createCanceledFuture<int>()
|
||||||
|
.then([](int) { return 42; })
|
||||||
|
.onCanceled([] { return -1; })
|
||||||
|
.onFailed([] { return -2; });
|
||||||
|
QCOMPARE(future.result(), -1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class Callable>
|
template<class Callable>
|
||||||
@ -2565,6 +2575,84 @@ void tst_QFuture::onFailedForMoveOnlyTypes()
|
|||||||
|
|
||||||
#endif // QT_NO_EXCEPTIONS
|
#endif // QT_NO_EXCEPTIONS
|
||||||
|
|
||||||
|
void tst_QFuture::onCanceled()
|
||||||
|
{
|
||||||
|
// Canceled int future
|
||||||
|
{
|
||||||
|
auto future = createCanceledFuture<int>().then([](int) { return 42; }).onCanceled([] {
|
||||||
|
return -1;
|
||||||
|
});
|
||||||
|
QCOMPARE(future.result(), -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Canceled void future
|
||||||
|
{
|
||||||
|
int checkpoint = 0;
|
||||||
|
auto future = createCanceledFuture<void>().then([&] { checkpoint = 42; }).onCanceled([&] {
|
||||||
|
checkpoint = -1;
|
||||||
|
});
|
||||||
|
QCOMPARE(checkpoint, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// onCanceled propagates result
|
||||||
|
{
|
||||||
|
QFutureInterface<int> promise;
|
||||||
|
auto future =
|
||||||
|
promise.future().then([](int res) { return res; }).onCanceled([] { return -1; });
|
||||||
|
|
||||||
|
promise.reportStarted();
|
||||||
|
promise.reportResult(42);
|
||||||
|
promise.reportFinished();
|
||||||
|
QCOMPARE(future.result(), 42);
|
||||||
|
}
|
||||||
|
|
||||||
|
// onCanceled propagates move-only result
|
||||||
|
{
|
||||||
|
QFutureInterface<UniquePtr> promise;
|
||||||
|
auto future = promise.future().then([](UniquePtr res) { return res; }).onCanceled([] {
|
||||||
|
return std::make_unique<int>(-1);
|
||||||
|
});
|
||||||
|
|
||||||
|
promise.reportStarted();
|
||||||
|
promise.reportAndMoveResult(std::make_unique<int>(42));
|
||||||
|
promise.reportFinished();
|
||||||
|
QCOMPARE(*future.takeResult(), 42);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef QT_NO_EXCEPTIONS
|
||||||
|
// onCanceled propagates exceptions
|
||||||
|
{
|
||||||
|
QFutureInterface<int> promise;
|
||||||
|
auto future = promise.future()
|
||||||
|
.then([](int res) {
|
||||||
|
throw std::runtime_error("error");
|
||||||
|
return res;
|
||||||
|
})
|
||||||
|
.onCanceled([] { return 2; })
|
||||||
|
.onFailed([] { return 3; });
|
||||||
|
|
||||||
|
promise.reportStarted();
|
||||||
|
promise.reportResult(1);
|
||||||
|
promise.reportFinished();
|
||||||
|
QCOMPARE(future.result(), 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
// onCanceled throws
|
||||||
|
{
|
||||||
|
auto future = createCanceledFuture<int>()
|
||||||
|
.then([](int) { return 42; })
|
||||||
|
.onCanceled([] {
|
||||||
|
throw std::runtime_error("error");
|
||||||
|
return -1;
|
||||||
|
})
|
||||||
|
.onFailed([] { return -2; });
|
||||||
|
|
||||||
|
QCOMPARE(future.result(), -2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // QT_NO_EXCEPTIONS
|
||||||
|
}
|
||||||
|
|
||||||
void tst_QFuture::testSingleResult(const UniquePtr &p)
|
void tst_QFuture::testSingleResult(const UniquePtr &p)
|
||||||
{
|
{
|
||||||
QVERIFY(p.get() != nullptr);
|
QVERIFY(p.get() != nullptr);
|
||||||
|
Loading…
Reference in New Issue
Block a user