/**************************************************************************** ** ** 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$ ** ****************************************************************************/ #include "qthread.h" #include "qplatformdefs.h" #include #include #if defined(Q_OS_DARWIN) # include #else # if !defined(QT_NO_GLIB) # include "../kernel/qeventdispatcher_glib_p.h" # endif #endif #include #include "qthreadstorage.h" #include "qthread_p.h" #include "qdebug.h" #ifdef __GLIBCXX__ #include #endif #include #include #ifdef Q_OS_BSD4 #include #endif #ifdef Q_OS_VXWORKS # if (_WRS_VXWORKS_MAJOR > 6) || ((_WRS_VXWORKS_MAJOR == 6) && (_WRS_VXWORKS_MINOR >= 6)) # include # include # define QT_VXWORKS_HAS_CPUSET # endif #endif #ifdef Q_OS_HPUX #include #endif #if defined(Q_OS_MAC) # ifdef qDebug # define old_qDebug qDebug # undef qDebug # endif # ifdef old_qDebug # undef qDebug # define qDebug QT_NO_QDEBUG_MACRO # undef old_qDebug # endif #endif #if defined(Q_OS_LINUX) && !defined(QT_LINUXBASE) #include #endif #if defined(Q_OS_LINUX) && !defined(SCHED_IDLE) // from linux/sched.h # define SCHED_IDLE 5 #endif #if defined(Q_OS_DARWIN) || !defined(Q_OS_ANDROID) && !defined(Q_OS_OPENBSD) && defined(_POSIX_THREAD_PRIORITY_SCHEDULING) && (_POSIX_THREAD_PRIORITY_SCHEDULING-0 >= 0) #define QT_HAS_THREAD_PRIORITY_SCHEDULING #endif QT_BEGIN_NAMESPACE #ifndef QT_NO_THREAD Q_STATIC_ASSERT(sizeof(pthread_t) <= sizeof(Qt::HANDLE)); enum { ThreadPriorityResetFlag = 0x80000000 }; #if defined(Q_OS_LINUX) && defined(__GLIBC__) && (defined(Q_CC_GNU) || defined(Q_CC_INTEL)) && !defined(QT_LINUXBASE) /* LSB doesn't have __thread, https://lsbbugs.linuxfoundation.org/show_bug.cgi?id=993 */ #define HAVE_TLS #endif #if defined(Q_CC_XLC) || defined (Q_CC_SUN) #define HAVE_TLS #endif #ifdef HAVE_TLS static __thread QThreadData *currentThreadData = 0; #endif static pthread_once_t current_thread_data_once = PTHREAD_ONCE_INIT; static pthread_key_t current_thread_data_key; static void destroy_current_thread_data(void *p) { #if defined(Q_OS_VXWORKS) // Calling setspecific(..., 0) sets the value to 0 for ALL threads. // The 'set to 1' workaround adds a bit of an overhead though, // since this function is called twice now. if (p == (void *)1) return; #endif // POSIX says the value in our key is set to zero before calling // this destructor function, so we need to set it back to the // right value... pthread_setspecific(current_thread_data_key, p); QThreadData *data = static_cast(p); if (data->isAdopted) { QThread *thread = data->thread; Q_ASSERT(thread); QThreadPrivate *thread_p = static_cast(QObjectPrivate::get(thread)); Q_ASSERT(!thread_p->finished); thread_p->finish(thread); } data->deref(); // ... but we must reset it to zero before returning so we aren't // called again (POSIX allows implementations to call destructor // functions repeatedly until all values are zero) pthread_setspecific(current_thread_data_key, #if defined(Q_OS_VXWORKS) (void *)1); #else 0); #endif } static void create_current_thread_data_key() { pthread_key_create(¤t_thread_data_key, destroy_current_thread_data); } static void destroy_current_thread_data_key() { pthread_once(¤t_thread_data_once, create_current_thread_data_key); pthread_key_delete(current_thread_data_key); // Reset current_thread_data_once in case we end up recreating // the thread-data in the rare case of QObject construction // after destroying the QThreadData. pthread_once_t pthread_once_init = PTHREAD_ONCE_INIT; current_thread_data_once = pthread_once_init; } Q_DESTRUCTOR_FUNCTION(destroy_current_thread_data_key) // Utility functions for getting, setting and clearing thread specific data. static QThreadData *get_thread_data() { #ifdef HAVE_TLS return currentThreadData; #else pthread_once(¤t_thread_data_once, create_current_thread_data_key); return reinterpret_cast(pthread_getspecific(current_thread_data_key)); #endif } static void set_thread_data(QThreadData *data) { #ifdef HAVE_TLS currentThreadData = data; #endif pthread_once(¤t_thread_data_once, create_current_thread_data_key); pthread_setspecific(current_thread_data_key, data); } static void clear_thread_data() { #ifdef HAVE_TLS currentThreadData = 0; #endif pthread_setspecific(current_thread_data_key, 0); } template static typename std::enable_if::isIntegral, Qt::HANDLE>::type to_HANDLE(T id) { return reinterpret_cast(static_cast(id)); } template static typename std::enable_if::isIntegral, T>::type from_HANDLE(Qt::HANDLE id) { return static_cast(reinterpret_cast(id)); } template static typename std::enable_if::isPointer, Qt::HANDLE>::type to_HANDLE(T id) { return id; } template static typename std::enable_if::isPointer, T>::type from_HANDLE(Qt::HANDLE id) { return static_cast(id); } void QThreadData::clearCurrentThreadData() { clear_thread_data(); } QThreadData *QThreadData::current(bool createIfNecessary) { QThreadData *data = get_thread_data(); if (!data && createIfNecessary) { data = new QThreadData; QT_TRY { set_thread_data(data); data->thread = new QAdoptedThread(data); } QT_CATCH(...) { clear_thread_data(); data->deref(); data = 0; QT_RETHROW; } data->deref(); data->isAdopted = true; data->threadId.store(to_HANDLE(pthread_self())); if (!QCoreApplicationPrivate::theMainThread) QCoreApplicationPrivate::theMainThread = data->thread.load(); } return data; } void QAdoptedThread::init() { } /* QThreadPrivate */ extern "C" { typedef void*(*QtThreadCallback)(void*); } #endif // QT_NO_THREAD void QThreadPrivate::createEventDispatcher(QThreadData *data) { #if defined(Q_OS_DARWIN) bool ok = false; int value = qEnvironmentVariableIntValue("QT_EVENT_DISPATCHER_CORE_FOUNDATION", &ok); if (ok && value > 0) data->eventDispatcher.storeRelease(new QEventDispatcherCoreFoundation); else data->eventDispatcher.storeRelease(new QEventDispatcherUNIX); #elif !defined(QT_NO_GLIB) if (qEnvironmentVariableIsEmpty("QT_NO_GLIB") && qEnvironmentVariableIsEmpty("QT_NO_THREADED_GLIB") && QEventDispatcherGlib::versionSupported()) data->eventDispatcher.storeRelease(new QEventDispatcherGlib); else data->eventDispatcher.storeRelease(new QEventDispatcherUNIX); #else data->eventDispatcher.storeRelease(new QEventDispatcherUNIX); #endif data->eventDispatcher.load()->startingUp(); } #ifndef QT_NO_THREAD #if (defined(Q_OS_LINUX) || defined(Q_OS_MAC) || defined(Q_OS_QNX)) static void setCurrentThreadName(pthread_t threadId, const char *name) { # if defined(Q_OS_LINUX) && !defined(QT_LINUXBASE) Q_UNUSED(threadId); prctl(PR_SET_NAME, (unsigned long)name, 0, 0, 0); # elif defined(Q_OS_MAC) Q_UNUSED(threadId); pthread_setname_np(name); # elif defined(Q_OS_QNX) pthread_setname_np(threadId, name); # endif } #endif void *QThreadPrivate::start(void *arg) { #if !defined(Q_OS_ANDROID) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); #endif pthread_cleanup_push(QThreadPrivate::finish, arg); #ifndef QT_NO_EXCEPTIONS try #endif { QThread *thr = reinterpret_cast(arg); QThreadData *data = QThreadData::get2(thr); { QMutexLocker locker(&thr->d_func()->mutex); // 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); data->ref(); data->quitNow = thr->d_func()->exited; } if (data->eventDispatcher.load()) // custom event dispatcher set? data->eventDispatcher.load()->startingUp(); else 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(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()); #if !defined(Q_OS_ANDROID) pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); 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 // thrown. pthread_cleanup_pop(1); return 0; } void QThreadPrivate::finish(void *arg) { #ifndef QT_NO_EXCEPTIONS try #endif { QThread *thr = reinterpret_cast(arg); QThreadPrivate *d = thr->d_func(); 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; locker.unlock(); eventDispatcher->closingDown(); delete eventDispatcher; locker.relock(); } d->running = false; d->finished = true; d->interruptionRequested = false; 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 } /************************************************************************** ** QThread *************************************************************************/ Qt::HANDLE QThread::currentThreadId() Q_DECL_NOTHROW { // requires a C cast here otherwise we run into trouble on AIX return to_HANDLE(pthread_self()); } #if defined(QT_LINUXBASE) && !defined(_SC_NPROCESSORS_ONLN) // LSB doesn't define _SC_NPROCESSORS_ONLN. # define _SC_NPROCESSORS_ONLN 84 #endif int QThread::idealThreadCount() Q_DECL_NOTHROW { int cores = 1; #if defined(Q_OS_HPUX) // HP-UX struct pst_dynamic psd; if (pstat_getdynamic(&psd, sizeof(psd), 1, 0) == -1) { perror("pstat_getdynamic"); } else { cores = (int)psd.psd_proc_cnt; } #elif defined(Q_OS_BSD4) // FreeBSD, OpenBSD, NetBSD, BSD/OS, OS X, iOS size_t len = sizeof(cores); int mib[2]; mib[0] = CTL_HW; mib[1] = HW_NCPU; if (sysctl(mib, 2, &cores, &len, NULL, 0) != 0) { perror("sysctl"); } #elif defined(Q_OS_IRIX) // IRIX cores = (int)sysconf(_SC_NPROC_ONLN); #elif defined(Q_OS_INTEGRITY) #if (__INTEGRITY_MAJOR_VERSION >= 10) // Integrity V10+ does support multicore CPUs Value processorCount; if (GetProcessorCount(CurrentTask(), &processorCount) == 0) cores = processorCount; else #endif // as of aug 2008 Integrity only supports one single core CPU cores = 1; #elif defined(Q_OS_VXWORKS) // VxWorks # if defined(QT_VXWORKS_HAS_CPUSET) cpuset_t cpus = vxCpuEnabledGet(); cores = 0; // 128 cores should be enough for everyone ;) for (int i = 0; i < 128 && !CPUSET_ISZERO(cpus); ++i) { if (CPUSET_ISSET(cpus, i)) { CPUSET_CLR(cpus, i); cores++; } } # else // as of aug 2008 VxWorks < 6.6 only supports one single core CPU cores = 1; # endif #else // the rest: Linux, Solaris, AIX, Tru64 cores = (int)sysconf(_SC_NPROCESSORS_ONLN); if (cores == -1) return 1; #endif return cores; } void QThread::yieldCurrentThread() { sched_yield(); } static timespec makeTimespec(time_t secs, long nsecs) { struct timespec ts; ts.tv_sec = secs; ts.tv_nsec = nsecs; return ts; } void QThread::sleep(unsigned long secs) { qt_nanosleep(makeTimespec(secs, 0)); } void QThread::msleep(unsigned long msecs) { qt_nanosleep(makeTimespec(msecs / 1000, msecs % 1000 * 1000 * 1000)); } void QThread::usleep(unsigned long usecs) { qt_nanosleep(makeTimespec(usecs / 1000 / 1000, usecs % (1000*1000) * 1000)); } #ifdef QT_HAS_THREAD_PRIORITY_SCHEDULING // Does some magic and calculate the Unix scheduler priorities // sched_policy is IN/OUT: it must be set to a valid policy before calling this function // sched_priority is OUT only static bool calculateUnixPriority(int priority, int *sched_policy, int *sched_priority) { #ifdef SCHED_IDLE if (priority == QThread::IdlePriority) { *sched_policy = SCHED_IDLE; *sched_priority = 0; return true; } const int lowestPriority = QThread::LowestPriority; #else const int lowestPriority = QThread::IdlePriority; #endif const int highestPriority = QThread::TimeCriticalPriority; int prio_min; int prio_max; #if defined(Q_OS_VXWORKS) && defined(VXWORKS_DKM) // for other scheduling policies than SCHED_RR or SCHED_FIFO prio_min = SCHED_FIFO_LOW_PRI; prio_max = SCHED_FIFO_HIGH_PRI; if ((*sched_policy == SCHED_RR) || (*sched_policy == SCHED_FIFO)) #endif { prio_min = sched_get_priority_min(*sched_policy); prio_max = sched_get_priority_max(*sched_policy); } if (prio_min == -1 || prio_max == -1) return false; int prio; // crudely scale our priority enum values to the prio_min/prio_max prio = ((priority - lowestPriority) * (prio_max - prio_min) / highestPriority) + prio_min; prio = qMax(prio_min, qMin(prio_max, prio)); *sched_priority = prio; return true; } #endif void QThread::start(Priority priority) { Q_D(QThread); QMutexLocker locker(&d->mutex); if (d->isInFinish) d->thread_done.wait(locker.mutex()); if (d->running) return; d->running = true; d->finished = false; d->returnCode = 0; d->exited = false; d->interruptionRequested = false; pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); d->priority = priority; #if defined(QT_HAS_THREAD_PRIORITY_SCHEDULING) switch (priority) { case InheritPriority: { pthread_attr_setinheritsched(&attr, PTHREAD_INHERIT_SCHED); break; } default: { int sched_policy; if (pthread_attr_getschedpolicy(&attr, &sched_policy) != 0) { // failed to get the scheduling policy, don't bother // setting the priority qWarning("QThread::start: Cannot determine default scheduler policy"); break; } int prio; if (!calculateUnixPriority(priority, &sched_policy, &prio)) { // failed to get the scheduling parameters, don't // bother setting the priority qWarning("QThread::start: Cannot determine scheduler priority range"); break; } sched_param sp; sp.sched_priority = prio; if (pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED) != 0 || pthread_attr_setschedpolicy(&attr, sched_policy) != 0 || pthread_attr_setschedparam(&attr, &sp) != 0) { // 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(priority | ThreadPriorityResetFlag); } break; } } #endif // QT_HAS_THREAD_PRIORITY_SCHEDULING if (d->stackSize > 0) { #if defined(_POSIX_THREAD_ATTR_STACKSIZE) && (_POSIX_THREAD_ATTR_STACKSIZE-0 > 0) int code = pthread_attr_setstacksize(&attr, d->stackSize); #else int code = ENOSYS; // stack size not supported, automatically fail #endif // _POSIX_THREAD_ATTR_STACKSIZE if (code) { qWarning("QThread::start: Thread stack size error: %s", qPrintable(qt_error_string(code))); // we failed to set the stacksize, and as the documentation states, // the thread will fail to run... d->running = false; d->finished = false; return; } } pthread_t threadId; int code = pthread_create(&threadId, &attr, QThreadPrivate::start, this); if (code == EPERM) { // caller does not have permission to set the scheduling // parameters/policy #if defined(QT_HAS_THREAD_PRIORITY_SCHEDULING) pthread_attr_setinheritsched(&attr, PTHREAD_INHERIT_SCHED); #endif code = pthread_create(&threadId, &attr, QThreadPrivate::start, this); } d->data->threadId.store(to_HANDLE(threadId)); pthread_attr_destroy(&attr); if (code) { qWarning("QThread::start: Thread creation error: %s", qPrintable(qt_error_string(code))); d->running = false; d->finished = false; d->data->threadId.store(nullptr); } } void QThread::terminate() { #if !defined(Q_OS_ANDROID) Q_D(QThread); QMutexLocker locker(&d->mutex); if (!d->data->threadId.load()) return; int code = pthread_cancel(from_HANDLE(d->data->threadId.load())); if (code) { qWarning("QThread::start: Thread termination error: %s", qPrintable(qt_error_string((code)))); } #endif } bool QThread::wait(unsigned long time) { Q_D(QThread); QMutexLocker locker(&d->mutex); if (from_HANDLE(d->data->threadId.load()) == pthread_self()) { qWarning("QThread::wait: Thread tried to wait on itself"); return false; } if (d->finished || !d->running) return true; while (d->running) { if (!d->thread_done.wait(locker.mutex(), time)) return false; } return true; } void QThread::setTerminationEnabled(bool enabled) { QThread *thr = currentThread(); Q_ASSERT_X(thr != 0, "QThread::setTerminationEnabled()", "Current thread was not started with QThread."); Q_UNUSED(thr) #if defined(Q_OS_ANDROID) Q_UNUSED(enabled); #else pthread_setcancelstate(enabled ? PTHREAD_CANCEL_ENABLE : PTHREAD_CANCEL_DISABLE, NULL); if (enabled) pthread_testcancel(); #endif } // Caller must lock the mutex void QThreadPrivate::setPriority(QThread::Priority threadPriority) { priority = threadPriority; // copied from start() with a few modifications: #ifdef QT_HAS_THREAD_PRIORITY_SCHEDULING int sched_policy; sched_param param; if (pthread_getschedparam(from_HANDLE(data->threadId.load()), &sched_policy, ¶m) != 0) { // failed to get the scheduling policy, don't bother setting // the priority qWarning("QThread::setPriority: Cannot get scheduler parameters"); return; } int prio; if (!calculateUnixPriority(priority, &sched_policy, &prio)) { // failed to get the scheduling parameters, don't // bother setting the priority qWarning("QThread::setPriority: Cannot determine scheduler priority range"); return; } param.sched_priority = prio; int status = pthread_setschedparam(from_HANDLE(data->threadId.load()), sched_policy, ¶m); # ifdef SCHED_IDLE // were we trying to set to idle priority and failed? if (status == -1 && sched_policy == SCHED_IDLE && errno == EINVAL) { // reset to lowest priority possible pthread_getschedparam(from_HANDLE(data->threadId.load()), &sched_policy, ¶m); param.sched_priority = sched_get_priority_min(sched_policy); pthread_setschedparam(from_HANDLE(data->threadId.load()), sched_policy, ¶m); } # else Q_UNUSED(status); # endif // SCHED_IDLE #endif } #endif // QT_NO_THREAD QT_END_NAMESPACE