Store std::exception_ptr in QUnhandledException
For historical reasons Qt Concurrent reports QUnhandledException in case if an exception that is not derived from QException is thrown from a worker thread. Changing this behavior may not be a good idea, since the existing user code may rely on it. Changed QUnhandledException to wrap the std::exception_ptr to the actual exception, so that the users can obtain the information about the thrown exception if needed. [ChangeLog][QtCore][QUnhandledException] Improved QUnhandledException to store the std::exception_ptr to the actual exception thrown from a QtCocnurrent worker thread. Change-Id: I30e7c1d3e01aff6e1ed9938c421da0a888f12066 Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
This commit is contained in:
parent
1aa3459d0a
commit
4c793e6353
@ -114,7 +114,7 @@ public:
|
||||
} catch (QException &e) {
|
||||
promise.reportException(e);
|
||||
} catch (...) {
|
||||
promise.reportException(QUnhandledException());
|
||||
promise.reportException(QUnhandledException(std::current_exception()));
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -196,7 +196,7 @@ void ThreadEngineBase::startBlocking()
|
||||
} catch (QException &e) {
|
||||
handleException(e);
|
||||
} catch (...) {
|
||||
handleException(QUnhandledException());
|
||||
handleException(QUnhandledException(std::current_exception()));
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -325,7 +325,7 @@ void ThreadEngineBase::run() // implements QRunnable.
|
||||
} catch (QException &e) {
|
||||
handleException(e);
|
||||
} catch (...) {
|
||||
handleException(QUnhandledException());
|
||||
handleException(QUnhandledException(std::current_exception()));
|
||||
}
|
||||
#endif
|
||||
threadExit();
|
||||
|
@ -83,3 +83,19 @@ void MyException::raise() const { throw *this; }
|
||||
MyException *MyException::clone() const { return new MyException(*this); }
|
||||
|
||||
//! [3]
|
||||
|
||||
//! [4]
|
||||
|
||||
try {
|
||||
auto f = QtConcurrent::run([] { throw MyException {}; });
|
||||
// ...
|
||||
} catch (const QUnhandledException &e) {
|
||||
try {
|
||||
if (e.exception())
|
||||
std::rethrow_exception(e.exception());
|
||||
} catch (const MyException &ex) {
|
||||
// Process 'ex'
|
||||
}
|
||||
}
|
||||
|
||||
//! [4]
|
||||
|
@ -62,7 +62,7 @@ QT_BEGIN_NAMESPACE
|
||||
\snippet code/src_corelib_thread_qexception.cpp 1
|
||||
|
||||
If you throw an exception that is not a subclass of QException,
|
||||
the Qt functions will throw a QUnhandledException
|
||||
the \l{Qt Concurrent} functions will throw a QUnhandledException
|
||||
in the receiver thread.
|
||||
|
||||
When using QFuture, transferred exceptions will be thrown when calling the following functions:
|
||||
@ -92,12 +92,18 @@ QT_BEGIN_NAMESPACE
|
||||
\class QUnhandledException
|
||||
\inmodule QtCore
|
||||
|
||||
\brief The UnhandledException class represents an unhandled exception in a worker thread.
|
||||
\brief The QUnhandledException class represents an unhandled exception in a
|
||||
Qt Concurrent worker thread.
|
||||
\since 5.0
|
||||
|
||||
If a worker thread throws an exception that is not a subclass of QException,
|
||||
the Qt functions will throw a QUnhandledException
|
||||
on the receiver thread side.
|
||||
the \l{Qt Concurrent} functions will throw a QUnhandledException on the receiver
|
||||
thread side. The information about the actual exception that has been thrown
|
||||
will be saved in the QUnhandledException class and can be obtained using the
|
||||
exception() method. For example, you can process the exception held by
|
||||
QUnhandledException in the following way:
|
||||
|
||||
\snippet code/src_corelib_thread_qexception.cpp 4
|
||||
|
||||
Inheriting from this class is not supported.
|
||||
*/
|
||||
@ -127,6 +133,74 @@ QException *QException::clone() const
|
||||
return new QException(*this);
|
||||
}
|
||||
|
||||
class QUnhandledExceptionPrivate : public QSharedData
|
||||
{
|
||||
public:
|
||||
QUnhandledExceptionPrivate(std::exception_ptr exception) noexcept : exceptionPtr(exception) { }
|
||||
std::exception_ptr exceptionPtr;
|
||||
};
|
||||
|
||||
/*!
|
||||
\fn QUnhandledException::QUnhandledException(std::exception_ptr exception = nullptr) noexcept
|
||||
\since 6.0
|
||||
|
||||
Constructs a new QUnhandledException object. Saves the pointer to the actual
|
||||
exception object if \a exception is passed.
|
||||
|
||||
\sa exception()
|
||||
*/
|
||||
QUnhandledException::QUnhandledException(std::exception_ptr exception) noexcept
|
||||
: d(new QUnhandledExceptionPrivate(exception))
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
Move-constructs a QUnhandledException, making it point to the same
|
||||
object as \a other was pointing to.
|
||||
*/
|
||||
QUnhandledException::QUnhandledException(QUnhandledException &&other) noexcept
|
||||
: d(std::exchange(other.d, {}))
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
Constructs a QUnhandledException object as a copy of \a other.
|
||||
*/
|
||||
QUnhandledException::QUnhandledException(const QUnhandledException &other) noexcept
|
||||
: d(other.d)
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
Assigns \a other to this QUnhandledException object and returns a reference
|
||||
to this QUnhandledException object.
|
||||
*/
|
||||
QUnhandledException &QUnhandledException::operator=(const QUnhandledException &other) noexcept
|
||||
{
|
||||
d = other.d;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/*!
|
||||
\fn void QUnhandledException::swap(QUnhandledException &other)
|
||||
\since 6.0
|
||||
|
||||
Swaps this QUnhandledException with \a other. This function is very fast and
|
||||
never fails.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\since 6.0
|
||||
|
||||
Returns a \l{https://en.cppreference.com/w/cpp/error/exception_ptr}{pointer} to
|
||||
the actual exception that has been saved in this QUnhandledException. Returns a
|
||||
\c null pointer, if it does not point to an exception object.
|
||||
*/
|
||||
std::exception_ptr QUnhandledException::exception() const
|
||||
{
|
||||
return d->exceptionPtr;
|
||||
}
|
||||
|
||||
QUnhandledException::~QUnhandledException() noexcept
|
||||
{
|
||||
}
|
||||
|
@ -62,12 +62,28 @@ public:
|
||||
virtual QException *clone() const;
|
||||
};
|
||||
|
||||
class Q_CORE_EXPORT QUnhandledException : public QException
|
||||
class QUnhandledExceptionPrivate;
|
||||
class Q_CORE_EXPORT QUnhandledException final : public QException
|
||||
{
|
||||
public:
|
||||
~QUnhandledException() noexcept;
|
||||
QUnhandledException(std::exception_ptr exception = nullptr) noexcept;
|
||||
~QUnhandledException() noexcept override;
|
||||
|
||||
QUnhandledException(QUnhandledException &&other) noexcept;
|
||||
QUnhandledException(const QUnhandledException &other) noexcept;
|
||||
|
||||
void swap(QUnhandledException &other) noexcept { qSwap(d, other.d); }
|
||||
|
||||
QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_PURE_SWAP(QUnhandledException)
|
||||
QUnhandledException &operator=(const QUnhandledException &other) noexcept;
|
||||
|
||||
void raise() const override;
|
||||
QUnhandledException *clone() const override;
|
||||
|
||||
std::exception_ptr exception() const;
|
||||
|
||||
private:
|
||||
QSharedDataPointer<QUnhandledExceptionPrivate> d;
|
||||
};
|
||||
|
||||
namespace QtPrivate {
|
||||
|
@ -48,6 +48,7 @@ private slots:
|
||||
void recursive();
|
||||
#ifndef QT_NO_EXCEPTIONS
|
||||
void exceptions();
|
||||
void unhandledException();
|
||||
#endif
|
||||
void functor();
|
||||
void lambda();
|
||||
@ -890,6 +891,25 @@ void tst_QtConcurrentRun::exceptions()
|
||||
|
||||
QVERIFY2(caught, "did not get exception");
|
||||
}
|
||||
|
||||
void tst_QtConcurrentRun::unhandledException()
|
||||
{
|
||||
struct Exception {};
|
||||
bool caught = false;
|
||||
try {
|
||||
auto f = QtConcurrent::run([] { throw Exception {}; });
|
||||
f.waitForFinished();
|
||||
} catch (const QUnhandledException &e) {
|
||||
try {
|
||||
if (e.exception())
|
||||
std::rethrow_exception(e.exception());
|
||||
} catch (const Exception &) {
|
||||
caught = true;
|
||||
}
|
||||
}
|
||||
|
||||
QVERIFY(caught);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Compiler supports decltype
|
||||
|
@ -430,10 +430,10 @@ public:
|
||||
QThread *blockThread;
|
||||
};
|
||||
|
||||
class UnrelatedExceptionThrower : public ThreadEngine<void>
|
||||
class IntExceptionThrower : public ThreadEngine<void>
|
||||
{
|
||||
public:
|
||||
UnrelatedExceptionThrower(QThread *blockThread = nullptr)
|
||||
IntExceptionThrower(QThread *blockThread = nullptr)
|
||||
: ThreadEngine(QThreadPool::globalInstance())
|
||||
{
|
||||
this->blockThread = blockThread;
|
||||
@ -480,7 +480,7 @@ void tst_QtConcurrentThreadEngine::exceptions()
|
||||
{
|
||||
bool caught = false;
|
||||
try {
|
||||
QtConcurrentExceptionThrower e(0);
|
||||
QtConcurrentExceptionThrower e(nullptr);
|
||||
e.startBlocking();
|
||||
} catch (const QException &) {
|
||||
caught = true;
|
||||
@ -492,11 +492,17 @@ void tst_QtConcurrentThreadEngine::exceptions()
|
||||
{
|
||||
bool caught = false;
|
||||
try {
|
||||
UnrelatedExceptionThrower *e = new UnrelatedExceptionThrower();
|
||||
IntExceptionThrower *e = new IntExceptionThrower();
|
||||
QFuture<void> f = e->startAsynchronously();
|
||||
f.waitForFinished();
|
||||
} catch (const QUnhandledException &) {
|
||||
caught = true;
|
||||
} catch (const QUnhandledException &ex) {
|
||||
// Make sure the exception info is not lost
|
||||
try {
|
||||
if (ex.exception())
|
||||
std::rethrow_exception(ex.exception());
|
||||
} catch (int) {
|
||||
caught = true;
|
||||
}
|
||||
}
|
||||
QVERIFY2(caught, "did not get exception");
|
||||
}
|
||||
@ -506,10 +512,16 @@ void tst_QtConcurrentThreadEngine::exceptions()
|
||||
{
|
||||
bool caught = false;
|
||||
try {
|
||||
UnrelatedExceptionThrower e(QThread::currentThread());
|
||||
IntExceptionThrower e(QThread::currentThread());
|
||||
e.startBlocking();
|
||||
} catch (const QUnhandledException &) {
|
||||
caught = true;
|
||||
} catch (const QUnhandledException &ex) {
|
||||
// Make sure the exception info is not lost
|
||||
try {
|
||||
if (ex.exception())
|
||||
std::rethrow_exception(ex.exception());
|
||||
} catch (int) {
|
||||
caught = true;
|
||||
}
|
||||
}
|
||||
QVERIFY2(caught, "did not get exception");
|
||||
}
|
||||
@ -518,10 +530,16 @@ void tst_QtConcurrentThreadEngine::exceptions()
|
||||
{
|
||||
bool caught = false;
|
||||
try {
|
||||
UnrelatedExceptionThrower e(0);
|
||||
IntExceptionThrower e(nullptr);
|
||||
e.startBlocking();
|
||||
} catch (const QUnhandledException &) {
|
||||
caught = true;
|
||||
} catch (const QUnhandledException &ex) {
|
||||
// Make sure the exception info is not lost
|
||||
try {
|
||||
if (ex.exception())
|
||||
std::rethrow_exception(ex.exception());
|
||||
} catch (int) {
|
||||
caught = true;
|
||||
}
|
||||
}
|
||||
QVERIFY2(caught, "did not get exception");
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user