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