Qt 6.x
The Qt SDK
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
23
24#if QT_CONFIG(thread)
25
26void qt_watch_adopted_thread(const HANDLE adoptedThreadHandle, QThread *qthread);
27DWORD WINAPI qt_adopted_thread_watcher_function(LPVOID);
28
29static DWORD qt_current_thread_data_tls_index = TLS_OUT_OF_INDEXES;
30void qt_create_tls()
31{
32 if (qt_current_thread_data_tls_index != TLS_OUT_OF_INDEXES)
33 return;
34 Q_CONSTINIT static QBasicMutex mutex;
35 QMutexLocker locker(&mutex);
36 if (qt_current_thread_data_tls_index != TLS_OUT_OF_INDEXES)
37 return;
38 qt_current_thread_data_tls_index = TlsAlloc();
39}
40
41static void qt_free_tls()
42{
43 if (qt_current_thread_data_tls_index != TLS_OUT_OF_INDEXES) {
44 TlsFree(qt_current_thread_data_tls_index);
45 qt_current_thread_data_tls_index = TLS_OUT_OF_INDEXES;
46 }
47}
48Q_DESTRUCTOR_FUNCTION(qt_free_tls)
49
50/*
51 QThreadData
52*/
54{
55 TlsSetValue(qt_current_thread_data_tls_index, 0);
56}
57
58QThreadData *QThreadData::current(bool createIfNecessary)
59{
60 qt_create_tls();
61 QThreadData *threadData = reinterpret_cast<QThreadData *>(TlsGetValue(qt_current_thread_data_tls_index));
62 if (!threadData && createIfNecessary) {
63 threadData = new QThreadData;
64 // This needs to be called prior to new AdoptedThread() to
65 // avoid recursion.
66 TlsSetValue(qt_current_thread_data_tls_index, threadData);
67 QT_TRY {
68 threadData->thread = new QAdoptedThread(threadData);
69 } QT_CATCH(...) {
70 TlsSetValue(qt_current_thread_data_tls_index, 0);
71 threadData->deref();
72 threadData = 0;
74 }
75 threadData->deref();
76 threadData->isAdopted = true;
77 threadData->threadId.storeRelaxed(reinterpret_cast<Qt::HANDLE>(quintptr(GetCurrentThreadId())));
78
81 } else {
82 HANDLE realHandle = INVALID_HANDLE_VALUE;
83 DuplicateHandle(GetCurrentProcess(),
84 GetCurrentThread(),
85 GetCurrentProcess(),
86 &realHandle,
87 0,
88 FALSE,
89 DUPLICATE_SAME_ACCESS);
90 qt_watch_adopted_thread(realHandle, threadData->thread);
91 }
92 }
93 return threadData;
94}
95
97{
98 d_func()->handle = GetCurrentThread();
99 d_func()->id = GetCurrentThreadId();
100}
101
102static QList<HANDLE> qt_adopted_thread_handles;
103static QList<QThread *> qt_adopted_qthreads;
104Q_CONSTINIT static QBasicMutex qt_adopted_thread_watcher_mutex;
105static DWORD qt_adopted_thread_watcher_id = 0;
106static HANDLE qt_adopted_thread_wakeup = 0;
107
114void qt_watch_adopted_thread(const HANDLE adoptedThreadHandle, QThread *qthread)
115{
116 QMutexLocker lock(&qt_adopted_thread_watcher_mutex);
117
118 if (GetCurrentThreadId() == qt_adopted_thread_watcher_id) {
119 CloseHandle(adoptedThreadHandle);
120 return;
121 }
122
123 qt_adopted_thread_handles.append(adoptedThreadHandle);
124 qt_adopted_qthreads.append(qthread);
125
126 // Start watcher thread if it is not already running.
127 if (qt_adopted_thread_watcher_id == 0) {
128 if (qt_adopted_thread_wakeup == 0) {
129 qt_adopted_thread_wakeup = CreateEvent(0, false, false, 0);
130 qt_adopted_thread_handles.prepend(qt_adopted_thread_wakeup);
131 }
132
133 CloseHandle(CreateThread(0, 0, qt_adopted_thread_watcher_function, 0, 0, &qt_adopted_thread_watcher_id));
134 } else {
135 SetEvent(qt_adopted_thread_wakeup);
136 }
137}
138
139/*
140 This function loops and waits for native adopted threads to finish.
141 When this happens it derefs the QThreadData for the adopted thread
142 to make sure it gets cleaned up properly.
143*/
144DWORD WINAPI qt_adopted_thread_watcher_function(LPVOID)
145{
146 forever {
147 qt_adopted_thread_watcher_mutex.lock();
148
149 if (qt_adopted_thread_handles.count() == 1) {
150 qt_adopted_thread_watcher_id = 0;
151 qt_adopted_thread_watcher_mutex.unlock();
152 break;
153 }
154
155 QList<HANDLE> handlesCopy = qt_adopted_thread_handles;
156 qt_adopted_thread_watcher_mutex.unlock();
157
158 DWORD ret = WAIT_TIMEOUT;
159 int count;
160 int offset;
161 int loops = handlesCopy.size() / MAXIMUM_WAIT_OBJECTS;
162 if (handlesCopy.size() % MAXIMUM_WAIT_OBJECTS)
163 ++loops;
164 if (loops == 1) {
165 // no need to loop, no timeout
166 offset = 0;
167 count = handlesCopy.count();
168 ret = WaitForMultipleObjects(handlesCopy.count(), handlesCopy.constData(), false, INFINITE);
169 } else {
170 int loop = 0;
171 do {
172 offset = loop * MAXIMUM_WAIT_OBJECTS;
173 count = qMin(handlesCopy.count() - offset, MAXIMUM_WAIT_OBJECTS);
174 ret = WaitForMultipleObjects(count, handlesCopy.constData() + offset, false, 100);
175 loop = (loop + 1) % loops;
176 } while (ret == WAIT_TIMEOUT);
177 }
178
179 if (ret == WAIT_FAILED || ret >= WAIT_OBJECT_0 + uint(count)) {
180 qWarning("QThread internal error while waiting for adopted threads: %d", int(GetLastError()));
181 continue;
182 }
183
184 const int handleIndex = offset + ret - WAIT_OBJECT_0;
185 if (handleIndex == 0) // New handle to watch was added.
186 continue;
187 const int qthreadIndex = handleIndex - 1;
188
189 qt_adopted_thread_watcher_mutex.lock();
190 QThreadData *data = QThreadData::get2(qt_adopted_qthreads.at(qthreadIndex));
191 qt_adopted_thread_watcher_mutex.unlock();
192 if (data->isAdopted) {
193 QThread *thread = data->thread;
194 Q_ASSERT(thread);
195 auto thread_p = static_cast<QThreadPrivate *>(QObjectPrivate::get(thread));
196 Q_UNUSED(thread_p);
197 Q_ASSERT(!thread_p->finished);
198 QThreadPrivate::finish(thread);
199 }
200 data->deref();
201
202 QMutexLocker lock(&qt_adopted_thread_watcher_mutex);
203 CloseHandle(qt_adopted_thread_handles.at(handleIndex));
204 qt_adopted_thread_handles.remove(handleIndex);
205 qt_adopted_qthreads.remove(qthreadIndex);
206 }
207
208 QThreadData *threadData = reinterpret_cast<QThreadData *>(TlsGetValue(qt_current_thread_data_tls_index));
209 if (threadData)
210 threadData->deref();
211
212 return 0;
213}
214
215#if !defined(QT_NO_DEBUG) && defined(Q_CC_MSVC)
216
217#ifndef Q_OS_WIN64
218# define ULONG_PTR DWORD
219#endif
220
221typedef struct tagTHREADNAME_INFO
222{
223 DWORD dwType; // must be 0x1000
224 LPCSTR szName; // pointer to name (in user addr space)
225 HANDLE dwThreadID; // thread ID (-1=caller thread)
226 DWORD dwFlags; // reserved for future use, must be zero
227} THREADNAME_INFO;
228
229void qt_set_thread_name(HANDLE threadId, LPCSTR threadName)
230{
231 THREADNAME_INFO info;
232 info.dwType = 0x1000;
233 info.szName = threadName;
234 info.dwThreadID = threadId;
235 info.dwFlags = 0;
236
237 __try
238 {
239 RaiseException(0x406D1388, 0, sizeof(info)/sizeof(DWORD),
240 reinterpret_cast<const ULONG_PTR*>(&info));
241 }
242 __except (EXCEPTION_CONTINUE_EXECUTION)
243 {
244 }
245}
246#endif // !QT_NO_DEBUG && Q_CC_MSVC
247
248/**************************************************************************
249 ** QThreadPrivate
250 *************************************************************************/
251
252#endif // QT_CONFIG(thread)
253
255{
256 Q_UNUSED(data);
257 return new QEventDispatcherWin32;
258}
259
260#if QT_CONFIG(thread)
261
262unsigned int __stdcall QT_ENSURE_STACK_ALIGNED_FOR_SSE QThreadPrivate::start(void *arg) noexcept
263{
264 QThread *thr = reinterpret_cast<QThread *>(arg);
266
267 qt_create_tls();
268 TlsSetValue(qt_current_thread_data_tls_index, data);
269 data->threadId.storeRelaxed(reinterpret_cast<Qt::HANDLE>(quintptr(GetCurrentThreadId())));
270
272
273 {
274 QMutexLocker locker(&thr->d_func()->mutex);
275 data->quitNow = thr->d_func()->exited;
276 }
277
278 data->ensureEventDispatcher();
279 data->eventDispatcher.loadRelaxed()->startingUp();
280
281#if !defined(QT_NO_DEBUG) && defined(Q_CC_MSVC)
282 // sets the name of the current thread.
283 qt_set_thread_name(HANDLE(-1), thr->d_func()->objectName.isEmpty()
284 ? thr->metaObject()->className()
285 : std::exchange(thr->d_func()->objectName, {}).toLocal8Bit().constData());
286#endif
287
288 emit thr->started(QThread::QPrivateSignal());
290 thr->run();
291
292 finish(arg);
293 return 0;
294}
295
296/*
297 For regularly terminating threads, this will be called and executed by the thread as the
298 last code before the thread exits. In that case, \a arg is the current QThread.
299
300 However, this function will also be called by QThread::terminate (as well as wait() and
301 setTerminationEnabled) to give Qt a chance to update the terminated thread's state and
302 process pending DeleteLater events for objects that live in the terminated thread. And for
303 adopted thread, this method is called by the thread watcher.
304
305 In those cases, \a arg will not be the current thread.
306*/
307void QThreadPrivate::finish(void *arg, bool lockAnyway) noexcept
308{
309 QThread *thr = reinterpret_cast<QThread *>(arg);
310 QThreadPrivate *d = thr->d_func();
311
312 QMutexLocker locker(lockAnyway ? &d->mutex : nullptr);
313 d->isInFinish = true;
314 d->priority = QThread::InheritPriority;
315 void **tls_data = reinterpret_cast<void **>(&d->data->tls);
316 if (lockAnyway)
317 locker.unlock();
318 emit thr->finished(QThread::QPrivateSignal());
320 QThreadStorageData::finish(tls_data);
321 if (lockAnyway)
322 locker.relock();
323
324 QAbstractEventDispatcher *eventDispatcher = d->data->eventDispatcher.loadRelaxed();
325 if (eventDispatcher) {
326 d->data->eventDispatcher = 0;
327 if (lockAnyway)
328 locker.unlock();
329 eventDispatcher->closingDown();
330 delete eventDispatcher;
331 if (lockAnyway)
332 locker.relock();
333 }
334
335 d->running = false;
336 d->finished = true;
337 d->isInFinish = false;
338 d->interruptionRequested = false;
339
340 if (!d->waiters) {
341 CloseHandle(d->handle);
342 d->handle = 0;
343 }
344
345 d->id = 0;
346}
347
348/**************************************************************************
349 ** QThread
350 *************************************************************************/
351
352Qt::HANDLE QThread::currentThreadIdImpl() noexcept
353{
354 return reinterpret_cast<Qt::HANDLE>(quintptr(GetCurrentThreadId()));
355}
356
357int QThread::idealThreadCount() noexcept
358{
359 SYSTEM_INFO sysinfo;
360 GetSystemInfo(&sysinfo);
361 return sysinfo.dwNumberOfProcessors;
362}
363
365{
366 SwitchToThread();
367}
368
369#endif // QT_CONFIG(thread)
370
371void QThread::sleep(std::chrono::nanoseconds nsecs)
372{
373 using namespace std::chrono;
374 ::Sleep(DWORD(duration_cast<milliseconds>(nsecs).count()));
375}
376
377void QThread::sleep(unsigned long secs)
378{
379 ::Sleep(secs * 1000);
380}
381
382void QThread::msleep(unsigned long msecs)
383{
384 ::Sleep(msecs);
385}
386
387void QThread::usleep(unsigned long usecs)
388{
389 ::Sleep((usecs / 1000) + 1);
390}
391
392#if QT_CONFIG(thread)
393
394void QThread::start(Priority priority)
395{
396 Q_D(QThread);
397 QMutexLocker locker(&d->mutex);
398
399 if (d->isInFinish) {
400 locker.unlock();
401 wait();
402 locker.relock();
403 }
404
405 if (d->running)
406 return;
407
408 // avoid interacting with the binding system
409 d->objectName = d->extraData ? d->extraData->objectName.valueBypassingBindings()
410 : QString();
411 d->running = true;
412 d->finished = false;
413 d->exited = false;
414 d->returnCode = 0;
415 d->interruptionRequested = false;
416
417 /*
418 NOTE: we create the thread in the suspended state, set the
419 priority and then resume the thread.
420
421 since threads are created with normal priority by default, we
422 could get into a case where a thread (with priority less than
423 NormalPriority) tries to create a new thread (also with priority
424 less than NormalPriority), but the newly created thread preempts
425 its 'parent' and runs at normal priority.
426 */
427#if defined(Q_CC_MSVC) && !defined(_DLL)
428 // MSVC -MT or -MTd build
429 d->handle = (Qt::HANDLE) _beginthreadex(NULL, d->stackSize, QThreadPrivate::start,
430 this, CREATE_SUSPENDED, &(d->id));
431#else
432 // MSVC -MD or -MDd or MinGW build
433 d->handle = CreateThread(nullptr, d->stackSize,
434 reinterpret_cast<LPTHREAD_START_ROUTINE>(QThreadPrivate::start),
435 this, CREATE_SUSPENDED, reinterpret_cast<LPDWORD>(&d->id));
436#endif
437
438 if (!d->handle) {
439 qErrnoWarning("QThread::start: Failed to create thread");
440 d->running = false;
441 d->finished = true;
442 return;
443 }
444
445 int prio;
446 d->priority = priority;
447 switch (priority) {
448 case IdlePriority:
449 prio = THREAD_PRIORITY_IDLE;
450 break;
451
452 case LowestPriority:
453 prio = THREAD_PRIORITY_LOWEST;
454 break;
455
456 case LowPriority:
457 prio = THREAD_PRIORITY_BELOW_NORMAL;
458 break;
459
460 case NormalPriority:
461 prio = THREAD_PRIORITY_NORMAL;
462 break;
463
464 case HighPriority:
465 prio = THREAD_PRIORITY_ABOVE_NORMAL;
466 break;
467
468 case HighestPriority:
469 prio = THREAD_PRIORITY_HIGHEST;
470 break;
471
473 prio = THREAD_PRIORITY_TIME_CRITICAL;
474 break;
475
476 case InheritPriority:
477 default:
478 prio = GetThreadPriority(GetCurrentThread());
479 break;
480 }
481
482 if (!SetThreadPriority(d->handle, prio)) {
483 qErrnoWarning("QThread::start: Failed to set thread priority");
484 }
485
486 if (ResumeThread(d->handle) == (DWORD) -1) {
487 qErrnoWarning("QThread::start: Failed to resume new thread");
488 }
489}
490
492{
493 Q_D(QThread);
494 QMutexLocker locker(&d->mutex);
495 if (!d->running)
496 return;
497 if (!d->terminationEnabled) {
498 d->terminatePending = true;
499 return;
500 }
501
502 TerminateThread(d->handle, 0);
503 QThreadPrivate::finish(this, false);
504}
505
507{
508 Q_D(QThread);
509 QMutexLocker locker(&d->mutex);
510
511 if (d->id == GetCurrentThreadId()) {
512 qWarning("QThread::wait: Thread tried to wait on itself");
513 return false;
514 }
515 if (d->finished || !d->running)
516 return true;
517
518 ++d->waiters;
519 locker.mutex()->unlock();
520
521 bool ret = false;
522 switch (WaitForSingleObject(d->handle, deadline.remainingTime())) {
523 case WAIT_OBJECT_0:
524 ret = true;
525 break;
526 case WAIT_FAILED:
527 qErrnoWarning("QThread::wait: Thread wait failure");
528 break;
529 case WAIT_ABANDONED:
530 case WAIT_TIMEOUT:
531 default:
532 break;
533 }
534
535 locker.mutex()->lock();
536 --d->waiters;
537
538 if (ret && !d->finished) {
539 // thread was terminated by someone else
540
541 QThreadPrivate::finish(this, false);
542 }
543
544 if (d->finished && !d->waiters) {
545 CloseHandle(d->handle);
546 d->handle = 0;
547 }
548
549 return ret;
550}
551
553{
554 QThread *thr = currentThread();
555 Q_ASSERT_X(thr != 0, "QThread::setTerminationEnabled()",
556 "Current thread was not started with QThread.");
557 QThreadPrivate *d = thr->d_func();
558 QMutexLocker locker(&d->mutex);
559 d->terminationEnabled = enabled;
560 if (enabled && d->terminatePending) {
561 QThreadPrivate::finish(thr, false);
562 locker.unlock(); // don't leave the mutex locked!
563 _endthreadex(0);
564 }
565}
566
567// Caller must hold the mutex
568void QThreadPrivate::setPriority(QThread::Priority threadPriority)
569{
570 // copied from start() with a few modifications:
571
572 int prio;
573 priority = threadPriority;
574 switch (threadPriority) {
576 prio = THREAD_PRIORITY_IDLE;
577 break;
578
580 prio = THREAD_PRIORITY_LOWEST;
581 break;
582
584 prio = THREAD_PRIORITY_BELOW_NORMAL;
585 break;
586
588 prio = THREAD_PRIORITY_NORMAL;
589 break;
590
592 prio = THREAD_PRIORITY_ABOVE_NORMAL;
593 break;
594
596 prio = THREAD_PRIORITY_HIGHEST;
597 break;
598
600 prio = THREAD_PRIORITY_TIME_CRITICAL;
601 break;
602
603 default:
604 return;
605 }
606
607 if (!SetThreadPriority(handle, prio)) {
608 qErrnoWarning("QThread::setPriority: Failed to set thread priority");
609 }
610}
611
612#endif // QT_CONFIG(thread)
613
Type loadRelaxed() const noexcept
void storeRelaxed(Type newValue) noexcept
static void sendPostedEvents(QObject *receiver, int event_type, QThreadData *data)
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
Definition qlist.h:74
qsizetype size() const noexcept
Definition qlist.h:386
const_pointer constData() const noexcept
Definition qlist.h:416
const_reference at(qsizetype i) const noexcept
Definition qlist.h:429
void remove(qsizetype i, qsizetype n=1)
Definition qlist.h:787
qsizetype count() const noexcept
Definition qlist.h:387
void prepend(rvalue_ref t)
Definition qlist.h:456
void append(parameter_type t)
Definition qlist.h:441
\inmodule QtCore
Definition qmutex.h:317
\inmodule QtCore
Definition qmutex.h:285
static QObjectPrivate * get(QObject *o)
Definition qobject_p.h:153
QString objectName
the name of this object
Definition qobject.h:94
QThread * thread() const
Returns the thread in which the object lives.
Definition qobject.cpp:1561
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
bool isEmpty() const
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:1083
static Q_AUTOTEST_EXPORT QThreadData * current(bool createIfNecessary=true)
Definition qthread.cpp:1005
void deref()
Definition qthread.cpp:104
static void clearCurrentThreadData()
Definition qthread.cpp:1019
bool isAdopted
Definition qthread_p.h:329
QAtomicPointer< void > threadId
Definition qthread_p.h:323
static QThreadData * get2(QThread *thread)
Definition qthread_p.h:290
QAtomicPointer< QThread > thread
Definition qthread_p.h:322
static QAbstractEventDispatcher * createEventDispatcher(QThreadData *data)
void terminate()
Definition qthread.cpp:930
void start(Priority=InheritPriority)
Definition qthread.cpp:923
QAbstractEventDispatcher * eventDispatcher() const
Definition qthread.cpp:1064
static int idealThreadCount() noexcept
Definition qthread.cpp:971
static void setTerminationEnabled(bool enabled=true)
virtual void run()
Definition qthread.cpp:913
static QThread * currentThread()
Definition qthread.cpp:966
@ LowPriority
Definition qthread.h:45
@ LowestPriority
Definition qthread.h:44
@ TimeCriticalPriority
Definition qthread.h:50
@ HighestPriority
Definition qthread.h:48
@ InheritPriority
Definition qthread.h:52
@ IdlePriority
Definition qthread.h:42
@ NormalPriority
Definition qthread.h:46
@ HighPriority
Definition qthread.h:47
static void yieldCurrentThread()
Definition qthread.cpp:976
bool wait(QDeadlineTimer deadline=QDeadlineTimer(QDeadlineTimer::Forever))
Definition qthread.cpp:950
static void usleep(unsigned long)
Priority priority() const
void finished(QPrivateSignal)
static void msleep(unsigned long)
static void sleep(unsigned long)
void started(QPrivateSignal)
void qErrnoWarning(const char *msg,...)
Combined button and popup list for selecting options.
void * HANDLE
#define QT_RETHROW
#define QT_CATCH(A)
#define QT_TRY
#define forever
Definition qforeach.h:78
#define qWarning
Definition qlogging.h:162
return ret
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
GLuint64 GLenum void * handle
GLenum GLenum GLsizei count
GLenum GLenum GLsizei const GLuint GLboolean enabled
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
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)
static char * toLocal8Bit(char *out, QStringView in, QStringConverter::State *state)
#define emit
#define Q_UNUSED(x)
size_t quintptr
Definition qtypes.h:72
unsigned int uint
Definition qtypes.h:29
#define enabled
QFileInfo info(fileName)
[8]
QDeadlineTimer deadline(30s)
QObject::connect nullptr
QMutex mutex
[2]
QReadWriteLock lock
[0]