diff options
Diffstat (limited to 'src/corelib/thread')
-rw-r--r-- | src/corelib/thread/qmutex.cpp | 2 | ||||
-rw-r--r-- | src/corelib/thread/qmutex.h | 8 | ||||
-rw-r--r-- | src/corelib/thread/qresultstore.h | 2 | ||||
-rw-r--r-- | src/corelib/thread/qsemaphore.cpp | 153 | ||||
-rw-r--r-- | src/corelib/thread/qsemaphore.h | 41 | ||||
-rw-r--r-- | src/corelib/thread/qthread.cpp | 78 | ||||
-rw-r--r-- | src/corelib/thread/qthread.h | 108 | ||||
-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 | ||||
-rw-r--r-- | src/corelib/thread/qthreadpool.cpp | 35 | ||||
-rw-r--r-- | src/corelib/thread/qthreadpool.h | 4 | ||||
-rw-r--r-- | src/corelib/thread/qthreadpool_p.h | 12 |
13 files changed, 528 insertions, 74 deletions
diff --git a/src/corelib/thread/qmutex.cpp b/src/corelib/thread/qmutex.cpp index 7e3610f0b3..3e7033451e 100644 --- a/src/corelib/thread/qmutex.cpp +++ b/src/corelib/thread/qmutex.cpp @@ -642,7 +642,7 @@ const int FreeListConstants::Sizes[FreeListConstants::BlockCount] = { 16, 128, 1024, - FreeListConstants::MaxIndex - (16-128-1024) + FreeListConstants::MaxIndex - (16 + 128 + 1024) }; typedef QFreeList<QMutexPrivate, FreeListConstants> FreeList; diff --git a/src/corelib/thread/qmutex.h b/src/corelib/thread/qmutex.h index 12987f16c3..3d24379fa9 100644 --- a/src/corelib/thread/qmutex.h +++ b/src/corelib/thread/qmutex.h @@ -67,6 +67,12 @@ class QMutexData; class Q_CORE_EXPORT QBasicMutex { public: +#ifdef Q_COMPILER_CONSTEXPR + constexpr QBasicMutex() + : d_ptr(nullptr) + {} +#endif + // BasicLockable concept inline void lock() QT_MUTEX_LOCK_NOEXCEPT { if (!fastTryLock()) @@ -251,7 +257,7 @@ class Q_CORE_EXPORT QMutex public: enum RecursionMode { NonRecursive, Recursive }; - inline explicit QMutex(RecursionMode mode = NonRecursive) Q_DECL_NOTHROW { Q_UNUSED(mode); } + inline Q_DECL_CONSTEXPR explicit QMutex(RecursionMode = NonRecursive) Q_DECL_NOTHROW { } inline void lock() Q_DECL_NOTHROW {} inline bool tryLock(int timeout = 0) Q_DECL_NOTHROW { Q_UNUSED(timeout); return true; } diff --git a/src/corelib/thread/qresultstore.h b/src/corelib/thread/qresultstore.h index be9f632557..6c814ef854 100644 --- a/src/corelib/thread/qresultstore.h +++ b/src/corelib/thread/qresultstore.h @@ -196,6 +196,8 @@ public: } // namespace QtPrivate +Q_DECLARE_TYPEINFO(QtPrivate::ResultItem, Q_PRIMITIVE_TYPE); + #endif //Q_QDOC QT_END_NAMESPACE diff --git a/src/corelib/thread/qsemaphore.cpp b/src/corelib/thread/qsemaphore.cpp index ce0c1c91df..96c031eec6 100644 --- a/src/corelib/thread/qsemaphore.cpp +++ b/src/corelib/thread/qsemaphore.cpp @@ -94,7 +94,7 @@ QT_BEGIN_NAMESPACE seated (taking the available seats to 5, making the party of 10 people wait longer). - \sa QMutex, QWaitCondition, QThread, {Semaphores Example} + \sa QSemaphoreReleaser, QMutex, QWaitCondition, QThread, {Semaphores Example} */ class QSemaphorePrivate { @@ -152,7 +152,10 @@ void QSemaphore::acquire(int n) \snippet code/src_corelib_thread_qsemaphore.cpp 1 - \sa acquire(), available() + QSemaphoreReleaser is a \l{http://en.cppreference.com/w/cpp/language/raii}{RAII} + wrapper around this function. + + \sa acquire(), available(), QSemaphoreReleaser */ void QSemaphore::release(int n) { @@ -233,6 +236,152 @@ bool QSemaphore::tryAcquire(int n, int timeout) } +/*! + \class QSemaphoreReleaser + \brief The QSemaphoreReleaser class provides exception-safe deferral of a QSemaphore::release() call + \since 5.10 + \ingroup thread + \inmodule QtCore + + \reentrant + + QSemaphoreReleaser can be used wherever you would otherwise use + QSemaphore::release(). Constructing a QSemaphoreReleaser defers the + release() call on the semaphore until the QSemaphoreReleaser is + destroyed (see + \l{http://en.cppreference.com/w/cpp/language/raii}{RAII pattern}). + + You can use this to reliably release a semaphore to avoid dead-lock + in the face of exceptions or early returns: + + \code + // ... do something that may throw or return early + sem.release(); + \endcode + + If an early return is taken or an exception is thrown before the + \c{sem.release()} call is reached, the semaphore is not released, + possibly preventing the thread waiting in the corresponding + \c{sem.acquire()} call from ever continuing execution. + + When using RAII instead: + + \code + const QSemaphoreReleaser releaser(sem); + // ... do something that may throw or early return + // implicitly calls sem.release() here and at every other return in between + \endcode + + this can no longer happen, because the compiler will make sure that + the QSemaphoreReleaser destructor is always called, and therefore + the semaphore is always released. + + QSemaphoreReleaser is move-enabled and can therefore be returned + from functions to transfer responsibility for releasing a semaphore + out of a function or a scope: + + \code + { // some scope + QSemaphoreReleaser releaser; // does nothing + // ... + if (someCondition) { + releaser = QSemaphoreReleaser(sem); + // ... + } + // ... + } // conditionally calls sem.release(), depending on someCondition + \endcode + + A QSemaphoreReleaser can be canceled by a call to cancel(). A canceled + semaphore releaser will no longer call QSemaphore::release() in its + destructor. + + \sa QMutexLocker +*/ + +/*! + \fn QSemaphoreReleaser::QSemaphoreReleaser() + + Default constructor. Creates a QSemaphoreReleaser that does nothing. +*/ + +/*! + \fn QSemaphoreReleaser::QSemaphoreReleaser(QSemaphore &sem, int n) + + Constructor. Stores the arguments and calls \a{sem}.release(\a{n}) + in the destructor. +*/ + +/*! + \fn QSemaphoreReleaser::QSemaphoreReleaser(QSemaphore *sem, int n) + + Constructor. Stores the arguments and calls \a{sem}->release(\a{n}) + in the destructor. +*/ + +/*! + \fn QSemaphoreReleaser::QSemaphoreReleaser(QSemaphoreReleaser &&other) + + Move constructor. Takes over responsibility to call QSemaphore::release() + from \a other, which in turn is canceled. + + \sa cancel() +*/ + +/*! + \fn QSemaphoreReleaser::operator=(QSemaphoreReleaser &&other) + + Move assignment operator. Takes over responsibility to call QSemaphore::release() + from \a other, which in turn is canceled. + + If this semaphore releaser had the responsibility to call some QSemaphore::release() + itself, it performs the call before taking over from \a other. + + \sa cancel() +*/ + +/*! + \fn QSemaphoreReleaser::~QSemaphoreReleaser() + + Unless canceled, calls QSemaphore::release() with the arguments provided + to the constructor, or by the last move assignment. +*/ + +/*! + \fn QSemaphoreReleaser::swap(QSemaphoreReleaser &other) + + Exchanges the responsibilites of \c{*this} and \a other. + + Unlike move assignment, neither of the two objects ever releases its + semaphore, if any, as a consequence of swapping. + + Therefore this function is very fast and never fails. +*/ + +/*! + \fn QSemaphoreReleaser::semaphore() const + + Returns a pointer to the QSemaphore object provided to the constructor, + or by the last move assignment, if any. Otherwise, returns \c nullptr. +*/ + +/*! + \fn QSemaphoreReleaser::cancel() + + Cancels this QSemaphoreReleaser such that the destructor will no longer + call \c{semaphore()->release()}. Returns the value of semaphore() + before this call. After this call, semaphore() will return \c nullptr. + + To enable again, assign a new QSemaphoreReleaser: + + \code + releaser.cancel(); // avoid releasing old semaphore() + releaser = QSemaphoreReleaser(sem, 42); + // now will call sem.release(42) when 'releaser' is destroyed + \endcode +*/ + + QT_END_NAMESPACE #endif // QT_NO_THREAD diff --git a/src/corelib/thread/qsemaphore.h b/src/corelib/thread/qsemaphore.h index adb9d73e50..9de23173e8 100644 --- a/src/corelib/thread/qsemaphore.h +++ b/src/corelib/thread/qsemaphore.h @@ -69,6 +69,47 @@ private: QSemaphorePrivate *d; }; +class QSemaphoreReleaser +{ +public: + QSemaphoreReleaser() = default; + explicit QSemaphoreReleaser(QSemaphore &sem, int n = 1) Q_DECL_NOTHROW + : m_sem(&sem), m_n(n) {} + explicit QSemaphoreReleaser(QSemaphore *sem, int n = 1) Q_DECL_NOTHROW + : m_sem(sem), m_n(n) {} + QSemaphoreReleaser(QSemaphoreReleaser &&other) Q_DECL_NOTHROW + : m_sem(other.m_sem), m_n(other.m_n) + { other.m_sem = nullptr; } + QSemaphoreReleaser &operator=(QSemaphoreReleaser &&other) Q_DECL_NOTHROW + { QSemaphoreReleaser moved(std::move(other)); swap(moved); return *this; } + + ~QSemaphoreReleaser() + { + if (m_sem) + m_sem->release(m_n); + } + + void swap(QSemaphoreReleaser &other) Q_DECL_NOTHROW + { + qSwap(m_sem, other.m_sem); + qSwap(m_n, other.m_n); + } + + QSemaphore *semaphore() const Q_DECL_NOTHROW + { return m_sem; } + + QSemaphore *cancel() Q_DECL_NOTHROW + { + QSemaphore *old = m_sem; + m_sem = nullptr; + return old; + } + +private: + QSemaphore *m_sem = nullptr; + int m_n; +}; + #endif // QT_NO_THREAD QT_END_NAMESPACE diff --git a/src/corelib/thread/qthread.cpp b/src/corelib/thread/qthread.cpp index 996a1df9a0..cad7a14017 100644 --- a/src/corelib/thread/qthread.cpp +++ b/src/corelib/thread/qthread.cpp @@ -78,6 +78,13 @@ QThreadData::~QThreadData() QThreadData::clearCurrentThreadData(); } + // ~QThread() sets thread to nullptr, so if it isn't null here, it's + // because we're being run before the main object itself. This can only + // happen for QAdoptedThread. Note that both ~QThreadPrivate() and + // ~QObjectPrivate() will deref this object again, but that is acceptable + // because this destructor is still running (the _ref sub-object has not + // been destroyed) and there's no reentrancy. The refcount will become + // negative, but that's acceptable. QThread *t = thread; thread = 0; delete t; @@ -291,7 +298,7 @@ QThreadPrivate::~QThreadPrivate() \fn int QThread::idealThreadCount() Returns the ideal number of threads that can be run on the system. This is done querying - the number of processor cores, both real and logical, in the system. This function returns -1 + the number of processor cores, both real and logical, in the system. This function returns 1 if the number of processor cores could not be detected. */ @@ -880,6 +887,75 @@ bool QThread::isInterruptionRequested() const return d->interruptionRequested; } +/* + \fn template <typename Function, typename Args...> static QThread *QThread::create(Function &&f, Args &&... args) + \since 5.10 + + Creates a new QThread object that will execute the function \a f with the + arguments \a args. + + The new thread is not started -- it must be started by an explicit call + to start(). This allows you to connect to its signals, move QObjects + to the thread, choose the new thread's priority and so on. The function + \a f will be called in the new thread. + + Returns the newly created QThread instance. + + \note the caller acquires ownership of the returned QThread instance. + + \note this function is only available when using C++17. + + \warning do not call start() on the returned QThread instance more than once; + doing so will result in undefined behavior. + + \sa start() +*/ + +/* + \fn template <typename Function> static QThread *QThread::create(Function &&f) + \since 5.10 + + Creates a new QThread object that will execute the function \a f. + + The new thread is not started -- it must be started by an explicit call + to start(). This allows you to connect to its signals, move QObjects + to the thread, choose the new thread's priority and so on. The function + \a f will be called in the new thread. + + Returns the newly created QThread instance. + + \note the caller acquires ownership of the returned QThread instance. + + \warning do not call start() on the returned QThread instance more than once; + doing so will result in undefined behavior. + + \sa start() +*/ + +#if QT_CONFIG(cxx11_future) +class QThreadCreateThread : public QThread +{ +public: + explicit QThreadCreateThread(std::future<void> &&future) + : m_future(std::move(future)) + { + } + +private: + void run() override + { + m_future.get(); + } + + std::future<void> m_future; +}; + +QThread *QThread::createThreadImpl(std::future<void> &&future) +{ + return new QThreadCreateThread(std::move(future)); +} +#endif // QT_CONFIG(cxx11_future) + /*! \class QDaemonThread \since 5.5 diff --git a/src/corelib/thread/qthread.h b/src/corelib/thread/qthread.h index 45786537e2..03b5424bb6 100644 --- a/src/corelib/thread/qthread.h +++ b/src/corelib/thread/qthread.h @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Giuseppe D'Angelo <giuseppe.dangelo@kdab.com> ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtCore module of the Qt Toolkit. @@ -42,6 +43,20 @@ #include <QtCore/qobject.h> +// For QThread::create. The configure-time test just checks for the availability +// of std::future and std::async; for the C++17 codepath we perform some extra +// checks here (for std::invoke and C++14 lambdas). +#if QT_CONFIG(cxx11_future) +# include <future> // for std::async +# include <functional> // for std::invoke; no guard needed as it's a C++98 header + +# if defined(__cpp_lib_invoke) && __cpp_lib_invoke >= 201411 \ + && defined(__cpp_init_captures) && __cpp_init_captures >= 201304 \ + && defined(__cpp_generic_lambdas) && __cpp_generic_lambdas >= 201304 +# define QTHREAD_HAS_VARIADIC_CREATE +# endif +#endif + #include <limits.h> QT_BEGIN_NAMESPACE @@ -98,6 +113,23 @@ public: bool event(QEvent *event) Q_DECL_OVERRIDE; int loopLevel() const; +#ifdef Q_QDOC + template <typename Function, typename... Args> + static QThread *create(Function &&f, Args &&... args); + template <typename Function> + static QThread *create(Function &&f); +#else +# if QT_CONFIG(cxx11_future) +# ifdef QTHREAD_HAS_VARIADIC_CREATE + template <typename Function, typename... Args> + static QThread *create(Function &&f, Args &&... args); +# else + template <typename Function> + static QThread *create(Function &&f); +# endif // QTHREAD_HAS_VARIADIC_CREATE +# endif // QT_CONFIG(cxx11_future) +#endif // Q_QDOC + public Q_SLOTS: void start(Priority = InheritPriority); void terminate(); @@ -127,10 +159,86 @@ protected: private: Q_DECLARE_PRIVATE(QThread) +#if QT_CONFIG(cxx11_future) + static QThread *createThreadImpl(std::future<void> &&future); +#endif + friend class QCoreApplication; friend class QThreadData; }; +#if QT_CONFIG(cxx11_future) + +#if defined(QTHREAD_HAS_VARIADIC_CREATE) +// C++17: std::thread's constructor complying call +template <typename Function, typename... Args> +QThread *QThread::create(Function &&f, Args &&... args) +{ + using DecayedFunction = typename std::decay<Function>::type; + auto threadFunction = + [f = static_cast<DecayedFunction>(std::forward<Function>(f))](auto &&... largs) mutable -> void + { + (void)std::invoke(std::move(f), std::forward<decltype(largs)>(largs)...); + }; + + return createThreadImpl(std::async(std::launch::deferred, + std::move(threadFunction), + std::forward<Args>(args)...)); +} +#elif defined(__cpp_init_captures) && __cpp_init_captures >= 201304 +// C++14: implementation for just one callable +template <typename Function> +QThread *QThread::create(Function &&f) +{ + using DecayedFunction = typename std::decay<Function>::type; + auto threadFunction = + [f = static_cast<DecayedFunction>(std::forward<Function>(f))]() mutable -> void + { + (void)f(); + }; + + return createThreadImpl(std::async(std::launch::deferred, std::move(threadFunction))); +} +#else +// C++11: same as C++14, but with a workaround for not having generalized lambda captures +namespace QtPrivate { +template <typename Function> +struct Callable +{ + explicit Callable(Function &&f) + : m_function(std::forward<Function>(f)) + { + } + +#if defined(Q_COMPILER_DEFAULT_MEMBERS) && defined(Q_COMPILER_DELETE_MEMBERS) + // Apply the same semantics of a lambda closure type w.r.t. the special + // member functions, if possible: delete the copy assignment operator, + // bring back all the others as per the RO5 (cf. §8.1.5.1/11 [expr.prim.lambda.closure]) + ~Callable() = default; + Callable(const Callable &) = default; + Callable(Callable &&) = default; + Callable &operator=(const Callable &) = delete; + Callable &operator=(Callable &&) = default; +#endif + + void operator()() + { + (void)m_function(); + } + + typename std::decay<Function>::type m_function; +}; +} // namespace QtPrivate + +template <typename Function> +QThread *QThread::create(Function &&f) +{ + return createThreadImpl(std::async(std::launch::deferred, QtPrivate::Callable<Function>(std::forward<Function>(f)))); +} +#endif // QTHREAD_HAS_VARIADIC_CREATE + +#endif // QT_CONFIG(cxx11_future) + #else // QT_NO_THREAD class Q_CORE_EXPORT QThread : public QObject 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 69a11b2b62..6540f02f34 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(); diff --git a/src/corelib/thread/qthreadpool.cpp b/src/corelib/thread/qthreadpool.cpp index ccd8194b35..8eca3b56e4 100644 --- a/src/corelib/thread/qthreadpool.cpp +++ b/src/corelib/thread/qthreadpool.cpp @@ -74,7 +74,9 @@ public: */ QThreadPoolThread::QThreadPoolThread(QThreadPoolPrivate *manager) :manager(manager), runnable(nullptr) -{ } +{ + setStackSize(manager->stackSize); +} /* \internal @@ -165,11 +167,6 @@ void QThreadPoolThread::registerThreadInactive() \internal */ QThreadPoolPrivate:: QThreadPoolPrivate() - : isExiting(false), - expiryTimeout(30000), - maxThreadCount(qAbs(QThread::idealThreadCount())), - reservedThreads(0), - activeThreads(0) { } bool QThreadPoolPrivate::tryStart(QRunnable *task) @@ -636,6 +633,32 @@ void QThreadPool::reserveThread() ++d->reservedThreads; } +/*! \property QThreadPool::stackSize + + This property contains the stack size for the thread pool worker + threads. + + The value of the property is only used when the thread pool creates + new threads. Changing it has no effect for already created + or running threads. + + The default value is 0, which makes QThread use the operating + system default stack size. + + \since 5.10 +*/ +void QThreadPool::setStackSize(uint stackSize) +{ + Q_D(QThreadPool); + d->stackSize = stackSize; +} + +uint QThreadPool::stackSize() const +{ + Q_D(const QThreadPool); + return d->stackSize; +} + /*! Releases a thread previously reserved by a call to reserveThread(). diff --git a/src/corelib/thread/qthreadpool.h b/src/corelib/thread/qthreadpool.h index 09b7f96f48..a65eacc996 100644 --- a/src/corelib/thread/qthreadpool.h +++ b/src/corelib/thread/qthreadpool.h @@ -58,6 +58,7 @@ class Q_CORE_EXPORT QThreadPool : public QObject Q_PROPERTY(int expiryTimeout READ expiryTimeout WRITE setExpiryTimeout) Q_PROPERTY(int maxThreadCount READ maxThreadCount WRITE setMaxThreadCount) Q_PROPERTY(int activeThreadCount READ activeThreadCount) + Q_PROPERTY(uint stackSize READ stackSize WRITE setStackSize) friend class QFutureInterfaceBase; public: @@ -77,6 +78,9 @@ public: int activeThreadCount() const; + void setStackSize(uint stackSize); + uint stackSize() const; + void reserveThread(); void releaseThread(); diff --git a/src/corelib/thread/qthreadpool_p.h b/src/corelib/thread/qthreadpool_p.h index 18b89bbba9..d03ba9d77f 100644 --- a/src/corelib/thread/qthreadpool_p.h +++ b/src/corelib/thread/qthreadpool_p.h @@ -53,6 +53,7 @@ // #include "QtCore/qmutex.h" +#include "QtCore/qthread.h" #include "QtCore/qwaitcondition.h" #include "QtCore/qset.h" #include "QtCore/qqueue.h" @@ -173,11 +174,12 @@ public: QVector<QueuePage*> queue; QWaitCondition noActiveThreads; - bool isExiting; - int expiryTimeout; - int maxThreadCount; - int reservedThreads; - int activeThreads; + int expiryTimeout = 30000; + int maxThreadCount = QThread::idealThreadCount(); + int reservedThreads = 0; + int activeThreads = 0; + uint stackSize = 0; + bool isExiting = false; }; QT_END_NAMESPACE |