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