Revision control
++ // 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
++ // Qt-Security score:significant reason:default
++
++ #ifndef QTHREAD_P_H
++ #define QTHREAD_P_H
++
++ //
++ // W A R N I N G
++ // -------------
++ //
++ // This file is not part of the Qt API. It exists purely as an
++ // implementation detail. This header file may change from version to
++ // version without notice, or even be removed.
++ //
++ // We mean it.
++ //
++ //
++
++ #include "QtCore/qthread.h"
++
++ #include "QtCore/qcoreapplication.h"
++ #include "private/qobject_p.h"
++ #include "QtCore/qmap.h"
++ #include "QtCore/qmutex.h"
++ #include "QtCore/qstack.h"
++
++ #if QT_CONFIG(thread)
++ #include "private/qthreadstorage_p.h"
++ #include "QtCore/qwaitcondition.h"
++ #endif
++
++ #include <atomic>
++
++ QT_BEGIN_NAMESPACE
++
++ class QAbstractEventDispatcher;
++ class QEventLoop;
++
++ class QPostEvent
++ {
++ public:
++ QObject *receiver;
++ QEvent *event;
++ int priority;
++ inline QPostEvent()
++ : receiver(nullptr), event(nullptr), priority(0)
++ { }
++ inline QPostEvent(QObject *r, QEvent *e, int p)
++ : receiver(r), event(e), priority(p)
++ { }
++ };
++ Q_DECLARE_TYPEINFO(QPostEvent, Q_RELOCATABLE_TYPE);
++
++ inline bool operator<(const QPostEvent &first, const QPostEvent &second)
++ {
++ return first.priority > second.priority;
++ }
++
++ // This class holds the list of posted events.
++ // The list has to be kept sorted by priority
++ // ### Qt7 remove the next line
++ // It's used in a virtual in QCoreApplication, so ELFVERSION:ignore-next
++ class QPostEventList : public QList<QPostEvent>
++ {
++ public:
++ // recursion == recursion count for sendPostedEvents()
++ int recursion;
++
++ // sendOffset == the current event to start sending
++ qsizetype startOffset;
++ // insertionOffset == set by sendPostedEvents to tell postEvent() where to start insertions
++ qsizetype insertionOffset;
++
++ QMutex mutex;
++
++ inline QPostEventList() : QList<QPostEvent>(), recursion(0), startOffset(0), insertionOffset(0) { }
++
++ void addEvent(const QPostEvent &ev);
++
++ private:
++ //hides because they do not keep that list sorted. addEvent must be used
++ using QList<QPostEvent>::append;
++ using QList<QPostEvent>::insert;
++ };
++
++ namespace QtPrivate {
++
++ /* BindingStatusOrList is basically a QBiPointer (as found in declarative)
++ with some helper methods to manipulate the list. BindingStatusOrList starts
++ its life in a null state and supports the following transitions
++
++ 0 state (initial)
++ / \
++ / \
++ v v
++ pending object list----------->binding status
++ Note that binding status is the final state, and we never transition away
++ from it
++ */
++ class BindingStatusOrList
++ {
++ Q_DISABLE_COPY_MOVE(BindingStatusOrList)
++ public:
++ using List = std::vector<QObject *>;
++
++ constexpr BindingStatusOrList() noexcept : data(0) {}
++ explicit BindingStatusOrList(QBindingStatus *status) noexcept :
++ data(encodeBindingStatus(status)) {}
++ explicit BindingStatusOrList(List *list) noexcept : data(encodeList(list)) {}
++ ~BindingStatusOrList()
++ {
++ auto status = bindingStatus();
++ delete status;
++ }
++
++ // requires external synchronization:
++ QBindingStatus *addObjectUnlessAlreadyStatus(QObject *object);
++ void removeObject(QObject *object);
++ void setStatusAndClearList(QBindingStatus *status) noexcept;
++
++
++ static bool isBindingStatus(quintptr data) noexcept
++ {
++ return !isNull(data) && !isList(data);
++ }
++ static bool isList(quintptr data) noexcept { return data & 1; }
++ static bool isNull(quintptr data) noexcept { return data == 0; }
++
++ // thread-safe:
++ QBindingStatus *bindingStatus() const noexcept
++ {
++ // synchronizes-with the store-release in setStatusAndClearList():
++ const auto d = data.load(std::memory_order_acquire);
++ if (isBindingStatus(d))
++ return reinterpret_cast<QBindingStatus *>(d);
++ else
++ return nullptr;
++ }
++
++ // requires external synchronization:
++ List *list() const noexcept
++ {
++ return decodeList(data.load(std::memory_order_relaxed));
++ }
++
++ private:
++ static List *decodeList(quintptr ptr) noexcept
++ {
++ if (isList(ptr))
++ return reinterpret_cast<List *>(ptr & ~1);
++ else
++ return nullptr;
++ }
++
++ static quintptr encodeBindingStatus(QBindingStatus *status) noexcept
++ {
++ return quintptr(status);
++ }
++
++ static quintptr encodeList(List *list) noexcept
++ {
++ return quintptr(list) | 1;
++ }
++
++ std::atomic<quintptr> data;
++ };
++
++ } // namespace QtPrivate
++
++ #if QT_CONFIG(thread)
++
++ class Q_CORE_EXPORT QDaemonThread : public QThread
++ {
++ public:
++ QDaemonThread(QObject *parent = nullptr);
++ ~QDaemonThread();
++ };
++
++ class Q_AUTOTEST_EXPORT QThreadPrivate : public QObjectPrivate
++ {
++ Q_DECLARE_PUBLIC(QThread)
++
++ public:
++ QThreadPrivate(QThreadData *d = nullptr);
++ ~QThreadPrivate();
++
++ void setPriority(QThread::Priority prio);
++ Qt::HANDLE threadId() const noexcept;
++
++ mutable QMutex mutex;
++ QAtomicInt quitLockRef;
++
++ enum State : quint8 {
++ // All state changes are imprecise
++ NotStarted = 0, // before start() or if failed to start
++ Running = 1, // in run()
++ Finishing = 2, // in QThreadPrivate::finish()
++ Finished = 3, // QThreadPrivate::finish() or cleanup() is done
++ // or, if using pthread_join, joining is done
++ };
++
++ State threadState = NotStarted;
++ bool exited = false;
++ std::atomic<bool> interruptionRequested = false;
++ #ifdef Q_OS_UNIX
++ bool terminated = false; // when (the first) terminate has been called
++ #endif
++
++ int waiters = 0;
++ int returnCode = -1;
++
++ uint stackSize = 0;
++ std::underlying_type_t<QThread::Priority> priority = QThread::InheritPriority;
++
++ bool wait(QMutexLocker<QMutex> &locker, QDeadlineTimer deadline);
++
++ QThread::QualityOfService serviceLevel = QThread::QualityOfService::Auto;
++ void setQualityOfServiceLevel(QThread::QualityOfService qosLevel);
++ #ifdef Q_OS_DARWIN
++ qos_class_t nativeQualityOfServiceClass() const;
++ #endif
++
++ #ifdef Q_OS_UNIX
++ QWaitCondition thread_done;
++
++ void wakeAll();
++ static void *start(void *arg);
++ void finish(bool = false); // happens early (before thread-local dtors)
++ void cleanup(); // happens late (as a thread-local dtor, if possible)
++ #endif // Q_OS_UNIX
++
++ #ifdef Q_OS_WIN
++ static unsigned int __stdcall start(void *) noexcept;
++ void finish(bool lockAnyway = true) noexcept;
++
++ Qt::HANDLE handle = nullptr;
++ bool terminationEnabled = false; // enabled after start()
++ bool terminatePending = false;
++ #endif // Q_OS_WIN
++ #ifdef Q_OS_WASM
++ static int idealThreadCount;
++ #endif
++ QThreadData *data;
++
++ static QAbstractEventDispatcher *createEventDispatcher(QThreadData *data);
++
++ void ref()
++ {
++ quitLockRef.ref();
++ }
++
++ void deref()
++ {
++ if (!quitLockRef.deref() && threadState == Running) {
++ QCoreApplication::instance()->postEvent(q_ptr, new QEvent(QEvent::Quit));
++ }
++ }
++
++ QBindingStatus *bindingStatus();
++
++ /* Returns nullptr if the object has been added, or the binding status
++ if that one has been set in the meantime
++ */
++ QBindingStatus *addObjectWithPendingBindingStatusChange(QObject *obj);
++ void removeObjectWithPendingBindingStatusChange(QObject *obj);
++
++ private:
++ #ifdef Q_OS_INTEGRITY
++ // On INTEGRITY we set the thread name before starting it, so just fake a string
++ struct FakeString {
++ bool isEmpty() const { return true; }
++ const char *toLocal8Bit() const { return nullptr; }
++ } objectName;
++ #else
++ // Used in QThread(Private)::start to avoid racy access to QObject::objectName,
++ // unset afterwards.
++ QString objectName;
++ #endif
++ };
++
++ #else // QT_CONFIG(thread)
++
++ class QThreadPrivate : public QObjectPrivate
++ {
++ public:
++ QThreadPrivate(QThreadData *d = nullptr);
++ ~QThreadPrivate();
++
++ mutable QMutex mutex;
++ QThreadData *data;
++ QBindingStatus* m_bindingStatus;
++ bool running = false;
++
++ QBindingStatus* bindingStatus() { return m_bindingStatus; }
++ QBindingStatus *addObjectWithPendingBindingStatusChange(QObject *) { return nullptr; }
++ void removeObjectWithPendingBindingStatusChange(QObject *) {}
++
++ static void setCurrentThread(QThread *) { }
++ static QAbstractEventDispatcher *createEventDispatcher(QThreadData *data);
++
++ void ref() {}
++ void deref() {}
++
++ Q_DECLARE_PUBLIC(QThread)
++ };
++
++ #endif // QT_CONFIG(thread)
++
++ class QThreadData
++ {
++ public:
++ QThreadData(int initialRefCount = 1)
++ : _ref(initialRefCount)
++ {
++ // fprintf(stderr, "QThreadData %p created\n", this);
++ }
++ ~QThreadData();
++
++ static QThreadData *current()
++ {
++ if (QThreadData *data = currentThreadData()) Q_LIKELY_BRANCH
++ return data;
++ return createCurrentThreadData();
++ }
++ // non-creating version of current():
++ Q_DECL_PURE_FUNCTION static Q_AUTOTEST_EXPORT QThreadData *currentThreadData() noexcept;
++ static void clearCurrentThreadData();
++ static QThreadData *get2(QThread *thread)
++ { Q_ASSERT_X(thread != nullptr, "QThread", "internal error"); return thread->d_func()->data; }
++
++ #if QT_CONFIG(thread)
++ void ref()
++ {
++ (void) _ref.ref();
++ Q_ASSERT(_ref.loadRelaxed() != 0);
++ }
++ void deref()
++ {
++ if (!_ref.deref())
++ delete this;
++ }
++ #else
++ void ref() {}
++ void deref() {}
++ #endif
++ inline bool hasEventDispatcher() const
++ { return eventDispatcher.loadRelaxed() != nullptr; }
++ QAbstractEventDispatcher *createEventDispatcher();
++ QAbstractEventDispatcher *ensureEventDispatcher()
++ {
++ QAbstractEventDispatcher *ed = eventDispatcher.loadRelaxed();
++ if (Q_LIKELY(ed))
++ return ed;
++ return createEventDispatcher();
++ }
++
++ bool canWaitLocked()
++ {
++ QMutexLocker locker(&postEventList.mutex);
++ return canWait;
++ }
++
++ void clearEvents();
++
++ void reuseBindingStatusForNewNativeThread()
++ {
++ auto status = m_statusOrPendingObjects.bindingStatus();
++ if (status)
++ QtPrivate::setBindingStatus(status, {});
++ }
++
++ QStack<QEventLoop *> eventLoops;
++ QPostEventList postEventList;
++ QAtomicPointer<QThread> thread;
++ QAtomicPointer<void> threadId;
++ QAtomicPointer<QAbstractEventDispatcher> eventDispatcher;
++ QList<void *> tls;
++ // manipulating m_statusOrPendingObjects requires QTreadPrivate's mutex to be locked
++ QtPrivate::BindingStatusOrList m_statusOrPendingObjects = {};
++
++ private:
++ QAtomicInt _ref;
++
++ public:
++ int loopLevel = 0;
++ int scopeLevel = 0;
++
++ bool quitNow = false;
++ bool canWait = true;
++ bool isAdopted = false;
++ bool requiresCoreApplication = true;
++
++ private:
++ friend class QAbstractEventDispatcher;
++ friend class QBasicTimer;
++ static Q_AUTOTEST_EXPORT QThreadData *createCurrentThreadData();
++ };
++
++ class QScopedScopeLevelCounter
++ {
++ QThreadData *threadData;
++ public:
++ inline QScopedScopeLevelCounter(QThreadData *threadData)
++ : threadData(threadData)
++ { ++threadData->scopeLevel; }
++ inline ~QScopedScopeLevelCounter()
++ { --threadData->scopeLevel; }
++ };
++
++ // thread wrapper for the main() thread
++ class QAdoptedThread : public QThread
++ {
++ Q_DECLARE_PRIVATE(QThread)
++
++ public:
++ QAdoptedThread(QThreadData *data);
++ ~QAdoptedThread();
++ void init();
++
++ private:
++ #if QT_CONFIG(thread)
++ void run() override;
++ #endif
++ };
++
++ #if QT_CONFIG(thread)
++ inline QBindingStatus *QThreadPrivate::bindingStatus()
++ {
++ return data->m_statusOrPendingObjects.bindingStatus();
++ }
++ #endif
++
++ QT_END_NAMESPACE
++
++ #endif // QTHREAD_P_H