Store QFuture exceptions as std::exception_ptr
Replaced the internal ExceptionHolder for storing QException* by std::exception_ptr. This will allow to report and store exceptions of types that are not derived from QException. Task-number: QTBUG-81588 Change-Id: I96be919d8289448b3e608310e51a16cebc586301 Reviewed-by: Vitaly Fanaskov <vitaly.fanaskov@qt.io> Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
This commit is contained in:
parent
7909de1beb
commit
495f958b9a
@ -324,7 +324,7 @@ void ThreadEngineBase::handleException(const QException &exception)
|
|||||||
{
|
{
|
||||||
if (futureInterface)
|
if (futureInterface)
|
||||||
futureInterface->reportException(exception);
|
futureInterface->reportException(exception);
|
||||||
else
|
else if (!exceptionStore.hasException())
|
||||||
exceptionStore.setException(exception);
|
exceptionStore.setException(exception);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -158,65 +158,38 @@ QUnhandledException *QUnhandledException::clone() const
|
|||||||
|
|
||||||
namespace QtPrivate {
|
namespace QtPrivate {
|
||||||
|
|
||||||
class Base : public QSharedData
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
Base(QException *exception)
|
|
||||||
: exception(exception), hasThrown(false) { }
|
|
||||||
~Base() { delete exception; }
|
|
||||||
|
|
||||||
QException *exception;
|
|
||||||
bool hasThrown;
|
|
||||||
};
|
|
||||||
|
|
||||||
ExceptionHolder::ExceptionHolder(QException *exception)
|
|
||||||
: base(exception ? new Base(exception) : nullptr) {}
|
|
||||||
|
|
||||||
ExceptionHolder::ExceptionHolder(const ExceptionHolder &other)
|
|
||||||
: base(other.base)
|
|
||||||
{}
|
|
||||||
|
|
||||||
void ExceptionHolder::operator=(const ExceptionHolder &other)
|
|
||||||
{
|
|
||||||
base = other.base;
|
|
||||||
}
|
|
||||||
|
|
||||||
ExceptionHolder::~ExceptionHolder()
|
|
||||||
{}
|
|
||||||
|
|
||||||
QException *ExceptionHolder::exception() const
|
|
||||||
{
|
|
||||||
if (!base)
|
|
||||||
return nullptr;
|
|
||||||
return base->exception;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ExceptionStore::setException(const QException &e)
|
void ExceptionStore::setException(const QException &e)
|
||||||
{
|
{
|
||||||
if (hasException() == false)
|
Q_ASSERT(!hasException());
|
||||||
exceptionHolder = ExceptionHolder(e.clone());
|
try {
|
||||||
|
e.raise();
|
||||||
|
} catch (...) {
|
||||||
|
exceptionHolder = std::current_exception();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExceptionStore::setException(std::exception_ptr e)
|
||||||
|
{
|
||||||
|
Q_ASSERT(!hasException());
|
||||||
|
exceptionHolder = e;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ExceptionStore::hasException() const
|
bool ExceptionStore::hasException() const
|
||||||
{
|
{
|
||||||
return (exceptionHolder.exception() != nullptr);
|
return !!exceptionHolder;
|
||||||
}
|
}
|
||||||
|
|
||||||
ExceptionHolder ExceptionStore::exception()
|
std::exception_ptr ExceptionStore::exception() const
|
||||||
{
|
{
|
||||||
return exceptionHolder;
|
return exceptionHolder;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExceptionStore::throwPossibleException()
|
void ExceptionStore::throwPossibleException()
|
||||||
{
|
{
|
||||||
if (hasException() ) {
|
if (hasException())
|
||||||
exceptionHolder.base->hasThrown = true;
|
std::rethrow_exception(exceptionHolder);
|
||||||
exceptionHolder.exception()->raise();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ExceptionStore::hasThrown() const { return exceptionHolder.base->hasThrown; }
|
|
||||||
|
|
||||||
} // namespace QtPrivate
|
} // namespace QtPrivate
|
||||||
|
|
||||||
#endif //Q_CLANG_QDOC
|
#endif //Q_CLANG_QDOC
|
||||||
|
@ -84,27 +84,15 @@ public:
|
|||||||
|
|
||||||
namespace QtPrivate {
|
namespace QtPrivate {
|
||||||
|
|
||||||
class Base;
|
|
||||||
class Q_CORE_EXPORT ExceptionHolder
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
ExceptionHolder(QException *exception = nullptr);
|
|
||||||
ExceptionHolder(const ExceptionHolder &other);
|
|
||||||
void operator=(const ExceptionHolder &other); // ### Qt6: copy-assign operator shouldn't return void. Remove this method and the copy-ctor, they are unneeded.
|
|
||||||
~ExceptionHolder();
|
|
||||||
QException *exception() const;
|
|
||||||
QExplicitlySharedDataPointer<Base> base;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Q_CORE_EXPORT ExceptionStore
|
class Q_CORE_EXPORT ExceptionStore
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
void setException(const QException &e);
|
void setException(const QException &e);
|
||||||
|
void setException(std::exception_ptr e);
|
||||||
bool hasException() const;
|
bool hasException() const;
|
||||||
ExceptionHolder exception();
|
std::exception_ptr exception() const;
|
||||||
void throwPossibleException();
|
void throwPossibleException();
|
||||||
bool hasThrown() const;
|
std::exception_ptr exceptionHolder;
|
||||||
ExceptionHolder exceptionHolder;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace QtPrivate
|
} // namespace QtPrivate
|
||||||
|
@ -226,10 +226,8 @@ void Continuation<Function, ResultType, ParentResultType>::runFunction()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#ifndef QT_NO_EXCEPTIONS
|
#ifndef QT_NO_EXCEPTIONS
|
||||||
} catch (QException &e) {
|
|
||||||
promise.reportException(e);
|
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
promise.reportException(QUnhandledException());
|
promise.reportException(std::current_exception());
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
promise.reportFinished();
|
promise.reportFinished();
|
||||||
@ -249,8 +247,7 @@ bool Continuation<Function, ResultType, ParentResultType>::execute()
|
|||||||
// interrupt the continuation chain, so don't report anything yet.
|
// interrupt the continuation chain, so don't report anything yet.
|
||||||
if constexpr (!std::is_invocable_v<std::decay_t<Function>, QFuture<ParentResultType>>) {
|
if constexpr (!std::is_invocable_v<std::decay_t<Function>, QFuture<ParentResultType>>) {
|
||||||
promise.reportStarted();
|
promise.reportStarted();
|
||||||
const QException *e = parentFuture.d.exceptionStore().exception().exception();
|
promise.reportException(parentFuture.d.exceptionStore().exception());
|
||||||
promise.reportException(*e);
|
|
||||||
promise.reportFinished();
|
promise.reportFinished();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -282,6 +282,15 @@ void QFutureInterfaceBase::reportCanceled()
|
|||||||
|
|
||||||
#ifndef QT_NO_EXCEPTIONS
|
#ifndef QT_NO_EXCEPTIONS
|
||||||
void QFutureInterfaceBase::reportException(const QException &exception)
|
void QFutureInterfaceBase::reportException(const QException &exception)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
exception.raise();
|
||||||
|
} catch (...) {
|
||||||
|
reportException(std::current_exception());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void QFutureInterfaceBase::reportException(std::exception_ptr exception)
|
||||||
{
|
{
|
||||||
QMutexLocker locker(&d->m_mutex);
|
QMutexLocker locker(&d->m_mutex);
|
||||||
if (d->state.loadRelaxed() & (Canceled|Finished))
|
if (d->state.loadRelaxed() & (Canceled|Finished))
|
||||||
|
@ -90,6 +90,7 @@ public:
|
|||||||
void reportCanceled();
|
void reportCanceled();
|
||||||
#ifndef QT_NO_EXCEPTIONS
|
#ifndef QT_NO_EXCEPTIONS
|
||||||
void reportException(const QException &e);
|
void reportException(const QException &e);
|
||||||
|
void reportException(std::exception_ptr e);
|
||||||
#endif
|
#endif
|
||||||
void reportResultsReady(int beginIndex, int endIndex);
|
void reportResultsReady(int beginIndex, int endIndex);
|
||||||
|
|
||||||
|
@ -1418,6 +1418,23 @@ QFuture<void> createDerivedExceptionFuture()
|
|||||||
return f;
|
return f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct TestException
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
QFuture<int> createCustomExceptionFuture()
|
||||||
|
{
|
||||||
|
QFutureInterface<int> i;
|
||||||
|
i.reportStarted();
|
||||||
|
QFuture<int> f = i.future();
|
||||||
|
int r = 0;
|
||||||
|
i.reportResult(r);
|
||||||
|
auto exception = std::make_exception_ptr(TestException());
|
||||||
|
i.reportException(exception);
|
||||||
|
i.reportFinished();
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
|
||||||
void tst_QFuture::exceptions()
|
void tst_QFuture::exceptions()
|
||||||
{
|
{
|
||||||
// test throwing from waitForFinished
|
// test throwing from waitForFinished
|
||||||
@ -1502,6 +1519,18 @@ void tst_QFuture::exceptions()
|
|||||||
}
|
}
|
||||||
QVERIFY(caught);
|
QVERIFY(caught);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Custom exceptions
|
||||||
|
{
|
||||||
|
QFuture<int> f = createCustomExceptionFuture();
|
||||||
|
bool caught = false;
|
||||||
|
try {
|
||||||
|
f.result();
|
||||||
|
} catch (const TestException &) {
|
||||||
|
caught = true;
|
||||||
|
}
|
||||||
|
QVERIFY(caught);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class MyClass
|
class MyClass
|
||||||
@ -2040,46 +2069,87 @@ void tst_QFuture::thenOnExceptionFuture()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<class Exception, bool hasTestMsg = false>
|
||||||
|
QFuture<void> createExceptionContinuation(QtFuture::Launch policy = QtFuture::Launch::Sync)
|
||||||
|
{
|
||||||
|
QFutureInterface<void> promise;
|
||||||
|
|
||||||
|
auto then = promise.future().then(policy, [] {
|
||||||
|
if constexpr (hasTestMsg)
|
||||||
|
throw Exception("TEST");
|
||||||
|
else
|
||||||
|
throw Exception();
|
||||||
|
});
|
||||||
|
|
||||||
|
promise.reportStarted();
|
||||||
|
promise.reportFinished();
|
||||||
|
|
||||||
|
return then;
|
||||||
|
}
|
||||||
|
|
||||||
void tst_QFuture::thenThrows()
|
void tst_QFuture::thenThrows()
|
||||||
{
|
{
|
||||||
// Continuation throws an exception
|
// Continuation throws a QException
|
||||||
{
|
{
|
||||||
QFutureInterface<void> promise;
|
auto future = createExceptionContinuation<QException>();
|
||||||
|
|
||||||
QFuture<void> then = promise.future().then([]() { throw QException(); });
|
|
||||||
|
|
||||||
promise.reportStarted();
|
|
||||||
promise.reportFinished();
|
|
||||||
|
|
||||||
bool caught = false;
|
bool caught = false;
|
||||||
try {
|
try {
|
||||||
then.waitForFinished();
|
future.waitForFinished();
|
||||||
} catch (QException &) {
|
} catch (const QException &) {
|
||||||
caught = true;
|
caught = true;
|
||||||
}
|
}
|
||||||
QVERIFY(caught);
|
QVERIFY(caught);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Continuation throws an exception derived from QException
|
||||||
|
{
|
||||||
|
auto future = createExceptionContinuation<DerivedException>();
|
||||||
|
|
||||||
|
bool caught = false;
|
||||||
|
try {
|
||||||
|
future.waitForFinished();
|
||||||
|
} catch (const QException &) {
|
||||||
|
caught = true;
|
||||||
|
} catch (const std::exception &) {
|
||||||
|
QFAIL("The exception should be caught by the above catch block.");
|
||||||
|
}
|
||||||
|
|
||||||
|
QVERIFY(caught);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Continuation throws std::exception
|
||||||
|
{
|
||||||
|
auto future = createExceptionContinuation<std::runtime_error, true>();
|
||||||
|
|
||||||
|
bool caught = false;
|
||||||
|
try {
|
||||||
|
future.waitForFinished();
|
||||||
|
} catch (const QException &) {
|
||||||
|
QFAIL("The exception should be caught by the below catch block.");
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
QCOMPARE(e.what(), "TEST");
|
||||||
|
caught = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVERIFY(caught);
|
||||||
|
}
|
||||||
|
|
||||||
// Same with QtFuture::Launch::Async
|
// Same with QtFuture::Launch::Async
|
||||||
{
|
{
|
||||||
QFutureInterface<void> promise;
|
auto future = createExceptionContinuation<QException>(QtFuture::Launch::Async);
|
||||||
|
|
||||||
QFuture<void> then =
|
|
||||||
promise.future().then(QtFuture::Launch::Async, []() { throw QException(); });
|
|
||||||
|
|
||||||
promise.reportStarted();
|
|
||||||
promise.reportFinished();
|
|
||||||
|
|
||||||
bool caught = false;
|
bool caught = false;
|
||||||
try {
|
try {
|
||||||
then.waitForFinished();
|
future.waitForFinished();
|
||||||
} catch (QException &) {
|
} catch (const QException &) {
|
||||||
caught = true;
|
caught = true;
|
||||||
}
|
}
|
||||||
QVERIFY(caught);
|
QVERIFY(caught);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
#endif // QT_NO_EXCEPTIONS
|
||||||
|
|
||||||
void tst_QFuture::testSingleResult(const UniquePtr &p)
|
void tst_QFuture::testSingleResult(const UniquePtr &p)
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user