Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qthread_win.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
5#include "qthread.h"
6#include "qthread_p.h"
7
9#include <private/qcoreapplication_p.h>
10#include <private/qeventdispatcher_win_p.h>
12#include "qmutex.h"
13#include <private/quniquehandle_types_p.h>
14
15#include <qt_windows.h>
16
17#ifndef _MT
18# define _MT
19#endif // _MT
20#include <process.h>
21
22extern "C" {
23// MinGW is missing the declaration of SetThreadDescription:
24WINBASEAPI
25HRESULT
26WINAPI
27SetThreadDescription(
28 _In_ HANDLE hThread,
29 _In_ PCWSTR lpThreadDescription
30 );
31}
32
33#ifndef THREAD_POWER_THROTTLING_EXECUTION_SPEED
34#define THREAD_POWER_THROTTLING_EXECUTION_SPEED 0x1
35#define THREAD_POWER_THROTTLING_CURRENT_VERSION 1
36
41} THREAD_POWER_THROTTLING_STATE;
42#endif
43
44#ifndef CREATE_WAITABLE_TIMER_HIGH_RESOLUTION
45#define CREATE_WAITABLE_TIMER_HIGH_RESOLUTION 0x2
46#endif
47
48QT_BEGIN_NAMESPACE
49
50Q_STATIC_LOGGING_CATEGORY(lcQThread, "qt.core.thread", QtWarningMsg)
51
52#if QT_CONFIG(thread)
53
54Q_CONSTINIT static thread_local QThreadData *currentThreadData = nullptr;
55
56static void deref_current_thread_data(QThreadData *data);
57static void destroy_current_thread_data(void *p)
58{
59 QThreadData *data = static_cast<QThreadData *>(p);
60 QThread *thread = data->thread.loadAcquire();
61
62 if (data->isAdopted) {
63 // If this is an adopted thread, then QThreadData owns the QThread and
64 // this is very likely the last reference. These pointers cannot be
65 // null and there is no race.
66 QThreadPrivate *thread_p = static_cast<QThreadPrivate *>(QObjectPrivate::get(thread));
67 thread_p->finish();
68 } else {
69 // We may be racing the QThread destructor in another thread and it may
70 // have begun destruction; we must not dereference the QThread pointer.
71 }
72
73 deref_current_thread_data(data);
74}
75
76// static
77void deref_current_thread_data(QThreadData *data)
78{
79 // the QThread object may still have a reference, so this may not delete
80 data->deref();
81
82 // ... but we must reset it to zero before returning so we aren't
83 // leaving a dangling pointer.
84 currentThreadData = nullptr;
85}
86
87static QThreadData *get_thread_data()
88{
89 return currentThreadData;
90}
91
92static void set_thread_data(QThreadData *data) noexcept
93{
94 if (data) {
95 struct Cleanup {
96 Cleanup() { QThreadStoragePrivate::init(); }
97 ~Cleanup() { destroy_current_thread_data(currentThreadData); }
98 };
99 static thread_local Cleanup currentThreadCleanup;
100 }
101 currentThreadData = data;
102}
103
104/*
105 QThreadData
106*/
107void QThreadData::clearCurrentThreadData()
108{
109 set_thread_data(nullptr);
110}
111
112QThreadData *QThreadData::currentThreadData() noexcept
113{
114 return get_thread_data();
115}
116
117QThreadData *QThreadData::createCurrentThreadData()
118{
119 Q_ASSERT(!currentThreadData());
120
121 QThreadData *data = new QThreadData();
122
123 // This needs to be called prior to new QAdoptedThread() to avoid
124 // recursion (see qobject.cpp).
125 set_thread_data(data);
126
127 QT_TRY {
128 data->thread.storeRelease(new QAdoptedThread(data));
129 } QT_CATCH(...) {
130 deref_current_thread_data(data);
131 QT_RETHROW;
132 }
133 return data;
134}
135
136void QAdoptedThread::init()
137{
138 d_func()->handle = GetCurrentThread();
139}
140
141/**************************************************************************
142 ** QThreadPrivate
143 *************************************************************************/
144
145#endif // QT_CONFIG(thread)
146
147QAbstractEventDispatcher *QThreadPrivate::createEventDispatcher(QThreadData *data)
148{
149 Q_UNUSED(data);
150 return new QEventDispatcherWin32;
151}
152
153#if QT_CONFIG(thread)
154
155unsigned int __stdcall QT_ENSURE_STACK_ALIGNED_FOR_SSE QThreadPrivate::start(void *arg) noexcept
156{
157 QThread *thr = reinterpret_cast<QThread *>(arg);
158 QThreadData *data = QThreadData::get2(thr);
159
160 data->ref();
161 set_thread_data(data);
162 data->threadId.storeRelaxed(QThread::currentThreadId());
163
164 // If a QThread is restarted, reuse the QBindingStatus, too
165 data->reuseBindingStatusForNewNativeThread();
166
167 QThread::setTerminationEnabled(false);
168
169 {
170 QMutexLocker locker(&thr->d_func()->mutex);
171 data->quitNow = thr->d_func()->exited;
172
173 if (thr->d_func()->serviceLevel != QThread::QualityOfService::Auto)
174 thr->d_func()->setQualityOfServiceLevel(thr->d_func()->serviceLevel);
175 }
176
177 data->ensureEventDispatcher();
178 data->eventDispatcher.loadRelaxed()->startingUp();
179
180 // sets the name of the current thread.
181 QString threadName = std::exchange(thr->d_func()->objectName, {});
182 if (Q_LIKELY(threadName.isEmpty()))
183 threadName = QString::fromUtf8(thr->metaObject()->className());
184#ifndef QT_WIN_SERVER_2016_COMPAT
185 SetThreadDescription(GetCurrentThread(), reinterpret_cast<const wchar_t *>(threadName.utf16()));
186#else
187 HMODULE kernelbase = GetModuleHandleW(L"kernelbase.dll");
188 if (kernelbase != NULL) {
189 typedef HRESULT (WINAPI *DESCFUNC)(HANDLE, PCWSTR);
190
191 DESCFUNC setThreadDescription =
192 (DESCFUNC)GetProcAddress(kernelbase, "SetThreadDescription");
193 if (setThreadDescription != NULL) {
194 setThreadDescription(GetCurrentThread(),
195 reinterpret_cast<const wchar_t *>(threadName.utf16()));
196 }
197 }
198#endif
199
200 emit thr->started(QThread::QPrivateSignal());
201 QThread::setTerminationEnabled(true);
202 thr->run();
203
204 thr->d_func()->finish();
205 return 0;
206}
207
208void QThreadPrivate::setQualityOfServiceLevel(QThread::QualityOfService qosLevel)
209{
210 Q_Q(QThread);
211 serviceLevel = qosLevel;
212
213#if (_WIN32_WINNT >= _WIN32_WINNT_WIN10_RS3)
214 qCDebug(lcQThread) << "Setting thread QoS class to" << qosLevel << "for thread" << q;
215
216 THREAD_POWER_THROTTLING_STATE state;
217 memset(&state, 0, sizeof(state));
218 state.Version = THREAD_POWER_THROTTLING_CURRENT_VERSION;
219
220 switch (qosLevel) {
221 case QThread::QualityOfService::Auto:
222 state.ControlMask = 0; // Unset control of QoS
223 state.StateMask = 0;
224 break;
225 case QThread::QualityOfService::Eco:
226 state.ControlMask = THREAD_POWER_THROTTLING_EXECUTION_SPEED;
227 state.StateMask = THREAD_POWER_THROTTLING_EXECUTION_SPEED;
228 break;
229 case QThread::QualityOfService::High:
230 state.ControlMask = THREAD_POWER_THROTTLING_EXECUTION_SPEED;
231 state.StateMask = 0; // Ask to disable throttling
232 break;
233 }
234 if (!SetThreadInformation(::GetCurrentThread(), THREAD_INFORMATION_CLASS::ThreadPowerThrottling,
235 &state, sizeof(state))) {
236 qErrnoWarning("Failed to set thread power throttling state");
237 }
238#endif
239}
240
241/*
242 For regularly terminating threads, this will be called and executed by the thread as the
243 last code before the thread exits. In that case, \a arg is the current QThread.
244
245 However, this function will also be called by QThread::terminate (as well as wait() and
246 setTerminationEnabled) to give Qt a chance to update the terminated thread's state and
247 process pending DeleteLater events for objects that live in the terminated thread. And for
248 adopted thread, this method is called by the thread watcher.
249
250 In those cases, \a arg will not be the current thread.
251*/
252void QThreadPrivate::finish(bool lockAnyway) noexcept
253{
254 QThreadPrivate *d = this;
255 QThread *thr = q_func();
256
257 QMutexLocker locker(lockAnyway ? &d->mutex : nullptr);
258 d->threadState = QThreadPrivate::Finishing;
259 d->priority = QThread::InheritPriority;
260 if (lockAnyway)
261 locker.unlock();
262 emit thr->finished(QThread::QPrivateSignal());
263 QCoreApplicationPrivate::sendPostedEvents(nullptr, QEvent::DeferredDelete, d->data);
264 QThreadStoragePrivate::finish(&d->data->tls);
265 if (lockAnyway)
266 locker.relock();
267
268 QAbstractEventDispatcher *eventDispatcher = d->data->eventDispatcher.loadRelaxed();
269 if (eventDispatcher) {
270 d->data->eventDispatcher = 0;
271 if (lockAnyway)
272 locker.unlock();
273 eventDispatcher->closingDown();
274 delete eventDispatcher;
275 if (lockAnyway)
276 locker.relock();
277 }
278
279 d->threadState = QThreadPrivate::Finished;
280 d->interruptionRequested.store(false, std::memory_order_relaxed);
281
282 if (!d->waiters) {
283 CloseHandle(d->handle);
284 d->handle = 0;
285 }
286}
287
288/**************************************************************************
289 ** QThread
290 *************************************************************************/
291
292Qt::HANDLE QThread::currentThreadIdImpl() noexcept
293{
294 return reinterpret_cast<Qt::HANDLE>(quintptr(GetCurrentThreadId()));
295}
296
297int QThread::idealThreadCount() noexcept
298{
299 SYSTEM_INFO sysinfo;
300 GetSystemInfo(&sysinfo);
301 return sysinfo.dwNumberOfProcessors;
302}
303
304void QThread::yieldCurrentThread()
305{
306 SwitchToThread();
307}
308
309#endif // QT_CONFIG(thread)
310
311void QThread::sleep(std::chrono::nanoseconds nsecs)
312{
313 using namespace std::chrono;
314 if (nsecs < 2ms) {
315 QUniqueWin32NullHandle waitableTimerHandle;
316 waitableTimerHandle.reset(CreateWaitableTimerEx(
317 nullptr, nullptr, CREATE_WAITABLE_TIMER_HIGH_RESOLUTION, TIMER_ALL_ACCESS));
318 if (waitableTimerHandle) {
319 using namespace std::chrono_literals;
320 // SetWaitableTimerEx's uses intervals of 100ns (0.1µs)
321 using hundredsOfNano = std::ratio<1, 10'000'000>;
322 using hundredsOfNanoseconds = std::chrono::duration<long long, hundredsOfNano>;
323 const auto ticks100ns = duration_cast<hundredsOfNanoseconds>(nsecs);
324 LARGE_INTEGER i;
325 // Negate to make it a relative timeout:
326 i.QuadPart = std::min(-(ticks100ns.count()), -1ll);
327 BOOL timerResult = SetWaitableTimerEx(
328 waitableTimerHandle.get(), &i,
329 0, // not periodic
330 nullptr, nullptr, // no callback
331 nullptr, // no wake context / don't wake from sleep
332 0); // lowest tolerated delay
333
334 if (timerResult == TRUE) {
335 if (WaitForSingleObject(waitableTimerHandle.get(), INFINITE) == ERROR_SUCCESS)
336 return;
337 }
338 }
339 }
340 // Fallback:
341 ::Sleep(DWORD(duration_cast<milliseconds>(nsecs).count()));
342}
343
344void QThread::sleep(unsigned long secs)
345{
346 ::Sleep(secs * 1000);
347}
348
349void QThread::msleep(unsigned long msecs)
350{
351 ::Sleep(msecs);
352}
353
354void QThread::usleep(unsigned long usecs)
355{
356 sleep(std::chrono::microseconds(usecs));
357}
358
359#if QT_CONFIG(thread)
360
361void QThread::start(Priority priority)
362{
363 Q_D(QThread);
364 QMutexLocker locker(&d->mutex);
365
366 if (d->threadState == QThreadPrivate::Finishing) {
367 locker.unlock();
368 wait();
369 locker.relock();
370 }
371
372 if (d->threadState == QThreadPrivate::Running)
373 return;
374
375 // avoid interacting with the binding system
376 d->objectName = d->extraData ? d->extraData->objectName.valueBypassingBindings()
377 : QString();
378 d->threadState = QThreadPrivate::Running;
379 d->exited = false;
380 d->returnCode = 0;
381 d->interruptionRequested.store(false, std::memory_order_relaxed);
382
383 /*
384 NOTE: we create the thread in the suspended state, set the
385 priority and then resume the thread.
386
387 since threads are created with normal priority by default, we
388 could get into a case where a thread (with priority less than
389 NormalPriority) tries to create a new thread (also with priority
390 less than NormalPriority), but the newly created thread preempts
391 its 'parent' and runs at normal priority.
392 */
393#if defined(Q_CC_MSVC) && !defined(_DLL)
394 // MSVC -MT or -MTd build
395 d->handle = (Qt::HANDLE) _beginthreadex(NULL, d->stackSize, QThreadPrivate::start,
396 this, CREATE_SUSPENDED, nullptr);
397#else
398 // MSVC -MD or -MDd or MinGW build
399 d->handle = CreateThread(nullptr, d->stackSize,
400 reinterpret_cast<LPTHREAD_START_ROUTINE>(QThreadPrivate::start),
401 this, CREATE_SUSPENDED, nullptr);
402#endif
403
404 if (!d->handle) {
405 qErrnoWarning("QThread::start: Failed to create thread");
406 d->threadState = QThreadPrivate::NotStarted;
407 return;
408 }
409
410 int prio;
411 d->priority = priority;
412 switch (priority) {
413 case IdlePriority:
414 prio = THREAD_PRIORITY_IDLE;
415 break;
416
417 case LowestPriority:
418 prio = THREAD_PRIORITY_LOWEST;
419 break;
420
421 case LowPriority:
422 prio = THREAD_PRIORITY_BELOW_NORMAL;
423 break;
424
425 case NormalPriority:
426 prio = THREAD_PRIORITY_NORMAL;
427 break;
428
429 case HighPriority:
430 prio = THREAD_PRIORITY_ABOVE_NORMAL;
431 break;
432
433 case HighestPriority:
434 prio = THREAD_PRIORITY_HIGHEST;
435 break;
436
437 case TimeCriticalPriority:
438 prio = THREAD_PRIORITY_TIME_CRITICAL;
439 break;
440
441 case InheritPriority:
442 default:
443 prio = GetThreadPriority(GetCurrentThread());
444 break;
445 }
446
447 if (!SetThreadPriority(d->handle, prio)) {
448 qErrnoWarning("QThread::start: Failed to set thread priority");
449 }
450
451 if (ResumeThread(d->handle) == (DWORD) -1) {
452 qErrnoWarning("QThread::start: Failed to resume new thread");
453 }
454}
455
456void QThread::terminate()
457{
458 Q_D(QThread);
459 QMutexLocker locker(&d->mutex);
460 if (d->threadState != QThreadPrivate::Running)
461 return;
462 if (!d->terminationEnabled) {
463 d->terminatePending = true;
464 return;
465 }
466
467 TerminateThread(d->handle, 0);
468 d->finish(false);
469}
470
471bool QThreadPrivate::wait(QMutexLocker<QMutex> &locker, QDeadlineTimer deadline)
472{
473 Q_ASSERT(threadState != QThreadPrivate::Finished);
474 Q_ASSERT(locker.isLocked());
475 QThreadPrivate *d = this;
476
477 ++d->waiters;
478 locker.mutex()->unlock();
479
480 bool ret = false;
481 switch (WaitForSingleObject(d->handle, deadline.remainingTime())) {
482 case WAIT_OBJECT_0:
483 ret = true;
484 break;
485 case WAIT_FAILED:
486 qErrnoWarning("QThread::wait: Thread wait failure");
487 break;
488 case WAIT_ABANDONED:
489 case WAIT_TIMEOUT:
490 default:
491 break;
492 }
493
494 locker.mutex()->lock();
495 --d->waiters;
496
497 if (ret && d->threadState < QThreadPrivate::Finished) {
498 // thread was terminated by someone else
499
500 d->finish(false);
501 }
502
503 if (d->threadState == QThreadPrivate::Finished && !d->waiters) {
504 CloseHandle(d->handle);
505 d->handle = 0;
506 }
507
508 return ret;
509}
510
511void QThread::setTerminationEnabled(bool enabled)
512{
513 QThread *thr = currentThread();
514 Q_ASSERT_X(thr != 0, "QThread::setTerminationEnabled()",
515 "Current thread was not started with QThread.");
516 QThreadPrivate *d = thr->d_func();
517 QMutexLocker locker(&d->mutex);
518 d->terminationEnabled = enabled;
519 if (enabled && d->terminatePending) {
520 d->finish(false);
521 locker.unlock(); // don't leave the mutex locked!
522 _endthreadex(0);
523 }
524}
525
526// Caller must hold the mutex
527void QThreadPrivate::setPriority(QThread::Priority threadPriority)
528{
529 // copied from start() with a few modifications:
530
531 int prio;
532 priority = threadPriority;
533 switch (threadPriority) {
534 case QThread::IdlePriority:
535 prio = THREAD_PRIORITY_IDLE;
536 break;
537
538 case QThread::LowestPriority:
539 prio = THREAD_PRIORITY_LOWEST;
540 break;
541
542 case QThread::LowPriority:
543 prio = THREAD_PRIORITY_BELOW_NORMAL;
544 break;
545
546 case QThread::NormalPriority:
547 prio = THREAD_PRIORITY_NORMAL;
548 break;
549
550 case QThread::HighPriority:
551 prio = THREAD_PRIORITY_ABOVE_NORMAL;
552 break;
553
554 case QThread::HighestPriority:
555 prio = THREAD_PRIORITY_HIGHEST;
556 break;
557
558 case QThread::TimeCriticalPriority:
559 prio = THREAD_PRIORITY_TIME_CRITICAL;
560 break;
561
562 default:
563 return;
564 }
565
566 if (!SetThreadPriority(handle, prio)) {
567 qErrnoWarning("QThread::setPriority: Failed to set thread priority");
568 }
569}
570
571#endif // QT_CONFIG(thread)
572
573QT_END_NAMESPACE
static QAbstractEventDispatcher * createEventDispatcher(QThreadData *data)
#define Q_STATIC_LOGGING_CATEGORY(name,...)
#define CREATE_WAITABLE_TIMER_HIGH_RESOLUTION