diff options
author | Giuseppe D'Angelo <giuseppe.dangelo@kdab.com> | 2016-08-07 20:58:54 +0200 |
---|---|---|
committer | Giuseppe D'Angelo <giuseppe.dangelo@kdab.com> | 2017-04-26 06:25:44 +0000 |
commit | 5677b70eee2e923eea8e5150500ac745d8d54974 (patch) | |
tree | 84434baeb97988c28b4d26742b8112eccc9e38d8 /src/corelib/thread | |
parent | 164392548e3e1c7526d4eef4896748ef5162cf2d (diff) |
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>
Diffstat (limited to 'src/corelib/thread')
-rw-r--r-- | src/corelib/thread/qthread_p.h | 4 | ||||
-rw-r--r-- | src/corelib/thread/qthread_unix.cpp | 151 | ||||
-rw-r--r-- | src/corelib/thread/qthread_win.cpp | 4 |
3 files changed, 101 insertions, 58 deletions
diff --git a/src/corelib/thread/qthread_p.h b/src/corelib/thread/qthread_p.h index 885b4c0c1e..f3d4750177 100644 --- a/src/corelib/thread/qthread_p.h +++ b/src/corelib/thread/qthread_p.h @@ -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; diff --git a/src/corelib/thread/qthread_unix.cpp b/src/corelib/thread/qthread_unix.cpp index f359d25a73..cddc4053b1 100644 --- a/src/corelib/thread/qthread_unix.cpp +++ b/src/corelib/thread/qthread_unix.cpp @@ -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)); - } - - data->threadId.store(to_HANDLE(pthread_self())); - set_thread_data(data); + { + QMutexLocker locker(&thr->d_func()->mutex); - data->ref(); - data->quitNow = thr->d_func()->exited; - } + // 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)); + } - if (data->eventDispatcher.load()) // custom event dispatcher set? - data->eventDispatcher.load()->startingUp(); - else - createEventDispatcher(data); + data->threadId.store(to_HANDLE(pthread_self())); + set_thread_data(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(); + data->ref(); + data->quitNow = thr->d_func()->exited; + } - pthread_t thread_id = from_HANDLE<pthread_t>(data->threadId.load()); - if (Q_LIKELY(objectName.isEmpty())) - setCurrentThreadName(thread_id, thr->metaObject()->className()); + if (data->eventDispatcher.load()) // custom event dispatcher set? + data->eventDispatcher.load()->startingUp(); else - setCurrentThreadName(thread_id, objectName.toLocal8Bit()); - } + 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(); + + 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(); - } - d->running = false; - d->finished = true; - d->interruptionRequested = false; + 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->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 } diff --git a/src/corelib/thread/qthread_win.cpp b/src/corelib/thread/qthread_win.cpp index ef7bfc511d..24d3ca2d7d 100644 --- a/src/corelib/thread/qthread_win.cpp +++ b/src/corelib/thread/qthread_win.cpp @@ -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(); |