diff options
Diffstat (limited to 'src/corelib/thread/qthread_unix.cpp')
-rw-r--r-- | src/corelib/thread/qthread_unix.cpp | 215 |
1 files changed, 108 insertions, 107 deletions
diff --git a/src/corelib/thread/qthread_unix.cpp b/src/corelib/thread/qthread_unix.cpp index 8fa4e939f8..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,8 +290,8 @@ 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)); } // threadId is set in QThread::start() @@ -316,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 @@ -359,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(); @@ -373,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(); @@ -394,20 +369,7 @@ void QThreadPrivate::finish(void *arg) 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 + }); } @@ -455,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; @@ -494,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; @@ -509,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) @@ -687,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; } @@ -718,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) { |