summaryrefslogtreecommitdiffstats
path: root/src/corelib/thread/qthread_unix.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/thread/qthread_unix.cpp')
-rw-r--r--src/corelib/thread/qthread_unix.cpp223
1 files changed, 115 insertions, 108 deletions
diff --git a/src/corelib/thread/qthread_unix.cpp b/src/corelib/thread/qthread_unix.cpp
index ad13f10890..4b165eef9c 100644
--- a/src/corelib/thread/qthread_unix.cpp
+++ b/src/corelib/thread/qthread_unix.cpp
@@ -1,42 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2016 Intel Corporation.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// Copyright (C) 2016 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qthread.h"
@@ -44,6 +8,7 @@
#include <private/qcoreapplication_p.h>
#include <private/qcore_unix_p.h>
+#include <private/qtools_p.h>
#if defined(Q_OS_DARWIN)
# include <private/qeventdispatcher_cf_p.h>
@@ -55,7 +20,9 @@
# endif
#endif
-#include <private/qeventdispatcher_unix_p.h>
+#if !defined(Q_OS_WASM)
+# include <private/qeventdispatcher_unix_p.h>
+#endif
#include "qthreadstorage.h"
@@ -70,8 +37,10 @@
#include <sched.h>
#include <errno.h>
-#ifdef Q_OS_BSD4
-#include <sys/sysctl.h>
+#if defined(Q_OS_FREEBSD)
+# include <sys/cpuset.h>
+#elif defined(Q_OS_BSD4)
+# include <sys/sysctl.h>
#endif
#ifdef Q_OS_VXWORKS
# if (_WRS_VXWORKS_MAJOR > 6) || ((_WRS_VXWORKS_MAJOR == 6) && (_WRS_VXWORKS_MINOR >= 6))
@@ -104,6 +73,8 @@
QT_BEGIN_NAMESPACE
+using namespace QtMiscUtils;
+
#if QT_CONFIG(thread)
static_assert(sizeof(pthread_t) <= sizeof(Qt::HANDLE));
@@ -111,10 +82,10 @@ static_assert(sizeof(pthread_t) <= sizeof(Qt::HANDLE));
enum { ThreadPriorityResetFlag = 0x80000000 };
-static thread_local QThreadData *currentThreadData = nullptr;
+Q_CONSTINIT static thread_local QThreadData *currentThreadData = nullptr;
-static pthread_once_t current_thread_data_once = PTHREAD_ONCE_INIT;
-static pthread_key_t current_thread_data_key;
+Q_CONSTINIT static pthread_once_t current_thread_data_once = PTHREAD_ONCE_INIT;
+Q_CONSTINIT static pthread_key_t current_thread_data_key;
static void destroy_current_thread_data(void *p)
{
@@ -171,30 +142,29 @@ static void set_thread_data(QThreadData *data)
static void clear_thread_data()
{
- currentThreadData = nullptr;
- pthread_setspecific(current_thread_data_key, nullptr);
+ set_thread_data(nullptr);
}
template <typename T>
-static typename std::enable_if<QTypeInfo<T>::isIntegral, Qt::HANDLE>::type to_HANDLE(T id)
+static typename std::enable_if<std::is_integral_v<T>, Qt::HANDLE>::type to_HANDLE(T id)
{
return reinterpret_cast<Qt::HANDLE>(static_cast<intptr_t>(id));
}
template <typename T>
-static typename std::enable_if<QTypeInfo<T>::isIntegral, T>::type from_HANDLE(Qt::HANDLE id)
+static typename std::enable_if<std::is_integral_v<T>, T>::type from_HANDLE(Qt::HANDLE id)
{
return static_cast<T>(reinterpret_cast<intptr_t>(id));
}
template <typename T>
-static typename std::enable_if<QTypeInfo<T>::isPointer, Qt::HANDLE>::type to_HANDLE(T id)
+static typename std::enable_if<std::is_pointer_v<T>, Qt::HANDLE>::type to_HANDLE(T id)
{
return id;
}
template <typename T>
-static typename std::enable_if<QTypeInfo<T>::isPointer, T>::type from_HANDLE(Qt::HANDLE id)
+static typename std::enable_if<std::is_pointer_v<T>, T>::type from_HANDLE(Qt::HANDLE id)
{
return static_cast<T>(id);
}
@@ -269,12 +239,12 @@ QAbstractEventDispatcher *QThreadPrivate::createEventDispatcher(QThreadData *dat
#if QT_CONFIG(thread)
-#if (defined(Q_OS_LINUX) || defined(Q_OS_MAC) || defined(Q_OS_QNX))
+#if (defined(Q_OS_LINUX) || defined(Q_OS_DARWIN) || defined(Q_OS_QNX))
static void setCurrentThreadName(const char *name)
{
# if defined(Q_OS_LINUX) && !defined(QT_LINUXBASE)
prctl(PR_SET_NAME, (unsigned long)name, 0, 0, 0);
-# elif defined(Q_OS_MAC)
+# elif defined(Q_OS_DARWIN)
pthread_setname_np(name);
# elif defined(Q_OS_QNX)
pthread_setname_np(pthread_self(), name);
@@ -282,17 +252,37 @@ static void setCurrentThreadName(const char *name)
}
#endif
+namespace {
+template <typename T>
+void terminate_on_exception(T &&t)
+{
+#ifndef QT_NO_EXCEPTIONS
+ try {
+#endif
+ std::forward<T>(t)();
+#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 (abi::__forced_unwind &) {
+ throw;
+#endif // __GLIBCXX__
+ } catch (...) {
+ qTerminate();
+ }
+#endif // QT_NO_EXCEPTIONS
+}
+} // unnamed namespace
+
void *QThreadPrivate::start(void *arg)
{
-#if !defined(Q_OS_ANDROID)
+#ifdef PTHREAD_CANCEL_DISABLE
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, nullptr);
#endif
pthread_cleanup_push(QThreadPrivate::finish, arg);
-#ifndef QT_NO_EXCEPTIONS
- try
-#endif
- {
+ terminate_on_exception([&] {
QThread *thr = reinterpret_cast<QThread *>(arg);
QThreadData *data = QThreadData::get2(thr);
@@ -300,11 +290,13 @@ void *QThreadPrivate::start(void *arg)
QMutexLocker locker(&thr->d_func()->mutex);
// do we need to reset the thread priority?
- if (qToUnderlying(thr->d_func()->priority) & ThreadPriorityResetFlag) {
- thr->d_func()->setPriority(QThread::Priority(qToUnderlying(thr->d_func()->priority) & ~ThreadPriorityResetFlag));
+ if (thr->d_func()->priority & ThreadPriorityResetFlag) {
+ thr->d_func()->setPriority(QThread::Priority(thr->d_func()->priority & ~ThreadPriorityResetFlag));
}
- data->threadId.storeRelaxed(to_HANDLE(pthread_self()));
+ // threadId is set in QThread::start()
+ Q_ASSERT(pthread_equal(from_HANDLE<pthread_t>(data->threadId.loadRelaxed()),
+ pthread_self()));
set_thread_data(data);
data->ref();
@@ -314,38 +306,25 @@ void *QThreadPrivate::start(void *arg)
data->ensureEventDispatcher();
data->eventDispatcher.loadRelaxed()->startingUp();
-#if (defined(Q_OS_LINUX) || defined(Q_OS_MAC) || defined(Q_OS_QNX))
+#if (defined(Q_OS_LINUX) || defined(Q_OS_DARWIN) || defined(Q_OS_QNX))
{
// Sets the name of the current thread. We can only do this
// when the thread is starting, as we don't have a cross
// platform way of setting the name of an arbitrary thread.
- if (Q_LIKELY(thr->objectName().isEmpty()))
+ if (Q_LIKELY(thr->d_func()->objectName.isEmpty()))
setCurrentThreadName(thr->metaObject()->className());
else
- setCurrentThreadName(thr->objectName().toLocal8Bit());
+ setCurrentThreadName(std::exchange(thr->d_func()->objectName, {}).toLocal8Bit());
}
#endif
emit thr->started(QThread::QPrivateSignal());
-#if !defined(Q_OS_ANDROID)
+#ifdef PTHREAD_CANCEL_DISABLE
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, nullptr);
pthread_testcancel();
#endif
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
@@ -357,10 +336,7 @@ void *QThreadPrivate::start(void *arg)
void QThreadPrivate::finish(void *arg)
{
-#ifndef QT_NO_EXCEPTIONS
- try
-#endif
- {
+ terminate_on_exception([&] {
QThread *thr = reinterpret_cast<QThread *>(arg);
QThreadPrivate *d = thr->d_func();
@@ -371,6 +347,7 @@ void QThreadPrivate::finish(void *arg)
void *data = &d->data->tls;
locker.unlock();
emit thr->finished(QThread::QPrivateSignal());
+ qCDebug(lcDeleteLater) << "Sending deferred delete events as part of finishing thread" << thr;
QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete);
QThreadStorageData::finish((void **)data);
locker.relock();
@@ -389,21 +366,10 @@ void QThreadPrivate::finish(void *arg)
d->interruptionRequested = false;
d->isInFinish = false;
+ d->data->threadId.storeRelaxed(nullptr);
+
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
+ });
}
@@ -451,8 +417,31 @@ int QThread::idealThreadCount() noexcept
} else {
cores = (int)psd.psd_proc_cnt;
}
+#elif (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)) || defined(Q_OS_FREEBSD)
+# if defined(Q_OS_FREEBSD) && !defined(CPU_COUNT_S)
+# define CPU_COUNT_S(setsize, cpusetp) ((int)BIT_COUNT(setsize, cpusetp))
+ // match the Linux API for simplicity
+ using cpu_set_t = cpuset_t;
+ auto sched_getaffinity = [](pid_t, size_t cpusetsize, cpu_set_t *mask) {
+ return cpuset_getaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, -1, cpusetsize, mask);
+ };
+# endif
+
+ // get the number of threads we're assigned, not the total in the system
+ QVarLengthArray<cpu_set_t, 1> cpuset(1);
+ int size = 1;
+ if (Q_UNLIKELY(sched_getaffinity(0, sizeof(cpu_set_t), cpuset.data()) < 0)) {
+ for (size = 2; size <= 4; size *= 2) {
+ cpuset.resize(size);
+ if (sched_getaffinity(0, sizeof(cpu_set_t) * size, cpuset.data()) == 0)
+ break;
+ }
+ if (size > 4)
+ return 1;
+ }
+ cores = CPU_COUNT_S(sizeof(cpu_set_t) * size, cpuset.data());
#elif defined(Q_OS_BSD4)
- // FreeBSD, OpenBSD, NetBSD, BSD/OS, OS X, iOS
+ // OpenBSD, NetBSD, BSD/OS, Darwin (macOS, iOS, etc.)
size_t len = sizeof(cores);
int mib[2];
mib[0] = CTL_HW;
@@ -490,7 +479,7 @@ int QThread::idealThreadCount() noexcept
#elif defined(Q_OS_WASM)
cores = QThreadPrivate::idealThreadCount;
#else
- // the rest: Linux, Solaris, AIX, Tru64
+ // the rest: Solaris, AIX, Tru64
cores = (int)sysconf(_SC_NPROCESSORS_ONLN);
if (cores == -1)
return 1;
@@ -505,27 +494,38 @@ void QThread::yieldCurrentThread()
#endif // QT_CONFIG(thread)
-static timespec makeTimespec(time_t secs, long nsecs)
+static void qt_nanosleep(timespec amount)
{
- struct timespec ts;
- ts.tv_sec = secs;
- ts.tv_nsec = nsecs;
- return ts;
+ // We'd like to use clock_nanosleep.
+ //
+ // But clock_nanosleep is from POSIX.1-2001 and both are *not*
+ // affected by clock changes when using relative sleeps, even for
+ // CLOCK_REALTIME.
+ //
+ // nanosleep is POSIX.1-1993
+
+ int r;
+ QT_EINTR_LOOP(r, nanosleep(&amount, &amount));
}
void QThread::sleep(unsigned long secs)
{
- qt_nanosleep(makeTimespec(secs, 0));
+ sleep(std::chrono::seconds{secs});
}
void QThread::msleep(unsigned long msecs)
{
- qt_nanosleep(makeTimespec(msecs / 1000, msecs % 1000 * 1000 * 1000));
+ sleep(std::chrono::milliseconds{msecs});
}
void QThread::usleep(unsigned long usecs)
{
- qt_nanosleep(makeTimespec(usecs / 1000 / 1000, usecs % (1000*1000) * 1000));
+ sleep(std::chrono::microseconds{usecs});
+}
+
+void QThread::sleep(std::chrono::nanoseconds nsec)
+{
+ qt_nanosleep(durationToTimespec(nsec));
}
#if QT_CONFIG(thread)
@@ -683,7 +683,7 @@ void QThread::start(Priority priority)
// could not set scheduling hints, fallback to inheriting them
// we'll try again from inside the thread
pthread_attr_setinheritsched(&attr, PTHREAD_INHERIT_SCHED);
- d->priority = Priority(qToUnderlying(priority) | ThreadPriorityResetFlag);
+ d->priority = qToUnderlying(priority) | ThreadPriorityResetFlag;
}
break;
}
@@ -714,7 +714,12 @@ void QThread::start(Priority priority)
pthread_attr_setthreadname(&attr, metaObject()->className());
else
pthread_attr_setthreadname(&attr, objectName().toLocal8Bit());
+#else
+ // avoid interacting with the binding system
+ d->objectName = d->extraData ? d->extraData->objectName.valueBypassingBindings()
+ : QString();
#endif
+
pthread_t threadId;
int code = pthread_create(&threadId, &attr, QThreadPrivate::start, this);
if (code == EPERM) {
@@ -771,6 +776,8 @@ bool QThread::wait(QDeadlineTimer deadline)
if (!d->thread_done.wait(locker.mutex(), deadline))
return false;
}
+ Q_ASSERT(d->data->threadId.loadRelaxed() == nullptr);
+
return true;
}