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