Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
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#include "qthreadstorage.h"
7#include "qmutex.h"
8
9#include <qcoreapplication.h>
10#include <qpointer.h>
11
12#include <private/qcoreapplication_p.h>
13#include <private/qeventdispatcher_win_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
26WINAPI
28 _In_ HANDLE hThread,
29 _In_ PCWSTR lpThreadDescription
30 );
31}
32
34
35#if QT_CONFIG(thread)
36
37void qt_watch_adopted_thread(const HANDLE adoptedThreadHandle, QThread *qthread);
38DWORD WINAPI qt_adopted_thread_watcher_function(LPVOID);
39
40static DWORD qt_current_thread_data_tls_index = TLS_OUT_OF_INDEXES;
41void qt_create_tls()
42{
43 if (qt_current_thread_data_tls_index != TLS_OUT_OF_INDEXES)
44 return;
45 Q_CONSTINIT static QBasicMutex mutex;
46 QMutexLocker locker(&mutex);
47 if (qt_current_thread_data_tls_index != TLS_OUT_OF_INDEXES)
48 return;
49 qt_current_thread_data_tls_index = TlsAlloc();
50}
51
52static void qt_free_tls()
53{
54 if (qt_current_thread_data_tls_index != TLS_OUT_OF_INDEXES) {
55 TlsFree(qt_current_thread_data_tls_index);
56 qt_current_thread_data_tls_index = TLS_OUT_OF_INDEXES;
57 }
58}
59Q_DESTRUCTOR_FUNCTION(qt_free_tls)
60
61/*
62 QThreadData
63*/
65{
66 TlsSetValue(qt_current_thread_data_tls_index, 0);
67}
68
69QThreadData *QThreadData::current(bool createIfNecessary)
70{
71 qt_create_tls();
72 QThreadData *threadData = reinterpret_cast<QThreadData *>(TlsGetValue(qt_current_thread_data_tls_index));
73 if (!threadData && createIfNecessary) {
74 threadData = new QThreadData;
75 // This needs to be called prior to new AdoptedThread() to
76 // avoid recursion.
77 TlsSetValue(qt_current_thread_data_tls_index, threadData);
78 QT_TRY {
79 threadData->thread = new QAdoptedThread(threadData);
80 } QT_CATCH(...) {
81 TlsSetValue(qt_current_thread_data_tls_index, 0);
82 threadData->deref();
83 threadData = 0;
85 }
86 threadData->deref();
87 threadData->isAdopted = true;
88 threadData->threadId.storeRelaxed(reinterpret_cast<Qt::HANDLE>(quintptr(GetCurrentThreadId())));
89
93 } else {
94 HANDLE realHandle = INVALID_HANDLE_VALUE;
95 DuplicateHandle(GetCurrentProcess(),
96 GetCurrentThread(),
97 GetCurrentProcess(),
98 &realHandle,
99 0,
100 FALSE,
101 DUPLICATE_SAME_ACCESS);
102 qt_watch_adopted_thread(realHandle, threadData->thread);
103 }
104 }
105 return threadData;
106}
107
109{
110 d_func()->handle = GetCurrentThread();
111 d_func()->id = GetCurrentThreadId();
112}
113
114static QList<HANDLE> qt_adopted_thread_handles;
115static QList<QThread *> qt_adopted_qthreads;
116Q_CONSTINIT static QBasicMutex qt_adopted_thread_watcher_mutex;
117static DWORD qt_adopted_thread_watcher_id = 0;
118static HANDLE qt_adopted_thread_wakeup = 0;
119
126void qt_watch_adopted_thread(const HANDLE adoptedThreadHandle, QThread *qthread)
127{
128 QMutexLocker lock(&qt_adopted_thread_watcher_mutex);
129
130 if (GetCurrentThreadId() == qt_adopted_thread_watcher_id) {
131 CloseHandle(adoptedThreadHandle);
132 return;
133 }
134
135 qt_adopted_thread_handles.append(adoptedThreadHandle);
136 qt_adopted_qthreads.append(qthread);
137
138 // Start watcher thread if it is not already running.
139 if (qt_adopted_thread_watcher_id == 0) {
140 if (qt_adopted_thread_wakeup == 0) {
141 qt_adopted_thread_wakeup = CreateEvent(0, false, false, 0);
142 qt_adopted_thread_handles.prepend(qt_adopted_thread_wakeup);
143 }
144
145 CloseHandle(CreateThread(0, 0, qt_adopted_thread_watcher_function, 0, 0, &qt_adopted_thread_watcher_id));
146 } else {
147 SetEvent(qt_adopted_thread_wakeup);
148 }
149}
150
151/*
152 This function loops and waits for native adopted threads to finish.
153 When this happens it derefs the QThreadData for the adopted thread
154 to make sure it gets cleaned up properly.
155*/
156DWORD WINAPI qt_adopted_thread_watcher_function(LPVOID)
157{
158 forever {
159 qt_adopted_thread_watcher_mutex.lock();
160
161 if (qt_adopted_thread_handles.count() == 1) {
162 qt_adopted_thread_watcher_id = 0;
163 qt_adopted_thread_watcher_mutex.unlock();
164 break;
165 }
166
167 QList<HANDLE> handlesCopy = qt_adopted_thread_handles;
168 qt_adopted_thread_watcher_mutex.unlock();
169
170 DWORD ret = WAIT_TIMEOUT;
171 int count;
172 int offset;
173 int loops = handlesCopy.size() / MAXIMUM_WAIT_OBJECTS;
174 if (handlesCopy.size() % MAXIMUM_WAIT_OBJECTS)
175 ++loops;
176 if (loops == 1) {
177 // no need to loop, no timeout
178 offset = 0;
179 count = handlesCopy.count();
180 ret = WaitForMultipleObjects(handlesCopy.count(), handlesCopy.constData(), false, INFINITE);
181 } else {
182 int loop = 0;
183 do {
184 offset = loop * MAXIMUM_WAIT_OBJECTS;
185 count = qMin(handlesCopy.count() - offset, MAXIMUM_WAIT_OBJECTS);
186 ret = WaitForMultipleObjects(count, handlesCopy.constData() + offset, false, 100);
187 loop = (loop + 1) % loops;
188 } while (ret == WAIT_TIMEOUT);
189 }
190
191 if (ret == WAIT_FAILED || ret >= WAIT_OBJECT_0 + uint(count)) {
192 qWarning("QThread internal error while waiting for adopted threads: %d", int(GetLastError()));
193 continue;
194 }
195
196 const int handleIndex = offset + ret - WAIT_OBJECT_0;
197 if (handleIndex == 0) // New handle to watch was added.
198 continue;
199 const int qthreadIndex = handleIndex - 1;
200
201 qt_adopted_thread_watcher_mutex.lock();
202 QThreadData *data = QThreadData::get2(qt_adopted_qthreads.at(qthreadIndex));
203 qt_adopted_thread_watcher_mutex.unlock();
204 if (data->isAdopted) {
205 QThread *thread = data->thread;
206 Q_ASSERT(thread);
207 auto thread_p = static_cast<QThreadPrivate *>(QObjectPrivate::get(thread));
208 Q_UNUSED(thread_p);
209 Q_ASSERT(!thread_p->finished);
210 QThreadPrivate::finish(thread);
211 }
212 data->deref();
213
214 QMutexLocker lock(&qt_adopted_thread_watcher_mutex);
215 CloseHandle(qt_adopted_thread_handles.at(handleIndex));
216 qt_adopted_thread_handles.remove(handleIndex);
217 qt_adopted_qthreads.remove(qthreadIndex);
218 }
219
220 QThreadData *threadData = reinterpret_cast<QThreadData *>(TlsGetValue(qt_current_thread_data_tls_index));
221 if (threadData)
222 threadData->deref();
223
224 return 0;
225}
226
227/**************************************************************************
228 ** QThreadPrivate
229 *************************************************************************/
230
231#endif // QT_CONFIG(thread)
232
234{
235 Q_UNUSED(data);
236 return new QEventDispatcherWin32;
237}
238
239#if QT_CONFIG(thread)
240
241unsigned int __stdcall QT_ENSURE_STACK_ALIGNED_FOR_SSE QThreadPrivate::start(void *arg) noexcept
242{
243 QThread *thr = reinterpret_cast<QThread *>(arg);
245
246 qt_create_tls();
247 TlsSetValue(qt_current_thread_data_tls_index, data);
248 data->threadId.storeRelaxed(reinterpret_cast<Qt::HANDLE>(quintptr(GetCurrentThreadId())));
249
251
252 {
253 QMutexLocker locker(&thr->d_func()->mutex);
254 data->quitNow = thr->d_func()->exited;
255 }
256
257 data->ensureEventDispatcher();
258 data->eventDispatcher.loadRelaxed()->startingUp();
259
260 // sets the name of the current thread.
261 QString threadName = std::exchange(thr->d_func()->objectName, {});
262 if (Q_LIKELY(threadName.isEmpty()))
263 threadName = QString::fromUtf8(thr->metaObject()->className());
264 SetThreadDescription(GetCurrentThread(), reinterpret_cast<const wchar_t *>(threadName.utf16()));
265
266 emit thr->started(QThread::QPrivateSignal());
268 thr->run();
269
270 finish(arg);
271 return 0;
272}
273
274/*
275 For regularly terminating threads, this will be called and executed by the thread as the
276 last code before the thread exits. In that case, \a arg is the current QThread.
277
278 However, this function will also be called by QThread::terminate (as well as wait() and
279 setTerminationEnabled) to give Qt a chance to update the terminated thread's state and
280 process pending DeleteLater events for objects that live in the terminated thread. And for
281 adopted thread, this method is called by the thread watcher.
282
283 In those cases, \a arg will not be the current thread.
284*/
285void QThreadPrivate::finish(void *arg, bool lockAnyway) noexcept
286{
287 QThread *thr = reinterpret_cast<QThread *>(arg);
288 QThreadPrivate *d = thr->d_func();
289
290 QMutexLocker locker(lockAnyway ? &d->mutex : nullptr);
291 d->isInFinish = true;
292 d->priority = QThread::InheritPriority;
293 void **tls_data = reinterpret_cast<void **>(&d->data->tls);
294 if (lockAnyway)
295 locker.unlock();
296 emit thr->finished(QThread::QPrivateSignal());
297 qCDebug(lcDeleteLater) << "Sending deferred delete events as part of finishing thread" << thr;
299 QThreadStorageData::finish(tls_data);
300 if (lockAnyway)
301 locker.relock();
302
303 QAbstractEventDispatcher *eventDispatcher = d->data->eventDispatcher.loadRelaxed();
304 if (eventDispatcher) {
305 d->data->eventDispatcher = 0;
306 if (lockAnyway)
307 locker.unlock();
308 eventDispatcher->closingDown();
309 delete eventDispatcher;
310 if (lockAnyway)
311 locker.relock();
312 }
313
314 d->running = false;
315 d->finished = true;
316 d->isInFinish = false;
317 d->interruptionRequested.store(false, std::memory_order_relaxed);
318
319 if (!d->waiters) {
320 CloseHandle(d->handle);
321 d->handle = 0;
322 }
323
324 d->id = 0;
325}
326
327/**************************************************************************
328 ** QThread
329 *************************************************************************/
330
331Qt::HANDLE QThread::currentThreadIdImpl() noexcept
332{
333 return reinterpret_cast<Qt::HANDLE>(quintptr(GetCurrentThreadId()));
334}
335
336int QThread::idealThreadCount() noexcept
337{
338 SYSTEM_INFO sysinfo;
339 GetSystemInfo(&sysinfo);
340 return sysinfo.dwNumberOfProcessors;
341}
342
344{
345 SwitchToThread();
346}
347
348#endif // QT_CONFIG(thread)
349
350void QThread::sleep(std::chrono::nanoseconds nsecs)
351{
352 using namespace std::chrono;
353 ::Sleep(DWORD(duration_cast<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((usecs / 1000) + 1);
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->isInFinish) {
379 locker.unlock();
380 wait();
381 locker.relock();
382 }
383
384 if (d->running)
385 return;
386
387 // avoid interacting with the binding system
388 d->objectName = d->extraData ? d->extraData->objectName.valueBypassingBindings()
389 : QString();
390 d->running = true;
391 d->finished = false;
392 d->exited = false;
393 d->returnCode = 0;
394 d->interruptionRequested.store(false, std::memory_order_relaxed);
395
396 /*
397 NOTE: we create the thread in the suspended state, set the
398 priority and then resume the thread.
399
400 since threads are created with normal priority by default, we
401 could get into a case where a thread (with priority less than
402 NormalPriority) tries to create a new thread (also with priority
403 less than NormalPriority), but the newly created thread preempts
404 its 'parent' and runs at normal priority.
405 */
406#if defined(Q_CC_MSVC) && !defined(_DLL)
407 // MSVC -MT or -MTd build
408 d->handle = (Qt::HANDLE) _beginthreadex(NULL, d->stackSize, QThreadPrivate::start,
409 this, CREATE_SUSPENDED, &(d->id));
410#else
411 // MSVC -MD or -MDd or MinGW build
412 d->handle = CreateThread(nullptr, d->stackSize,
413 reinterpret_cast<LPTHREAD_START_ROUTINE>(QThreadPrivate::start),
414 this, CREATE_SUSPENDED, reinterpret_cast<LPDWORD>(&d->id));
415#endif
416
417 if (!d->handle) {
418 qErrnoWarning("QThread::start: Failed to create thread");
419 d->running = false;
420 d->finished = true;
421 return;
422 }
423
424 int prio;
425 d->priority = priority;
426 switch (priority) {
427 case IdlePriority:
428 prio = THREAD_PRIORITY_IDLE;
429 break;
430
431 case LowestPriority:
432 prio = THREAD_PRIORITY_LOWEST;
433 break;
434
435 case LowPriority:
436 prio = THREAD_PRIORITY_BELOW_NORMAL;
437 break;
438
439 case NormalPriority:
440 prio = THREAD_PRIORITY_NORMAL;
441 break;
442
443 case HighPriority:
444 prio = THREAD_PRIORITY_ABOVE_NORMAL;
445 break;
446
447 case HighestPriority:
448 prio = THREAD_PRIORITY_HIGHEST;
449 break;
450
452 prio = THREAD_PRIORITY_TIME_CRITICAL;
453 break;
454
455 case InheritPriority:
456 default:
457 prio = GetThreadPriority(GetCurrentThread());
458 break;
459 }
460
461 if (!SetThreadPriority(d->handle, prio)) {
462 qErrnoWarning("QThread::start: Failed to set thread priority");
463 }
464
465 if (ResumeThread(d->handle) == (DWORD) -1) {
466 qErrnoWarning("QThread::start: Failed to resume new thread");
467 }
468}
469
471{
472 Q_D(QThread);
473 QMutexLocker locker(&d->mutex);
474 if (!d->running)
475 return;
476 if (!d->terminationEnabled) {
477 d->terminatePending = true;
478 return;
479 }
480
481 TerminateThread(d->handle, 0);
482 QThreadPrivate::finish(this, false);
483}
484
486{
487 Q_D(QThread);
488 QMutexLocker locker(&d->mutex);
489
490 if (d->id == GetCurrentThreadId()) {
491 qWarning("QThread::wait: Thread tried to wait on itself");
492 return false;
493 }
494 if (d->finished || !d->running)
495 return true;
496
497 ++d->waiters;
498 locker.mutex()->unlock();
499
500 bool ret = false;
501 switch (WaitForSingleObject(d->handle, deadline.remainingTime())) {
502 case WAIT_OBJECT_0:
503 ret = true;
504 break;
505 case WAIT_FAILED:
506 qErrnoWarning("QThread::wait: Thread wait failure");
507 break;
508 case WAIT_ABANDONED:
509 case WAIT_TIMEOUT:
510 default:
511 break;
512 }
513
514 locker.mutex()->lock();
515 --d->waiters;
516
517 if (ret && !d->finished) {
518 // thread was terminated by someone else
519
520 QThreadPrivate::finish(this, false);
521 }
522
523 if (d->finished && !d->waiters) {
524 CloseHandle(d->handle);
525 d->handle = 0;
526 }
527
528 return ret;
529}
530
532{
533 QThread *thr = currentThread();
534 Q_ASSERT_X(thr != 0, "QThread::setTerminationEnabled()",
535 "Current thread was not started with QThread.");
536 QThreadPrivate *d = thr->d_func();
537 QMutexLocker locker(&d->mutex);
538 d->terminationEnabled = enabled;
539 if (enabled && d->terminatePending) {
540 QThreadPrivate::finish(thr, false);
541 locker.unlock(); // don't leave the mutex locked!
542 _endthreadex(0);
543 }
544}
545
546// Caller must hold the mutex
547void QThreadPrivate::setPriority(QThread::Priority threadPriority)
548{
549 // copied from start() with a few modifications:
550
551 int prio;
552 priority = threadPriority;
553 switch (threadPriority) {
555 prio = THREAD_PRIORITY_IDLE;
556 break;
557
559 prio = THREAD_PRIORITY_LOWEST;
560 break;
561
563 prio = THREAD_PRIORITY_BELOW_NORMAL;
564 break;
565
567 prio = THREAD_PRIORITY_NORMAL;
568 break;
569
571 prio = THREAD_PRIORITY_ABOVE_NORMAL;
572 break;
573
575 prio = THREAD_PRIORITY_HIGHEST;
576 break;
577
579 prio = THREAD_PRIORITY_TIME_CRITICAL;
580 break;
581
582 default:
583 return;
584 }
585
586 if (!SetThreadPriority(handle, prio)) {
587 qErrnoWarning("QThread::setPriority: Failed to set thread priority");
588 }
589}
590
591#endif // QT_CONFIG(thread)
592
Type loadRelaxed() const noexcept
void storeRelaxed(Type newValue) noexcept
static void sendPostedEvents(QObject *receiver, int event_type, QThreadData *data)
static QBasicAtomicPointer< void > theMainThreadId
static QBasicAtomicPointer< QThread > theMainThread
\inmodule QtCore
qint64 remainingTime() const noexcept
Returns the remaining time in this QDeadlineTimer object in milliseconds.
@ DeferredDelete
Definition qcoreevent.h:100
\inmodule QtCore
Definition qmutex.h:313
void unlock() noexcept
Unlocks this mutex locker.
Definition qmutex.h:319
\inmodule QtCore
Definition qmutex.h:281
static QObjectPrivate * get(QObject *o)
Definition qobject_p.h:150
QThread * thread() const
Returns the thread in which the object lives.
Definition qobject.cpp:1598
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
const ushort * utf16() const
Returns the QString as a '\0\'-terminated array of unsigned shorts.
Definition qstring.cpp:6995
bool isEmpty() const noexcept
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:192
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:6018
static Q_AUTOTEST_EXPORT QThreadData * current(bool createIfNecessary=true)
Definition qthread.cpp:1087
QThreadData(int initialRefCount=1)
Definition qthread.cpp:48
void deref()
Definition qthread.cpp:105
static void clearCurrentThreadData()
Definition qthread.cpp:1103
bool isAdopted
Definition qthread_p.h:330
QAtomicPointer< void > threadId
Definition qthread_p.h:324
static QThreadData * get2(QThread *thread)
Definition qthread_p.h:291
QAtomicPointer< QThread > thread
Definition qthread_p.h:323
static QAbstractEventDispatcher * createEventDispatcher(QThreadData *data)
void terminate()
Definition qthread.cpp:1003
void start(Priority=InheritPriority)
Definition qthread.cpp:996
static int idealThreadCount() noexcept
Definition qthread.cpp:1049
static QThread * currentThread()
Definition qthread.cpp:1039
static void setTerminationEnabled(bool enabled=true)
Definition qthread.cpp:1080
@ LowPriority
Definition qthread.h:44
@ LowestPriority
Definition qthread.h:43
@ TimeCriticalPriority
Definition qthread.h:49
@ HighestPriority
Definition qthread.h:47
@ InheritPriority
Definition qthread.h:51
@ IdlePriority
Definition qthread.h:41
@ NormalPriority
Definition qthread.h:45
@ HighPriority
Definition qthread.h:46
static void yieldCurrentThread()
Definition qthread.cpp:1054
bool wait(QDeadlineTimer deadline=QDeadlineTimer(QDeadlineTimer::Forever))
Definition qthread.cpp:1023
static void usleep(unsigned long)
Priority priority() const
static void msleep(unsigned long)
static void sleep(unsigned long)
void qErrnoWarning(const char *msg,...)
Combined button and popup list for selecting options.
void * HANDLE
#define Q_LIKELY(x)
#define QT_RETHROW
#define QT_CATCH(A)
#define QT_TRY
#define forever
Definition qforeach.h:78
#define qWarning
Definition qlogging.h:166
#define qCDebug(category,...)
return ret
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
GLuint64 GLenum void * handle
GLenum GLenum GLsizei count
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLenum GLenum GLsizei const GLuint GLboolean enabled
GLenum GLuint GLintptr offset
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define Q_ASSERT_X(cond, x, msg)
Definition qrandom.cpp:48
SSL_CTX int void * arg
WINBASEAPI HRESULT WINAPI SetThreadDescription(_In_ HANDLE hThread, _In_ PCWSTR lpThreadDescription)
#define emit
#define Q_UNUSED(x)
size_t quintptr
Definition qtypes.h:167
unsigned int uint
Definition qtypes.h:34
long HRESULT
#define enabled
QDeadlineTimer deadline(30s)
QObject::connect nullptr
QMutex mutex
[2]
QReadWriteLock lock
[0]