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 // Since Windows 11 & Server 2022, the process defaults to affinity to all
300 // processor groups, even though each thread is limited to a specific
301 // processor group. Prior to that, the application starts with affinity to
302 // a specific group but the process becomes multi-group if the application
303 // changes any thread's affinity to another group.
304 //
305 // https://learn.microsoft.com/en-us/windows/win32/procthread/processor-groups#behavior-starting-with-windows-11-and-windows-server-2022
306 auto countArray = [](QSpan<const USHORT> groups) {
307 int total = 0;
308 for (USHORT group : groups)
309 total += GetActiveProcessorCount(group);
310 return total;
311 };
312
313 // Typical case: all but the largest systems will fit this array
314 // Note: this API has been around since Windows 7.
315 USHORT groups[16];
316 USHORT groupCount = std::size(groups);
317 if (Q_LIKELY(GetProcessGroupAffinity(HANDLE(-1), &groupCount, groups))) {
318 return countArray({ groups, groupCount });
319 } else if (groupCount > std::size(groups)) {
320 // use a larger buffer
321 auto buffer = static_cast<USHORT *>(alloca(sizeof(USHORT) * groupCount));
322 if (GetProcessGroupAffinity(HANDLE(-1), &groupCount, buffer))
323 return countArray({ buffer, groupCount });
324 }
325
326 // The new API failed, for some reason (it shouldn't have!). Fall back to
327 // the oldest of APIs. Note this may be limited to 64 logical processors.
328 SYSTEM_INFO sysinfo;
329 GetSystemInfo(&sysinfo);
330 return sysinfo.dwNumberOfProcessors;
331}
332
333void QThread::yieldCurrentThread()
334{
335 SwitchToThread();
336}
337
338#endif // QT_CONFIG(thread)
339
340void QThread::sleep(std::chrono::nanoseconds nsecs)
341{
342 using namespace std::chrono;
343 if (nsecs < 2ms) {
344 QUniqueWin32NullHandle waitableTimerHandle;
345 waitableTimerHandle.reset(CreateWaitableTimerEx(
346 nullptr, nullptr, CREATE_WAITABLE_TIMER_HIGH_RESOLUTION, TIMER_ALL_ACCESS));
347 if (waitableTimerHandle) {
348 using namespace std::chrono_literals;
349 // SetWaitableTimerEx's uses intervals of 100ns (0.1µs)
350 using hundredsOfNano = std::ratio<1, 10'000'000>;
351 using hundredsOfNanoseconds = std::chrono::duration<long long, hundredsOfNano>;
352 const auto ticks100ns = duration_cast<hundredsOfNanoseconds>(nsecs);
353 LARGE_INTEGER i;
354 // Negate to make it a relative timeout:
355 i.QuadPart = std::min(-(ticks100ns.count()), -1ll);
356 BOOL timerResult = SetWaitableTimerEx(
357 waitableTimerHandle.get(), &i,
358 0, // not periodic
359 nullptr, nullptr, // no callback
360 nullptr, // no wake context / don't wake from sleep
361 0); // lowest tolerated delay
362
363 if (timerResult == TRUE) {
364 if (WaitForSingleObject(waitableTimerHandle.get(), INFINITE) == ERROR_SUCCESS)
365 return;
366 }
367 }
368 }
369 // Fallback:
370 ::Sleep(DWORD(duration_cast<milliseconds>(nsecs).count()));
371}
372
373void QThread::sleep(unsigned long secs)
374{
375 ::Sleep(secs * 1000);
376}
377
378void QThread::msleep(unsigned long msecs)
379{
380 ::Sleep(msecs);
381}
382
383void QThread::usleep(unsigned long usecs)
384{
385 sleep(std::chrono::microseconds(usecs));
386}
387
388#if QT_CONFIG(thread)
389
390void QThread::start(Priority priority)
391{
392 Q_D(QThread);
393 QMutexLocker locker(&d->mutex);
394
395 if (d->threadState == QThreadPrivate::Finishing) {
396 locker.unlock();
397 wait();
398 locker.relock();
399 }
400
401 if (d->threadState == QThreadPrivate::Running)
402 return;
403
404 // avoid interacting with the binding system
405 d->objectName = d->extraData ? d->extraData->objectName.valueBypassingBindings()
406 : QString();
407 d->threadState = QThreadPrivate::Running;
408 d->exited = false;
409 d->returnCode = 0;
410 d->interruptionRequested.store(false, std::memory_order_relaxed);
411
412 /*
413 NOTE: we create the thread in the suspended state, set the
414 priority and then resume the thread.
415
416 since threads are created with normal priority by default, we
417 could get into a case where a thread (with priority less than
418 NormalPriority) tries to create a new thread (also with priority
419 less than NormalPriority), but the newly created thread preempts
420 its 'parent' and runs at normal priority.
421 */
422#if defined(Q_CC_MSVC) && !defined(_DLL)
423 // MSVC -MT or -MTd build
424 d->handle = (Qt::HANDLE) _beginthreadex(NULL, d->stackSize, QThreadPrivate::start,
425 this, CREATE_SUSPENDED, nullptr);
426#else
427 // MSVC -MD or -MDd or MinGW build
428 d->handle = CreateThread(nullptr, d->stackSize,
429 reinterpret_cast<LPTHREAD_START_ROUTINE>(QThreadPrivate::start),
430 this, CREATE_SUSPENDED, nullptr);
431#endif
432
433 if (!d->handle) {
434 qErrnoWarning("QThread::start: Failed to create thread");
435 d->threadState = QThreadPrivate::NotStarted;
436 return;
437 }
438
439 int prio;
440 d->priority = priority;
441 switch (priority) {
442 case IdlePriority:
443 prio = THREAD_PRIORITY_IDLE;
444 break;
445
446 case LowestPriority:
447 prio = THREAD_PRIORITY_LOWEST;
448 break;
449
450 case LowPriority:
451 prio = THREAD_PRIORITY_BELOW_NORMAL;
452 break;
453
454 case NormalPriority:
455 prio = THREAD_PRIORITY_NORMAL;
456 break;
457
458 case HighPriority:
459 prio = THREAD_PRIORITY_ABOVE_NORMAL;
460 break;
461
462 case HighestPriority:
463 prio = THREAD_PRIORITY_HIGHEST;
464 break;
465
466 case TimeCriticalPriority:
467 prio = THREAD_PRIORITY_TIME_CRITICAL;
468 break;
469
470 case InheritPriority:
471 default:
472 prio = GetThreadPriority(GetCurrentThread());
473 break;
474 }
475
476 if (!SetThreadPriority(d->handle, prio)) {
477 qErrnoWarning("QThread::start: Failed to set thread priority");
478 }
479
480 if (ResumeThread(d->handle) == (DWORD) -1) {
481 qErrnoWarning("QThread::start: Failed to resume new thread");
482 }
483}
484
485void QThread::terminate()
486{
487 Q_D(QThread);
488 QMutexLocker locker(&d->mutex);
489 if (d->threadState != QThreadPrivate::Running)
490 return;
491 if (!d->terminationEnabled) {
492 d->terminatePending = true;
493 return;
494 }
495
496 TerminateThread(d->handle, 0);
497 d->finish(false);
498}
499
500bool QThreadPrivate::wait(QMutexLocker<QMutex> &locker, QDeadlineTimer deadline)
501{
502 Q_ASSERT(threadState != QThreadPrivate::Finished);
503 Q_ASSERT(locker.isLocked());
504 QThreadPrivate *d = this;
505
506 ++d->waiters;
507 locker.mutex()->unlock();
508
509 bool ret = false;
510 switch (WaitForSingleObject(d->handle, deadline.remainingTime())) {
511 case WAIT_OBJECT_0:
512 ret = true;
513 break;
514 case WAIT_FAILED:
515 qErrnoWarning("QThread::wait: Thread wait failure");
516 break;
517 case WAIT_ABANDONED:
518 case WAIT_TIMEOUT:
519 default:
520 break;
521 }
522
523 locker.mutex()->lock();
524 --d->waiters;
525
526 if (ret && d->threadState < QThreadPrivate::Finished) {
527 // thread was terminated by someone else
528
529 d->finish(false);
530 }
531
532 if (d->threadState == QThreadPrivate::Finished && !d->waiters) {
533 CloseHandle(d->handle);
534 d->handle = 0;
535 }
536
537 return ret;
538}
539
540void QThread::setTerminationEnabled(bool enabled)
541{
542 QThread *thr = currentThread();
543 Q_ASSERT_X(thr != 0, "QThread::setTerminationEnabled()",
544 "Current thread was not started with QThread.");
545 QThreadPrivate *d = thr->d_func();
546 QMutexLocker locker(&d->mutex);
547 d->terminationEnabled = enabled;
548 if (enabled && d->terminatePending) {
549 d->finish(false);
550 locker.unlock(); // don't leave the mutex locked!
551 _endthreadex(0);
552 }
553}
554
555// Caller must hold the mutex
556void QThreadPrivate::setPriority(QThread::Priority threadPriority)
557{
558 // copied from start() with a few modifications:
559
560 int prio;
561 priority = threadPriority;
562 switch (threadPriority) {
563 case QThread::IdlePriority:
564 prio = THREAD_PRIORITY_IDLE;
565 break;
566
567 case QThread::LowestPriority:
568 prio = THREAD_PRIORITY_LOWEST;
569 break;
570
571 case QThread::LowPriority:
572 prio = THREAD_PRIORITY_BELOW_NORMAL;
573 break;
574
575 case QThread::NormalPriority:
576 prio = THREAD_PRIORITY_NORMAL;
577 break;
578
579 case QThread::HighPriority:
580 prio = THREAD_PRIORITY_ABOVE_NORMAL;
581 break;
582
583 case QThread::HighestPriority:
584 prio = THREAD_PRIORITY_HIGHEST;
585 break;
586
587 case QThread::TimeCriticalPriority:
588 prio = THREAD_PRIORITY_TIME_CRITICAL;
589 break;
590
591 default:
592 return;
593 }
594
595 if (!SetThreadPriority(handle, prio)) {
596 qErrnoWarning("QThread::setPriority: Failed to set thread priority");
597 }
598}
599
600#endif // QT_CONFIG(thread)
601
602QT_END_NAMESPACE
static QAbstractEventDispatcher * createEventDispatcher(QThreadData *data)
#define Q_STATIC_LOGGING_CATEGORY(name,...)
#define CREATE_WAITABLE_TIMER_HIGH_RESOLUTION