summaryrefslogtreecommitdiffstats
path: root/src/corelib/thread
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/thread')
-rw-r--r--src/corelib/thread/qmutex.cpp2
-rw-r--r--src/corelib/thread/qmutex.h8
-rw-r--r--src/corelib/thread/qresultstore.h2
-rw-r--r--src/corelib/thread/qsemaphore.cpp153
-rw-r--r--src/corelib/thread/qsemaphore.h41
-rw-r--r--src/corelib/thread/qthread.cpp78
-rw-r--r--src/corelib/thread/qthread.h108
-rw-r--r--src/corelib/thread/qthread_p.h4
-rw-r--r--src/corelib/thread/qthread_unix.cpp151
-rw-r--r--src/corelib/thread/qthread_win.cpp4
-rw-r--r--src/corelib/thread/qthreadpool.cpp35
-rw-r--r--src/corelib/thread/qthreadpool.h4
-rw-r--r--src/corelib/thread/qthreadpool_p.h12
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