1// Copyright (C) 2016 The Qt Company Ltd.
2// Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Giuseppe D'Angelo <giuseppe.dangelo@kdab.com>
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4
5#ifndef QTHREAD_H
6#define QTHREAD_H
7
8#include <QtCore/qobject.h>
9#include <QtCore/qdeadlinetimer.h>
10
11// For QThread::create
12#include <future> // for std::async
13#include <functional> // for std::invoke; no guard needed as it's a C++98 header
14// internal compiler error with mingw 8.1
15#if defined(Q_CC_MSVC) && defined(Q_PROCESSOR_X86)
16#include <intrin.h>
17#endif
18
19QT_BEGIN_NAMESPACE
20
21
22class QThreadData;
23class QThreadPrivate;
24class QAbstractEventDispatcher;
25class QEventLoopLocker;
26
27class Q_CORE_EXPORT QThread : public QObject
28{
29 Q_OBJECT
30public:
31 static Qt::HANDLE currentThreadId() noexcept Q_DECL_PURE_FUNCTION;
32 static QThread *currentThread();
33 static int idealThreadCount() noexcept;
34 static void yieldCurrentThread();
35
36 explicit QThread(QObject *parent = nullptr);
37 ~QThread();
38
39 enum Priority {
40 IdlePriority,
41
42 LowestPriority,
43 LowPriority,
44 NormalPriority,
45 HighPriority,
46 HighestPriority,
47
48 TimeCriticalPriority,
49
50 InheritPriority
51 };
52
53 void setPriority(Priority priority);
54 Priority priority() const;
55
56 bool isFinished() const;
57 bool isRunning() const;
58
59 void requestInterruption();
60 bool isInterruptionRequested() const;
61
62 void setStackSize(uint stackSize);
63 uint stackSize() const;
64
65 QAbstractEventDispatcher *eventDispatcher() const;
66 void setEventDispatcher(QAbstractEventDispatcher *eventDispatcher);
67
68 bool event(QEvent *event) override;
69 int loopLevel() const;
70
71 template <typename Function, typename... Args>
72 [[nodiscard]] static QThread *create(Function &&f, Args &&... args);
73
74public Q_SLOTS:
75 void start(Priority = InheritPriority);
76 void terminate();
77 void exit(int retcode = 0);
78 void quit();
79
80public:
81 bool wait(QDeadlineTimer deadline = QDeadlineTimer(QDeadlineTimer::Forever));
82 bool wait(unsigned long time)
83 {
84 if (time == (std::numeric_limits<unsigned long>::max)())
85 return wait(deadline: QDeadlineTimer(QDeadlineTimer::Forever));
86 return wait(deadline: QDeadlineTimer(time));
87 }
88
89 static void sleep(unsigned long);
90 static void msleep(unsigned long);
91 static void usleep(unsigned long);
92 static void sleep(std::chrono::nanoseconds nsec);
93
94Q_SIGNALS:
95 void started(QPrivateSignal);
96 void finished(QPrivateSignal);
97
98protected:
99 virtual void run();
100 int exec();
101
102 static void setTerminationEnabled(bool enabled = true);
103
104protected:
105 QThread(QThreadPrivate &dd, QObject *parent = nullptr);
106
107private:
108 Q_DECLARE_PRIVATE(QThread)
109 friend class QEventLoopLocker;
110
111 [[nodiscard]] static QThread *createThreadImpl(std::future<void> &&future);
112 static Qt::HANDLE currentThreadIdImpl() noexcept Q_DECL_PURE_FUNCTION;
113
114 friend class QCoreApplication;
115 friend class QThreadData;
116};
117
118template <typename Function, typename... Args>
119QThread *QThread::create(Function &&f, Args &&... args)
120{
121 using DecayedFunction = typename std::decay<Function>::type;
122 auto threadFunction =
123 [f = static_cast<DecayedFunction>(std::forward<Function>(f))](auto &&... largs) mutable -> void
124 {
125 (void)std::invoke(std::move(f), std::forward<decltype(largs)>(largs)...);
126 };
127
128 return createThreadImpl(future: std::async(std::launch::deferred,
129 std::move(threadFunction),
130 std::forward<Args>(args)...));
131}
132
133/*
134 On architectures and platforms we know, interpret the thread control
135 block (TCB) as a unique identifier for a thread within a process. Otherwise,
136 fall back to a slower but safe implementation.
137
138 As per the documentation of currentThreadId, we return an opaque handle
139 as a thread identifier, and application code is not supposed to use that
140 value for anything. In Qt we use the handle to check if threads are identical,
141 for which the TCB is sufficient.
142
143 So we use the fastest possible way, rather than spend time on returning
144 some pseudo-interoperable value.
145*/
146inline Qt::HANDLE QThread::currentThreadId() noexcept
147{
148 // define is undefed if we have to fall back to currentThreadIdImpl
149#define QT_HAS_FAST_CURRENT_THREAD_ID
150 Qt::HANDLE tid; // typedef to void*
151 static_assert(sizeof(tid) == sizeof(void*));
152 // See https://akkadia.org/drepper/tls.pdf for x86 ABI
153#if defined(Q_PROCESSOR_X86_32) && ((defined(Q_OS_LINUX) && defined(__GLIBC__)) || defined(Q_OS_FREEBSD)) // x86 32-bit always uses GS
154 __asm__("mov %%gs:%c1, %0" : "=r" (tid) : "i" (2 * sizeof(void*)) : );
155#elif defined(Q_PROCESSOR_X86_64) && defined(Q_OS_DARWIN)
156 // 64bit macOS uses GS, see https://github.com/apple/darwin-xnu/blob/master/libsyscall/os/tsd.h
157 __asm__("mov %%gs:0, %0" : "=r" (tid) : : );
158#elif defined(Q_PROCESSOR_X86_64) && ((defined(Q_OS_LINUX) && defined(__GLIBC__)) || defined(Q_OS_FREEBSD))
159 // x86_64 Linux, BSD uses FS
160 __asm__("mov %%fs:%c1, %0" : "=r" (tid) : "i" (2 * sizeof(void*)) : );
161#elif defined(Q_PROCESSOR_X86_64) && defined(Q_OS_WIN)
162 // See https://en.wikipedia.org/wiki/Win32_Thread_Information_Block
163 // First get the pointer to the TIB
164 quint8 *tib;
165# if defined(Q_CC_MINGW) // internal compiler error when using the intrinsics
166 __asm__("movq %%gs:0x30, %0" : "=r" (tib) : :);
167# else
168 tib = reinterpret_cast<quint8 *>(__readgsqword(0x30));
169# endif
170 // Then read the thread ID
171 tid = *reinterpret_cast<Qt::HANDLE *>(tib + 0x48);
172#elif defined(Q_PROCESSOR_X86_32) && defined(Q_OS_WIN)
173 // First get the pointer to the TIB
174 quint8 *tib;
175# if defined(Q_CC_MINGW) // internal compiler error when using the intrinsics
176 __asm__("movl %%fs:0x18, %0" : "=r" (tib) : :);
177# else
178 tib = reinterpret_cast<quint8 *>(__readfsdword(0x18));
179# endif
180 // Then read the thread ID
181 tid = *reinterpret_cast<Qt::HANDLE *>(tib + 0x24);
182#else
183#undef QT_HAS_FAST_CURRENT_THREAD_ID
184 tid = currentThreadIdImpl();
185#endif
186 return tid;
187}
188
189QT_END_NAMESPACE
190
191#endif // QTHREAD_H
192