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 {
169 QMutexLocker locker(&thr->d_func()->mutex);
170 data->quitNow = thr->d_func()->exited;
171
172 if (thr->d_func()->serviceLevel != QThread::QualityOfService::Auto)
173 thr->d_func()->setQualityOfServiceLevel(thr->d_func()->serviceLevel);
174 }
175
176 data->ensureEventDispatcher();
177 data->eventDispatcher.loadRelaxed()->startingUp();
178
179 // sets the name of the current thread.
180 QString threadName = std::exchange(thr->d_func()->objectName, {});
181 if (Q_LIKELY(threadName.isEmpty()))
182 threadName = QString::fromUtf8(thr->metaObject()->className());
183 SetThreadDescription(GetCurrentThread(), reinterpret_cast<const wchar_t *>(threadName.utf16()));
184
185 emit thr->started(QThread::QPrivateSignal());
186 QThread::setTerminationEnabled(true);
187 thr->run();
188
189 thr->d_func()->finish();
190 return 0;
191}
192
193void QThreadPrivate::setQualityOfServiceLevel(QThread::QualityOfService qosLevel)
194{
195 Q_Q(QThread);
196 serviceLevel = qosLevel;
197
198#if (_WIN32_WINNT >= _WIN32_WINNT_WIN10_RS3)
199 qCDebug(lcQThread) << "Setting thread QoS class to" << qosLevel << "for thread" << q;
200
201 THREAD_POWER_THROTTLING_STATE state;
202 memset(&state, 0, sizeof(state));
203 state.Version = THREAD_POWER_THROTTLING_CURRENT_VERSION;
204
205 switch (qosLevel) {
206 case QThread::QualityOfService::Auto:
207 state.ControlMask = 0; // Unset control of QoS
208 state.StateMask = 0;
209 break;
210 case QThread::QualityOfService::Eco:
211 state.ControlMask = THREAD_POWER_THROTTLING_EXECUTION_SPEED;
212 state.StateMask = THREAD_POWER_THROTTLING_EXECUTION_SPEED;
213 break;
214 case QThread::QualityOfService::High:
215 state.ControlMask = THREAD_POWER_THROTTLING_EXECUTION_SPEED;
216 state.StateMask = 0; // Ask to disable throttling
217 break;
218 }
219 if (!SetThreadInformation(::GetCurrentThread(), THREAD_INFORMATION_CLASS::ThreadPowerThrottling,
220 &state, sizeof(state))) {
221 qErrnoWarning("Failed to set thread power throttling state");
222 }
223#endif
224}
225
226/*
227 For regularly terminating threads, this will be called and executed by the thread as the
228 last code before the thread exits. In that case, \a arg is the current QThread.
229
230 However, this function will also be called by QThread::terminate (as well as wait() and
231 setTerminationEnabled) to give Qt a chance to update the terminated thread's state and
232 process pending DeleteLater events for objects that live in the terminated thread. And for
233 adopted thread, this method is called by the thread watcher.
234
235 In those cases, \a arg will not be the current thread.
236*/
237void QThreadPrivate::finish(bool lockAnyway) noexcept
238{
239 QThreadPrivate *d = this;
240 QThread *thr = q_func();
241
242 QMutexLocker locker(lockAnyway ? &d->mutex : nullptr);
243 d->threadState = QThreadPrivate::Finishing;
244 d->priority = QThread::InheritPriority;
245 if (lockAnyway)
246 locker.unlock();
247 emit thr->finished(QThread::QPrivateSignal());
248 QCoreApplicationPrivate::sendPostedEvents(nullptr, QEvent::DeferredDelete, d->data);
249 QThreadStoragePrivate::finish(&d->data->tls);
250 if (lockAnyway)
251 locker.relock();
252
253 QAbstractEventDispatcher *eventDispatcher = d->data->eventDispatcher.loadRelaxed();
254 if (eventDispatcher) {
255 d->data->eventDispatcher = 0;
256 if (lockAnyway)
257 locker.unlock();
258 eventDispatcher->closingDown();
259 delete eventDispatcher;
260 if (lockAnyway)
261 locker.relock();
262 }
263
264 d->threadState = QThreadPrivate::Finished;
265 d->interruptionRequested.store(false, std::memory_order_relaxed);
266
267 if (!d->waiters) {
268 CloseHandle(d->handle);
269 d->handle = 0;
270 }
271}
272
273/**************************************************************************
274 ** QThread
275 *************************************************************************/
276
277Qt::HANDLE QThread::currentThreadIdImpl() noexcept
278{
279 return reinterpret_cast<Qt::HANDLE>(quintptr(GetCurrentThreadId()));
280}
281
282int QThread::idealThreadCount() noexcept
283{
284 // Since Windows 11 & Server 2022, the process defaults to affinity to all
285 // processor groups, even though each thread is limited to a specific
286 // processor group. Prior to that, the application starts with affinity to
287 // a specific group but the process becomes multi-group if the application
288 // changes any thread's affinity to another group.
289 //
290 // https://learn.microsoft.com/en-us/windows/win32/procthread/processor-groups#behavior-starting-with-windows-11-and-windows-server-2022
291 auto countArray = [](QSpan<const USHORT> groups) {
292 int total = 0;
293 for (USHORT group : groups)
294 total += GetActiveProcessorCount(group);
295 return total;
296 };
297
298 // Typical case: all but the largest systems will fit this array
299 // Note: this API has been around since Windows 7.
300 USHORT groups[16];
301 USHORT groupCount = std::size(groups);
302 if (Q_LIKELY(GetProcessGroupAffinity(HANDLE(-1), &groupCount, groups))) {
303 return countArray({ groups, groupCount });
304 } else if (groupCount > std::size(groups)) {
305 // use a larger buffer
306 auto buffer = static_cast<USHORT *>(alloca(sizeof(USHORT) * groupCount));
307 if (GetProcessGroupAffinity(HANDLE(-1), &groupCount, buffer))
308 return countArray({ buffer, groupCount });
309 }
310
311 // The new API failed, for some reason (it shouldn't have!). Fall back to
312 // the oldest of APIs. Note this may be limited to 64 logical processors.
313 SYSTEM_INFO sysinfo;
314 GetSystemInfo(&sysinfo);
315 return sysinfo.dwNumberOfProcessors;
316}
317
318void QThread::yieldCurrentThread()
319{
320 SwitchToThread();
321}
322
323#endif // QT_CONFIG(thread)
324
325void QThread::sleep(std::chrono::nanoseconds nsecs)
326{
327 using namespace std::chrono;
328 if (nsecs < 30ms) {
329 QUniqueWin32NullHandle waitableTimerHandle;
330 waitableTimerHandle.reset(CreateWaitableTimerEx(
331 nullptr, nullptr, CREATE_WAITABLE_TIMER_HIGH_RESOLUTION, TIMER_ALL_ACCESS));
332 if (waitableTimerHandle) {
333 using namespace std::chrono_literals;
334 // SetWaitableTimerEx's uses intervals of 100ns (0.1µs)
335 const auto ticks100ns = ceil<QtPrivate::win32_ticks>(nsecs);
336 LARGE_INTEGER i;
337 // Negate to make it a relative timeout:
338 i.QuadPart = std::min(-(ticks100ns.count()), -1ll);
339 BOOL timerResult = SetWaitableTimerEx(
340 waitableTimerHandle.get(), &i,
341 0, // not periodic
342 nullptr, nullptr, // no callback
343 nullptr, // no wake context / don't wake from sleep
344 0); // lowest tolerated delay
345
346 if (timerResult == TRUE) {
347 if (WaitForSingleObject(waitableTimerHandle.get(), INFINITE) == ERROR_SUCCESS)
348 return;
349 }
350 }
351 }
352 // Fallback:
353 ::Sleep(DWORD(ceil<milliseconds>(nsecs).count()));
354}
355
356void QThread::sleep(unsigned long secs)
357{
358 ::Sleep(secs * 1000);
359}
360
361void QThread::msleep(unsigned long msecs)
362{
363 ::Sleep(msecs);
364}
365
366void QThread::usleep(unsigned long usecs)
367{
368 sleep(std::chrono::microseconds(usecs));
369}
370
371#if QT_CONFIG(thread)
372
373void QThread::start(Priority priority)
374{
375 Q_D(QThread);
376 QMutexLocker locker(&d->mutex);
377
378 if (d->threadState == QThreadPrivate::Finishing) {
379 locker.unlock();
380 wait();
381 locker.relock();
382 }
383
384 if (d->threadState == QThreadPrivate::Running)
385 return;
386
387 // avoid interacting with the binding system
388 d->objectName = d->extraData ? d->extraData->objectName.valueBypassingBindings()
389 : QString();
390 d->threadState = QThreadPrivate::Running;
391 d->exited = false;
392 d->returnCode = 0;
393 d->interruptionRequested.store(false, std::memory_order_relaxed);
394
395 /*
396 NOTE: we create the thread in the suspended state, set the
397 priority and then resume the thread.
398
399 since threads are created with normal priority by default, we
400 could get into a case where a thread (with priority less than
401 NormalPriority) tries to create a new thread (also with priority
402 less than NormalPriority), but the newly created thread preempts
403 its 'parent' and runs at normal priority.
404 */
405#if defined(Q_CC_MSVC) && !defined(_DLL)
406 // MSVC -MT or -MTd build
407 d->handle = (Qt::HANDLE) _beginthreadex(NULL, d->stackSize, QThreadPrivate::start,
408 this, CREATE_SUSPENDED, nullptr);
409#else
410 // MSVC -MD or -MDd or MinGW build
411 d->handle = CreateThread(nullptr, d->stackSize,
412 reinterpret_cast<LPTHREAD_START_ROUTINE>(QThreadPrivate::start),
413 this, CREATE_SUSPENDED, nullptr);
414#endif
415
416 if (!d->handle) {
417 qErrnoWarning("QThread::start: Failed to create thread");
418 d->threadState = QThreadPrivate::NotStarted;
419 return;
420 }
421
422 int prio;
423 d->priority = priority;
424 switch (priority) {
425 case IdlePriority:
426 prio = THREAD_PRIORITY_IDLE;
427 break;
428
429 case LowestPriority:
430 prio = THREAD_PRIORITY_LOWEST;
431 break;
432
433 case LowPriority:
434 prio = THREAD_PRIORITY_BELOW_NORMAL;
435 break;
436
437 case NormalPriority:
438 prio = THREAD_PRIORITY_NORMAL;
439 break;
440
441 case HighPriority:
442 prio = THREAD_PRIORITY_ABOVE_NORMAL;
443 break;
444
445 case HighestPriority:
446 prio = THREAD_PRIORITY_HIGHEST;
447 break;
448
449 case TimeCriticalPriority:
450 prio = THREAD_PRIORITY_TIME_CRITICAL;
451 break;
452
453 case InheritPriority:
454 default:
455 prio = GetThreadPriority(GetCurrentThread());
456 break;
457 }
458
459 if (!SetThreadPriority(d->handle, prio)) {
460 qErrnoWarning("QThread::start: Failed to set thread priority");
461 }
462
463 if (ResumeThread(d->handle) == (DWORD) -1) {
464 qErrnoWarning("QThread::start: Failed to resume new thread");
465 }
466}
467
468void QThread::terminate()
469{
470 Q_D(QThread);
471 QMutexLocker locker(&d->mutex);
472 if (d->threadState != QThreadPrivate::Running)
473 return;
474 if (!d->terminationEnabled) {
475 d->terminatePending = true;
476 return;
477 }
478
479 TerminateThread(d->handle, 0);
480 d->finish(false);
481}
482
483bool QThreadPrivate::wait(QMutexLocker<QMutex> &locker, QDeadlineTimer deadline)
484{
485 Q_ASSERT(threadState != QThreadPrivate::Finished);
486 Q_ASSERT(locker.isLocked());
487 QThreadPrivate *d = this;
488
489 ++d->waiters;
490 locker.mutex()->unlock();
491
492 bool ret = false;
493 switch (WaitForSingleObject(d->handle, deadline.remainingTime())) {
494 case WAIT_OBJECT_0:
495 ret = true;
496 break;
497 case WAIT_FAILED:
498 qErrnoWarning("QThread::wait: Thread wait failure");
499 break;
500 case WAIT_ABANDONED:
501 case WAIT_TIMEOUT:
502 default:
503 break;
504 }
505
506 locker.mutex()->lock();
507 --d->waiters;
508
509 if (ret && d->threadState < QThreadPrivate::Finished) {
510 // thread was terminated by someone else
511
512 d->finish(false);
513 }
514
515 if (d->threadState == QThreadPrivate::Finished && !d->waiters) {
516 CloseHandle(d->handle);
517 d->handle = 0;
518 }
519
520 return ret;
521}
522
523void QThread::setTerminationEnabled(bool enabled)
524{
525 QThreadData *thr = QThreadData::currentThreadData();
526 Q_ASSERT_X(thr && !thr->isAdopted, "QThread::setTerminationEnabled()",
527 "Current thread was not started with QThread.");
528 QThreadPrivate *d = thr->thread.loadRelaxed()->d_func();
529 QMutexLocker locker(&d->mutex);
530 d->terminationEnabled = enabled;
531 if (enabled && d->terminatePending) {
532 d->finish(false);
533 locker.unlock(); // don't leave the mutex locked!
534 _endthreadex(0);
535 }
536}
537
538// Caller must hold the mutex
539void QThreadPrivate::setPriority(QThread::Priority threadPriority)
540{
541 // copied from start() with a few modifications:
542
543 int prio;
544 priority = threadPriority;
545 switch (threadPriority) {
546 case QThread::IdlePriority:
547 prio = THREAD_PRIORITY_IDLE;
548 break;
549
550 case QThread::LowestPriority:
551 prio = THREAD_PRIORITY_LOWEST;
552 break;
553
554 case QThread::LowPriority:
555 prio = THREAD_PRIORITY_BELOW_NORMAL;
556 break;
557
558 case QThread::NormalPriority:
559 prio = THREAD_PRIORITY_NORMAL;
560 break;
561
562 case QThread::HighPriority:
563 prio = THREAD_PRIORITY_ABOVE_NORMAL;
564 break;
565
566 case QThread::HighestPriority:
567 prio = THREAD_PRIORITY_HIGHEST;
568 break;
569
570 case QThread::TimeCriticalPriority:
571 prio = THREAD_PRIORITY_TIME_CRITICAL;
572 break;
573
574 default:
575 return;
576 }
577
578 if (!SetThreadPriority(handle, prio)) {
579 qErrnoWarning("QThread::setPriority: Failed to set thread priority");
580 }
581}
582
583#endif // QT_CONFIG(thread)
584
585QT_END_NAMESPACE
static QAbstractEventDispatcher * createEventDispatcher(QThreadData *data)
#define Q_STATIC_LOGGING_CATEGORY(name,...)
#define CREATE_WAITABLE_TIMER_HIGH_RESOLUTION