diff options
Diffstat (limited to 'src/corelib/thread')
-rw-r--r-- | src/corelib/thread/qmutex.cpp | 2 | ||||
-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 | 39 | ||||
-rw-r--r-- | src/corelib/thread/qthread.cpp | 47 | ||||
-rw-r--r-- | src/corelib/thread/qthread.h | 124 | ||||
-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 |
12 files changed, 504 insertions, 73 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/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..a92740c8ce 100644 --- a/src/corelib/thread/qsemaphore.h +++ b/src/corelib/thread/qsemaphore.h @@ -69,6 +69,45 @@ private: QSemaphorePrivate *d; }; +class QSemaphoreReleaser +{ + QSemaphore *m_sem = nullptr; + int m_n; +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; + } +}; + #endif // QT_NO_THREAD QT_END_NAMESPACE diff --git a/src/corelib/thread/qthread.cpp b/src/corelib/thread/qthread.cpp index 996a1df9a0..1ec626a53b 100644 --- a/src/corelib/thread/qthread.cpp +++ b/src/corelib/thread/qthread.cpp @@ -291,7 +291,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 +880,51 @@ 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() +*/ + /*! \class QDaemonThread \since 5.5 diff --git a/src/corelib/thread/qthread.h b/src/corelib/thread/qthread.h index 45786537e2..5e6f216219 100644 --- a/src/corelib/thread/qthread.h +++ b/src/corelib/thread/qthread.h @@ -42,6 +42,27 @@ #include <QtCore/qobject.h> +// The implementation of QThread::create uses various C++14/C++17 facilities; +// we must check for their presence. Specifically for glibcxx bundled in MinGW +// with win32 threads, we check the condition found in its <future> header +// since _GLIBCXX_HAS_GTHREADS might then not be defined. +// For std::async (used in all codepaths) +// there is no SG10 feature macro; just test for the header presence. +// For the C++17 codepath do some more throughout checks for std::invoke and +// C++14 lambdas availability. +#if QT_HAS_INCLUDE(<future>) \ + && (!defined(__GLIBCXX__) || (defined(_GLIBCXX_HAS_GTHREADS) && defined(_GLIBCXX_USE_C99_STDINT_TR1))) +# define QTHREAD_HAS_CREATE +# 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 +119,16 @@ public: bool event(QEvent *event) Q_DECL_OVERRIDE; int loopLevel() const; +#ifdef QTHREAD_HAS_CREATE +#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 +#endif + public Q_SLOTS: void start(Priority = InheritPriority); void terminate(); @@ -131,6 +162,99 @@ private: friend class QThreadData; }; +#ifdef QTHREAD_HAS_CREATE +namespace QtPrivate { + +class QThreadCreateThread : public QThread +{ +public: +#if defined(QTHREAD_HAS_VARIADIC_CREATE) + // C++17: std::thread's constructor complying call + template <typename Function, typename... Args> + explicit QThreadCreateThread(Function &&f, Args &&... args) + : m_future(std::async(std::launch::deferred, + [f = static_cast<typename std::decay<Function>::type>(std::forward<Function>(f))](auto &&... largs) mutable -> void + { + (void)std::invoke(std::move(f), std::forward<decltype(largs)>(largs)...); + }, std::forward<Args>(args)...)) + { + } +#elif defined(__cpp_init_captures) && __cpp_init_captures >= 201304 + // C++14: implementation for just one callable + template <typename Function> + explicit QThreadCreateThread(Function &&f) + : m_future(std::async(std::launch::deferred, + [f = static_cast<typename std::decay<Function>::type>(std::forward<Function>(f))]() mutable -> void + { + (void)f(); + })) + { + } +#else +private: + // C++11: same as C++14, but with a workaround for not having generalized lambda captures + 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; + }; + +public: + template <typename Function> + explicit QThreadCreateThread(Function &&f) + : m_future(std::async(std::launch::deferred, Callable<Function>(std::forward<Function>(f)))) + { + } +#endif // QTHREAD_HAS_VARIADIC_CREATE + +private: + void run() override + { + m_future.get(); + } + + std::future<void> m_future; +}; + +} // namespace QtPrivate + +#ifdef QTHREAD_HAS_VARIADIC_CREATE +template <typename Function, typename... Args> +QThread *QThread::create(Function &&f, Args &&... args) +{ + return new QtPrivate::QThreadCreateThread(std::forward<Function>(f), std::forward<Args>(args)...); +} +#else +template <typename Function> +QThread *QThread::create(Function &&f) +{ + return new QtPrivate::QThreadCreateThread(std::forward<Function>(f)); +} +#endif // QTHREAD_HAS_VARIADIC_CREATE + +#endif // QTHREAD_HAS_CREATE + #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 f3ce1f258f..ba46d98cf9 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(0) -{ } +{ + setStackSize(manager->stackSize); +} /* \internal @@ -154,11 +156,6 @@ void QThreadPoolThread::registerThreadInactive() \internal */ QThreadPoolPrivate:: QThreadPoolPrivate() - : isExiting(false), - expiryTimeout(30000), - maxThreadCount(qAbs(QThread::idealThreadCount())), - reservedThreads(0), - activeThreads(0) { } bool QThreadPoolPrivate::tryStart(QRunnable *task) @@ -609,6 +606,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 4a9f9e5cfa..8b6a8cc476 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" @@ -91,11 +92,12 @@ public: QVector<QPair<QRunnable *, int> > 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 |