QThread: terminate on exceptions leaving run()
Align ourselves to the Standard Library and call std::terminate if an exception leaves the thread entry point (that is, run()). On platforms using pthreads, thread cancellation needs to be taken in special consideration, since it looks like it was supported before. On Glibc, and when using C++, pthread_cancel and pthread_exit are implemented by throwing a special kind of exception that can be caught, but must always be rethrown. That exception is then used to activate the cancellation clean-up handlers. (This is non-Standard C++ behavior.) So: mimic what libstdc++'s std::thread does to support Glibc's pthread cancellation. At this time, it looks like libc++ has no support for this, and when used in combination with Glibc a thread cancellation results in a crash (also because it does not seem to terminate() when exceptions leave the thread). [ChangeLog][QtCore][QThread] An exception escaping from QThread::run() will now result in immediate and abnormal program termination. The same applies if an exception leaves a slot connected directly to the QThread::started() or QThread::finished() signals. Change-Id: I73cc93cf06c57018e149a578cc9d4cd0d6fc00ef Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
parent
164392548e
commit
5677b70eee
@ -184,8 +184,8 @@ public:
|
||||
#endif // Q_OS_UNIX
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
static unsigned int __stdcall start(void *);
|
||||
static void finish(void *, bool lockAnyway=true);
|
||||
static unsigned int __stdcall start(void *) Q_DECL_NOEXCEPT;
|
||||
static void finish(void *, bool lockAnyway=true) Q_DECL_NOEXCEPT;
|
||||
|
||||
Qt::HANDLE handle;
|
||||
unsigned int id;
|
||||
|
@ -61,6 +61,10 @@
|
||||
|
||||
#include "qdebug.h"
|
||||
|
||||
#ifdef __GLIBCXX__
|
||||
#include <cxxabi.h>
|
||||
#endif
|
||||
|
||||
#include <sched.h>
|
||||
#include <errno.h>
|
||||
|
||||
@ -324,49 +328,70 @@ void *QThreadPrivate::start(void *arg)
|
||||
#endif
|
||||
pthread_cleanup_push(QThreadPrivate::finish, arg);
|
||||
|
||||
QThread *thr = reinterpret_cast<QThread *>(arg);
|
||||
QThreadData *data = QThreadData::get2(thr);
|
||||
|
||||
#ifndef QT_NO_EXCEPTIONS
|
||||
try
|
||||
#endif
|
||||
{
|
||||
QMutexLocker locker(&thr->d_func()->mutex);
|
||||
QThread *thr = reinterpret_cast<QThread *>(arg);
|
||||
QThreadData *data = QThreadData::get2(thr);
|
||||
|
||||
// do we need to reset the thread priority?
|
||||
if (int(thr->d_func()->priority) & ThreadPriorityResetFlag) {
|
||||
thr->d_func()->setPriority(QThread::Priority(thr->d_func()->priority & ~ThreadPriorityResetFlag));
|
||||
{
|
||||
QMutexLocker locker(&thr->d_func()->mutex);
|
||||
|
||||
// do we need to reset the thread priority?
|
||||
if (int(thr->d_func()->priority) & ThreadPriorityResetFlag) {
|
||||
thr->d_func()->setPriority(QThread::Priority(thr->d_func()->priority & ~ThreadPriorityResetFlag));
|
||||
}
|
||||
|
||||
data->threadId.store(to_HANDLE(pthread_self()));
|
||||
set_thread_data(data);
|
||||
|
||||
data->ref();
|
||||
data->quitNow = thr->d_func()->exited;
|
||||
}
|
||||
|
||||
data->threadId.store(to_HANDLE(pthread_self()));
|
||||
set_thread_data(data);
|
||||
|
||||
data->ref();
|
||||
data->quitNow = thr->d_func()->exited;
|
||||
}
|
||||
|
||||
if (data->eventDispatcher.load()) // custom event dispatcher set?
|
||||
data->eventDispatcher.load()->startingUp();
|
||||
else
|
||||
createEventDispatcher(data);
|
||||
if (data->eventDispatcher.load()) // custom event dispatcher set?
|
||||
data->eventDispatcher.load()->startingUp();
|
||||
else
|
||||
createEventDispatcher(data);
|
||||
|
||||
#if (defined(Q_OS_LINUX) || defined(Q_OS_MAC) || defined(Q_OS_QNX))
|
||||
{
|
||||
// sets the name of the current thread.
|
||||
QString objectName = thr->objectName();
|
||||
{
|
||||
// sets the name of the current thread.
|
||||
QString objectName = thr->objectName();
|
||||
|
||||
pthread_t thread_id = from_HANDLE<pthread_t>(data->threadId.load());
|
||||
if (Q_LIKELY(objectName.isEmpty()))
|
||||
setCurrentThreadName(thread_id, thr->metaObject()->className());
|
||||
else
|
||||
setCurrentThreadName(thread_id, objectName.toLocal8Bit());
|
||||
}
|
||||
pthread_t thread_id = from_HANDLE<pthread_t>(data->threadId.load());
|
||||
if (Q_LIKELY(objectName.isEmpty()))
|
||||
setCurrentThreadName(thread_id, thr->metaObject()->className());
|
||||
else
|
||||
setCurrentThreadName(thread_id, objectName.toLocal8Bit());
|
||||
}
|
||||
#endif
|
||||
|
||||
emit thr->started(QThread::QPrivateSignal());
|
||||
emit thr->started(QThread::QPrivateSignal());
|
||||
#if !defined(Q_OS_ANDROID)
|
||||
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
|
||||
pthread_testcancel();
|
||||
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
|
||||
pthread_testcancel();
|
||||
#endif
|
||||
thr->run();
|
||||
thr->run();
|
||||
}
|
||||
#ifndef QT_NO_EXCEPTIONS
|
||||
#ifdef __GLIBCXX__
|
||||
// POSIX thread cancellation under glibc is implemented by throwing an exception
|
||||
// of this type. Do what libstdc++ is doing and handle it specially in order not to
|
||||
// abort the application if user's code calls a cancellation function.
|
||||
catch (const abi::__forced_unwind &) {
|
||||
throw;
|
||||
}
|
||||
#endif // __GLIBCXX__
|
||||
catch (...) {
|
||||
qTerminate();
|
||||
}
|
||||
#endif // QT_NO_EXCEPTIONS
|
||||
|
||||
// This pop runs finish() below. It's outside the try/catch (and has its
|
||||
// own try/catch) to prevent finish() to be run in case an exception is
|
||||
// thrown.
|
||||
pthread_cleanup_pop(1);
|
||||
|
||||
return 0;
|
||||
@ -374,35 +399,53 @@ void *QThreadPrivate::start(void *arg)
|
||||
|
||||
void QThreadPrivate::finish(void *arg)
|
||||
{
|
||||
QThread *thr = reinterpret_cast<QThread *>(arg);
|
||||
QThreadPrivate *d = thr->d_func();
|
||||
#ifndef QT_NO_EXCEPTIONS
|
||||
try
|
||||
#endif
|
||||
{
|
||||
QThread *thr = reinterpret_cast<QThread *>(arg);
|
||||
QThreadPrivate *d = thr->d_func();
|
||||
|
||||
QMutexLocker locker(&d->mutex);
|
||||
QMutexLocker locker(&d->mutex);
|
||||
|
||||
d->isInFinish = true;
|
||||
d->priority = QThread::InheritPriority;
|
||||
void *data = &d->data->tls;
|
||||
locker.unlock();
|
||||
emit thr->finished(QThread::QPrivateSignal());
|
||||
QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
|
||||
QThreadStorageData::finish((void **)data);
|
||||
locker.relock();
|
||||
|
||||
QAbstractEventDispatcher *eventDispatcher = d->data->eventDispatcher.load();
|
||||
if (eventDispatcher) {
|
||||
d->data->eventDispatcher = 0;
|
||||
d->isInFinish = true;
|
||||
d->priority = QThread::InheritPriority;
|
||||
void *data = &d->data->tls;
|
||||
locker.unlock();
|
||||
eventDispatcher->closingDown();
|
||||
delete eventDispatcher;
|
||||
emit thr->finished(QThread::QPrivateSignal());
|
||||
QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
|
||||
QThreadStorageData::finish((void **)data);
|
||||
locker.relock();
|
||||
|
||||
QAbstractEventDispatcher *eventDispatcher = d->data->eventDispatcher.load();
|
||||
if (eventDispatcher) {
|
||||
d->data->eventDispatcher = 0;
|
||||
locker.unlock();
|
||||
eventDispatcher->closingDown();
|
||||
delete eventDispatcher;
|
||||
locker.relock();
|
||||
}
|
||||
|
||||
d->running = false;
|
||||
d->finished = true;
|
||||
d->interruptionRequested = false;
|
||||
|
||||
d->isInFinish = false;
|
||||
d->thread_done.wakeAll();
|
||||
}
|
||||
|
||||
d->running = false;
|
||||
d->finished = true;
|
||||
d->interruptionRequested = false;
|
||||
|
||||
d->isInFinish = false;
|
||||
d->thread_done.wakeAll();
|
||||
#ifndef QT_NO_EXCEPTIONS
|
||||
#ifdef __GLIBCXX__
|
||||
// POSIX thread cancellation under glibc is implemented by throwing an exception
|
||||
// of this type. Do what libstdc++ is doing and handle it specially in order not to
|
||||
// abort the application if user's code calls a cancellation function.
|
||||
catch (const abi::__forced_unwind &) {
|
||||
throw;
|
||||
}
|
||||
#endif // __GLIBCXX__
|
||||
catch (...) {
|
||||
qTerminate();
|
||||
}
|
||||
#endif // QT_NO_EXCEPTIONS
|
||||
}
|
||||
|
||||
|
||||
|
@ -344,7 +344,7 @@ void QThreadPrivate::createEventDispatcher(QThreadData *data)
|
||||
|
||||
#ifndef QT_NO_THREAD
|
||||
|
||||
unsigned int __stdcall QT_ENSURE_STACK_ALIGNED_FOR_SSE QThreadPrivate::start(void *arg)
|
||||
unsigned int __stdcall QT_ENSURE_STACK_ALIGNED_FOR_SSE QThreadPrivate::start(void *arg) Q_DECL_NOEXCEPT
|
||||
{
|
||||
QThread *thr = reinterpret_cast<QThread *>(arg);
|
||||
QThreadData *data = QThreadData::get2(thr);
|
||||
@ -381,7 +381,7 @@ unsigned int __stdcall QT_ENSURE_STACK_ALIGNED_FOR_SSE QThreadPrivate::start(voi
|
||||
return 0;
|
||||
}
|
||||
|
||||
void QThreadPrivate::finish(void *arg, bool lockAnyway)
|
||||
void QThreadPrivate::finish(void *arg, bool lockAnyway) Q_DECL_NOEXCEPT
|
||||
{
|
||||
QThread *thr = reinterpret_cast<QThread *>(arg);
|
||||
QThreadPrivate *d = thr->d_func();
|
||||
|
Loading…
Reference in New Issue
Block a user