diff --git a/src/corelib/thread/qpromise.qdoc b/src/corelib/thread/qpromise.qdoc index 31772c28873..c5397997faa 100644 --- a/src/corelib/thread/qpromise.qdoc +++ b/src/corelib/thread/qpromise.qdoc @@ -162,7 +162,7 @@ \note You can set at most one exception throughout the computation execution. - \note This method must not be used after QFuture::cancel() or + \note This method has no effect after QFuture::cancel() or finish(). \sa isCanceled() diff --git a/tests/auto/corelib/thread/qpromise/tst_qpromise.cpp b/tests/auto/corelib/thread/qpromise/tst_qpromise.cpp index 8de956f094d..18644d2d68a 100644 --- a/tests/auto/corelib/thread/qpromise/tst_qpromise.cpp +++ b/tests/auto/corelib/thread/qpromise/tst_qpromise.cpp @@ -9,13 +9,22 @@ #include #include #include +#include #include #include +#include #include using namespace std::chrono_literals; +template +struct promise_type {}; +template +struct promise_type> : q20::type_identity {}; +template +using promise_type_t = typename promise_type::type; + class tst_QPromise : public QObject { Q_OBJECT @@ -28,6 +37,8 @@ private slots: void addResultOutOfOrder(); #ifndef QT_NO_EXCEPTIONS void setException(); + void setExceptionAfterCancelDoesNothing(); // QTBUG-128405 + void setExceptionAfterFinishDoesNothing(); #endif void cancel(); void progress(); @@ -320,6 +331,69 @@ void tst_QPromise::setException() RUN_TEST_FUNC(testExceptionCaught, QPromise(), std::make_exception_ptr(TestException())); } + +void tst_QPromise::setExceptionAfterCancelDoesNothing() +{ + struct TestException {}; + + auto test = [](auto promise, auto exception) { + auto f = promise.future(); + FutureWatcher r(f); + QSemaphore sem; + QSemaphoreReleaser rel(sem); + ThreadWrapper t([&] { + auto p = std::move(promise); + p.start(); + sem.acquire(); // wait for cancel() in main thread + p.setException(std::move(exception)); + }); + + f.cancel(); + rel.cancel()->release(); // give a go for setException() in worker thread + + t.join(); + + QVERIFY(f.isCanceled()); + QVERIFY(!r.thenCalled); + QVERIFY(!r.onFailedCalled); + QVERIFY(r.onCanceledCalled); + }; + + RUN_TEST_FUNC(test, QPromise(), QException()); + RUN_TEST_FUNC(test, QPromise(), QException()); + RUN_TEST_FUNC(test, QPromise(), std::make_exception_ptr(TestException())); + RUN_TEST_FUNC(test, QPromise(), std::make_exception_ptr(TestException())); + RUN_TEST_FUNC(test, QPromise(), std::make_exception_ptr(TestException())); + RUN_TEST_FUNC(test, QPromise(), std::make_exception_ptr(TestException())); +} + +void tst_QPromise::setExceptionAfterFinishDoesNothing() +{ + struct TestException {}; + + auto test = [](auto promise, auto exception) { + auto f = promise.future(); + FutureWatcher r(f); + promise.start(); + using T = promise_type_t; + if constexpr (!std::is_void_v) + promise.addResult(T()); + promise.finish(); + promise.setException(std::move(exception)); + + QVERIFY(f.isFinished()); + QVERIFY(r.thenCalled); + QVERIFY(!r.onFailedCalled); + QVERIFY(!r.onCanceledCalled); + }; + + RUN_TEST_FUNC(test, QPromise(), QException()); + RUN_TEST_FUNC(test, QPromise(), QException()); + RUN_TEST_FUNC(test, QPromise(), std::make_exception_ptr(TestException())); + RUN_TEST_FUNC(test, QPromise(), std::make_exception_ptr(TestException())); + RUN_TEST_FUNC(test, QPromise(), std::make_exception_ptr(TestException())); + RUN_TEST_FUNC(test, QPromise(), std::make_exception_ptr(TestException())); +} #endif void tst_QPromise::cancel()