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