1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtCore module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #include "qthread.h"
41 #include "qthread_p.h"
42 #include "qthreadstorage.h"
43 #include "qmutex.h"
44 
45 #include <qcoreapplication.h>
46 #include <qpointer.h>
47 
48 #include <private/qcoreapplication_p.h>
49 #ifndef Q_OS_WINRT
50 #include <private/qeventdispatcher_win_p.h>
51 #else
52 #include <private/qeventdispatcher_winrt_p.h>
53 #endif
54 
55 #include <qt_windows.h>
56 
57 #ifndef Q_OS_WINRT
58 #  ifndef _MT
59 #    define _MT
60 #  endif // _MT
61 #  include <process.h>
62 #endif // Q_OS_WINRT
63 
64 QT_BEGIN_NAMESPACE
65 
66 #if QT_CONFIG(thread)
67 
68 #ifdef Q_OS_WINRT
qWinRTTlsAlloc()69 inline DWORD qWinRTTlsAlloc() {
70     return FlsAlloc(0);
71 }
72 
qWinRTTlsFree(DWORD dwTlsIndex)73 inline bool qWinRTTlsFree(DWORD dwTlsIndex) {
74     return FlsFree(dwTlsIndex);
75 }
76 
qWinRTTlsGetValue(DWORD dwTlsIndex)77 inline LPVOID qWinRTTlsGetValue(DWORD dwTlsIndex) {
78     return FlsGetValue(dwTlsIndex);
79 }
80 
qWinRTTlsSetValue(DWORD dwTlsIndex,LPVOID lpTlsValue)81 inline bool qWinRTTlsSetValue(DWORD dwTlsIndex, LPVOID lpTlsValue) {
82     return FlsSetValue(dwTlsIndex, lpTlsValue);
83 }
84 
85 #define TlsAlloc qWinRTTlsAlloc
86 #define TlsFree qWinRTTlsFree
87 #define TlsSetValue qWinRTTlsSetValue
88 #define TlsGetValue qWinRTTlsGetValue
89 
90 #endif // Q_OS_WINRT
91 
92 void qt_watch_adopted_thread(const HANDLE adoptedThreadHandle, QThread *qthread);
93 DWORD WINAPI qt_adopted_thread_watcher_function(LPVOID);
94 
95 static DWORD qt_current_thread_data_tls_index = TLS_OUT_OF_INDEXES;
qt_create_tls()96 void qt_create_tls()
97 {
98     if (qt_current_thread_data_tls_index != TLS_OUT_OF_INDEXES)
99         return;
100     static QBasicMutex mutex;
101     QMutexLocker locker(&mutex);
102     if (qt_current_thread_data_tls_index != TLS_OUT_OF_INDEXES)
103         return;
104     qt_current_thread_data_tls_index = TlsAlloc();
105 }
106 
qt_free_tls()107 static void qt_free_tls()
108 {
109     if (qt_current_thread_data_tls_index != TLS_OUT_OF_INDEXES) {
110         TlsFree(qt_current_thread_data_tls_index);
111         qt_current_thread_data_tls_index = TLS_OUT_OF_INDEXES;
112     }
113 }
Q_DESTRUCTOR_FUNCTION(qt_free_tls)114 Q_DESTRUCTOR_FUNCTION(qt_free_tls)
115 
116 /*
117     QThreadData
118 */
119 void QThreadData::clearCurrentThreadData()
120 {
121     TlsSetValue(qt_current_thread_data_tls_index, 0);
122 }
123 
current(bool createIfNecessary)124 QThreadData *QThreadData::current(bool createIfNecessary)
125 {
126     qt_create_tls();
127     QThreadData *threadData = reinterpret_cast<QThreadData *>(TlsGetValue(qt_current_thread_data_tls_index));
128     if (!threadData && createIfNecessary) {
129         threadData = new QThreadData;
130         // This needs to be called prior to new AdoptedThread() to
131         // avoid recursion.
132         TlsSetValue(qt_current_thread_data_tls_index, threadData);
133         QT_TRY {
134             threadData->thread = new QAdoptedThread(threadData);
135         } QT_CATCH(...) {
136             TlsSetValue(qt_current_thread_data_tls_index, 0);
137             threadData->deref();
138             threadData = 0;
139             QT_RETHROW;
140         }
141         threadData->deref();
142         threadData->isAdopted = true;
143         threadData->threadId.storeRelaxed(reinterpret_cast<Qt::HANDLE>(quintptr(GetCurrentThreadId())));
144 
145 #ifndef Q_OS_WINRT
146         if (!QCoreApplicationPrivate::theMainThread) {
147             QCoreApplicationPrivate::theMainThread = threadData->thread.loadRelaxed();
148         } else {
149 #else
150         // for winrt the main thread is set explicitly in QCoreApplication's constructor as the
151         // native main thread (Xaml thread) is not Qt's main thread.
152         {
153 #endif
154             HANDLE realHandle = INVALID_HANDLE_VALUE;
155             DuplicateHandle(GetCurrentProcess(),
156                     GetCurrentThread(),
157                     GetCurrentProcess(),
158                     &realHandle,
159                     0,
160                     FALSE,
161                     DUPLICATE_SAME_ACCESS);
162             qt_watch_adopted_thread(realHandle, threadData->thread);
163         }
164     }
165     return threadData;
166 }
167 
168 #ifdef Q_OS_WINRT
169 void QThreadData::setMainThread()
170 {
171     Q_ASSERT(!QCoreApplicationPrivate::theMainThread);
172     qt_create_tls();
173     QThreadData *threadData = reinterpret_cast<QThreadData *>(TlsGetValue(qt_current_thread_data_tls_index));
174     if (!threadData) {
175         threadData = new QThreadData;
176         // This needs to be called prior to new AdoptedThread() to
177         // avoid recursion.
178         TlsSetValue(qt_current_thread_data_tls_index, threadData);
179         QT_TRY {
180             threadData->thread = new QAdoptedThread(threadData);
181         } QT_CATCH(...) {
182             TlsSetValue(qt_current_thread_data_tls_index, 0);
183             threadData->deref();
184             threadData = 0;
185             QT_RETHROW;
186         }
187         threadData->deref();
188         threadData->isAdopted = true;
189         threadData->threadId.storeRelaxed(reinterpret_cast<Qt::HANDLE>(quintptr(GetCurrentThreadId())));
190     }
191     QCoreApplicationPrivate::theMainThread = threadData->thread.loadRelaxed();
192 }
193 #endif
194 
195 void QAdoptedThread::init()
196 {
197     d_func()->handle = GetCurrentThread();
198     d_func()->id = GetCurrentThreadId();
199 }
200 
201 static QVector<HANDLE> qt_adopted_thread_handles;
202 static QVector<QThread *> qt_adopted_qthreads;
203 static QBasicMutex qt_adopted_thread_watcher_mutex;
204 static DWORD qt_adopted_thread_watcher_id = 0;
205 static HANDLE qt_adopted_thread_wakeup = 0;
206 
207 /*!
208     \internal
209     Adds an adopted thread to the list of threads that Qt watches to make sure
210     the thread data is properly cleaned up. This function starts the watcher
211     thread if necessary.
212 */
213 void qt_watch_adopted_thread(const HANDLE adoptedThreadHandle, QThread *qthread)
214 {
215     QMutexLocker lock(&qt_adopted_thread_watcher_mutex);
216 
217     if (GetCurrentThreadId() == qt_adopted_thread_watcher_id) {
218         CloseHandle(adoptedThreadHandle);
219         return;
220     }
221 
222     qt_adopted_thread_handles.append(adoptedThreadHandle);
223     qt_adopted_qthreads.append(qthread);
224 
225     // Start watcher thread if it is not already running.
226     if (qt_adopted_thread_watcher_id == 0) {
227         if (qt_adopted_thread_wakeup == 0) {
228 #ifndef Q_OS_WINRT
229             qt_adopted_thread_wakeup = CreateEvent(0, false, false, 0);
230 #else
231             qt_adopted_thread_wakeup = CreateEventEx(0, NULL, 0, EVENT_ALL_ACCESS);
232 #endif
233             qt_adopted_thread_handles.prepend(qt_adopted_thread_wakeup);
234         }
235 
236         CloseHandle(CreateThread(0, 0, qt_adopted_thread_watcher_function, 0, 0, &qt_adopted_thread_watcher_id));
237     } else {
238         SetEvent(qt_adopted_thread_wakeup);
239     }
240 }
241 
242 /*
243     This function loops and waits for native adopted threads to finish.
244     When this happens it derefs the QThreadData for the adopted thread
245     to make sure it gets cleaned up properly.
246 */
247 DWORD WINAPI qt_adopted_thread_watcher_function(LPVOID)
248 {
249     forever {
250         qt_adopted_thread_watcher_mutex.lock();
251 
252         if (qt_adopted_thread_handles.count() == 1) {
253             qt_adopted_thread_watcher_id = 0;
254             qt_adopted_thread_watcher_mutex.unlock();
255             break;
256         }
257 
258         QVector<HANDLE> handlesCopy = qt_adopted_thread_handles;
259         qt_adopted_thread_watcher_mutex.unlock();
260 
261         DWORD ret = WAIT_TIMEOUT;
262         int count;
263         int offset;
264         int loops = handlesCopy.size() / MAXIMUM_WAIT_OBJECTS;
265         if (handlesCopy.size() % MAXIMUM_WAIT_OBJECTS)
266             ++loops;
267         if (loops == 1) {
268             // no need to loop, no timeout
269             offset = 0;
270             count = handlesCopy.count();
271 #ifndef Q_OS_WINRT
272             ret = WaitForMultipleObjects(handlesCopy.count(), handlesCopy.constData(), false, INFINITE);
273 #else
274             ret = WaitForMultipleObjectsEx(handlesCopy.count(), handlesCopy.constData(), false, INFINITE, false);
275 #endif
276         } else {
277             int loop = 0;
278             do {
279                 offset = loop * MAXIMUM_WAIT_OBJECTS;
280                 count = qMin(handlesCopy.count() - offset, MAXIMUM_WAIT_OBJECTS);
281 #ifndef Q_OS_WINRT
282                 ret = WaitForMultipleObjects(count, handlesCopy.constData() + offset, false, 100);
283 #else
284                 ret = WaitForMultipleObjectsEx(count, handlesCopy.constData() + offset, false, 100, false);
285 #endif
286                 loop = (loop + 1) % loops;
287             } while (ret == WAIT_TIMEOUT);
288         }
289 
290         if (ret == WAIT_FAILED || ret >= WAIT_OBJECT_0 + uint(count)) {
291             qWarning("QThread internal error while waiting for adopted threads: %d", int(GetLastError()));
292             continue;
293         }
294 
295         const int handleIndex = offset + ret - WAIT_OBJECT_0;
296         if (handleIndex == 0) // New handle to watch was added.
297             continue;
298         const int qthreadIndex = handleIndex - 1;
299 
300         qt_adopted_thread_watcher_mutex.lock();
301         QThreadData *data = QThreadData::get2(qt_adopted_qthreads.at(qthreadIndex));
302         qt_adopted_thread_watcher_mutex.unlock();
303         if (data->isAdopted) {
304             QThread *thread = data->thread;
305             Q_ASSERT(thread);
306             auto thread_p = static_cast<QThreadPrivate *>(QObjectPrivate::get(thread));
307             Q_UNUSED(thread_p)
308             Q_ASSERT(!thread_p->finished);
309             QThreadPrivate::finish(thread);
310         }
311         data->deref();
312 
313         QMutexLocker lock(&qt_adopted_thread_watcher_mutex);
314         CloseHandle(qt_adopted_thread_handles.at(handleIndex));
315         qt_adopted_thread_handles.remove(handleIndex);
316         qt_adopted_qthreads.remove(qthreadIndex);
317     }
318 
319     QThreadData *threadData = reinterpret_cast<QThreadData *>(TlsGetValue(qt_current_thread_data_tls_index));
320     if (threadData)
321         threadData->deref();
322 
323     return 0;
324 }
325 
326 #if !defined(QT_NO_DEBUG) && defined(Q_CC_MSVC) && !defined(Q_OS_WINRT)
327 
328 #ifndef Q_OS_WIN64
329 #  define ULONG_PTR DWORD
330 #endif
331 
332 typedef struct tagTHREADNAME_INFO
333 {
334     DWORD dwType;      // must be 0x1000
335     LPCSTR szName;     // pointer to name (in user addr space)
336     HANDLE dwThreadID; // thread ID (-1=caller thread)
337     DWORD dwFlags;     // reserved for future use, must be zero
338 } THREADNAME_INFO;
339 
340 void qt_set_thread_name(HANDLE threadId, LPCSTR threadName)
341 {
342     THREADNAME_INFO info;
343     info.dwType = 0x1000;
344     info.szName = threadName;
345     info.dwThreadID = threadId;
346     info.dwFlags = 0;
347 
348     __try
349     {
350         RaiseException(0x406D1388, 0, sizeof(info)/sizeof(DWORD),
351                        reinterpret_cast<const ULONG_PTR*>(&info));
352     }
353     __except (EXCEPTION_CONTINUE_EXECUTION)
354     {
355     }
356 }
357 #endif // !QT_NO_DEBUG && Q_CC_MSVC && !Q_OS_WINRT
358 
359 /**************************************************************************
360  ** QThreadPrivate
361  *************************************************************************/
362 
363 #endif // QT_CONFIG(thread)
364 
365 QAbstractEventDispatcher *QThreadPrivate::createEventDispatcher(QThreadData *data)
366 {
367     Q_UNUSED(data);
368 #ifndef Q_OS_WINRT
369     return new QEventDispatcherWin32;
370 #else
371     return new QEventDispatcherWinRT;
372 #endif
373 }
374 
375 #if QT_CONFIG(thread)
376 
377 unsigned int __stdcall QT_ENSURE_STACK_ALIGNED_FOR_SSE QThreadPrivate::start(void *arg) noexcept
378 {
379     QThread *thr = reinterpret_cast<QThread *>(arg);
380     QThreadData *data = QThreadData::get2(thr);
381 
382     qt_create_tls();
383     TlsSetValue(qt_current_thread_data_tls_index, data);
384     data->threadId.storeRelaxed(reinterpret_cast<Qt::HANDLE>(quintptr(GetCurrentThreadId())));
385 
386     QThread::setTerminationEnabled(false);
387 
388     {
389         QMutexLocker locker(&thr->d_func()->mutex);
390         data->quitNow = thr->d_func()->exited;
391     }
392 
393     data->ensureEventDispatcher();
394 
395 #if !defined(QT_NO_DEBUG) && defined(Q_CC_MSVC) && !defined(Q_OS_WINRT)
396     // sets the name of the current thread.
397     QByteArray objectName = thr->objectName().toLocal8Bit();
398     qt_set_thread_name(HANDLE(-1),
399                        objectName.isEmpty() ?
400                        thr->metaObject()->className() : objectName.constData());
401 #endif
402 
403     emit thr->started(QThread::QPrivateSignal());
404     QThread::setTerminationEnabled(true);
405     thr->run();
406 
407     finish(arg);
408     return 0;
409 }
410 
411 void QThreadPrivate::finish(void *arg, bool lockAnyway) noexcept
412 {
413     QThread *thr = reinterpret_cast<QThread *>(arg);
414     QThreadPrivate *d = thr->d_func();
415 
416     QMutexLocker locker(lockAnyway ? &d->mutex : 0);
417     d->isInFinish = true;
418     d->priority = QThread::InheritPriority;
419     void **tls_data = reinterpret_cast<void **>(&d->data->tls);
420     locker.unlock();
421     emit thr->finished(QThread::QPrivateSignal());
422     QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
423     QThreadStorageData::finish(tls_data);
424     locker.relock();
425 
426     QAbstractEventDispatcher *eventDispatcher = d->data->eventDispatcher.loadRelaxed();
427     if (eventDispatcher) {
428         d->data->eventDispatcher = 0;
429         locker.unlock();
430         eventDispatcher->closingDown();
431         delete eventDispatcher;
432         locker.relock();
433     }
434 
435     d->running = false;
436     d->finished = true;
437     d->isInFinish = false;
438     d->interruptionRequested = false;
439 
440     if (!d->waiters) {
441         CloseHandle(d->handle);
442         d->handle = 0;
443     }
444 
445     d->id = 0;
446 }
447 
448 /**************************************************************************
449  ** QThread
450  *************************************************************************/
451 
452 Qt::HANDLE QThread::currentThreadId() noexcept
453 {
454     return reinterpret_cast<Qt::HANDLE>(quintptr(GetCurrentThreadId()));
455 }
456 
457 int QThread::idealThreadCount() noexcept
458 {
459     SYSTEM_INFO sysinfo;
460 #ifndef Q_OS_WINRT
461     GetSystemInfo(&sysinfo);
462 #else
463     GetNativeSystemInfo(&sysinfo);
464 #endif
465     return sysinfo.dwNumberOfProcessors;
466 }
467 
468 void QThread::yieldCurrentThread()
469 {
470     SwitchToThread();
471 }
472 
473 #endif // QT_CONFIG(thread)
474 
475 void QThread::sleep(unsigned long secs)
476 {
477     ::Sleep(secs * 1000);
478 }
479 
480 void QThread::msleep(unsigned long msecs)
481 {
482     ::Sleep(msecs);
483 }
484 
485 void QThread::usleep(unsigned long usecs)
486 {
487     ::Sleep((usecs / 1000) + 1);
488 }
489 
490 #if QT_CONFIG(thread)
491 
492 void QThread::start(Priority priority)
493 {
494     Q_D(QThread);
495     QMutexLocker locker(&d->mutex);
496 
497     if (d->isInFinish) {
498         locker.unlock();
499         wait();
500         locker.relock();
501     }
502 
503     if (d->running)
504         return;
505 
506     d->running = true;
507     d->finished = false;
508     d->exited = false;
509     d->returnCode = 0;
510     d->interruptionRequested = false;
511 
512     /*
513       NOTE: we create the thread in the suspended state, set the
514       priority and then resume the thread.
515 
516       since threads are created with normal priority by default, we
517       could get into a case where a thread (with priority less than
518       NormalPriority) tries to create a new thread (also with priority
519       less than NormalPriority), but the newly created thread preempts
520       its 'parent' and runs at normal priority.
521     */
522 #if defined(Q_CC_MSVC) && !defined(_DLL) // && !defined(Q_OS_WINRT)
523 #  ifdef Q_OS_WINRT
524     // If you wish to accept the memory leaks, uncomment the part above.
525     // See:
526     //  https://support.microsoft.com/en-us/kb/104641
527     //  https://msdn.microsoft.com/en-us/library/kdzttdcb.aspx
528 #    error "Microsoft documentation says this combination leaks memory every time a thread is started. " \
529     "Please change your build back to -MD/-MDd or, if you understand this issue and want to continue, " \
530     "edit this source file."
531 #  endif
532     // MSVC -MT or -MTd build
533     d->handle = (Qt::HANDLE) _beginthreadex(NULL, d->stackSize, QThreadPrivate::start,
534                                             this, CREATE_SUSPENDED, &(d->id));
535 #else
536     // MSVC -MD or -MDd or MinGW build
537     d->handle = CreateThread(nullptr, d->stackSize,
538                              reinterpret_cast<LPTHREAD_START_ROUTINE>(QThreadPrivate::start),
539                              this, CREATE_SUSPENDED, reinterpret_cast<LPDWORD>(&d->id));
540 #endif // Q_OS_WINRT
541 
542     if (!d->handle) {
543         qErrnoWarning("QThread::start: Failed to create thread");
544         d->running = false;
545         d->finished = true;
546         return;
547     }
548 
549     int prio;
550     d->priority = priority;
551     switch (d->priority) {
552     case IdlePriority:
553         prio = THREAD_PRIORITY_IDLE;
554         break;
555 
556     case LowestPriority:
557         prio = THREAD_PRIORITY_LOWEST;
558         break;
559 
560     case LowPriority:
561         prio = THREAD_PRIORITY_BELOW_NORMAL;
562         break;
563 
564     case NormalPriority:
565         prio = THREAD_PRIORITY_NORMAL;
566         break;
567 
568     case HighPriority:
569         prio = THREAD_PRIORITY_ABOVE_NORMAL;
570         break;
571 
572     case HighestPriority:
573         prio = THREAD_PRIORITY_HIGHEST;
574         break;
575 
576     case TimeCriticalPriority:
577         prio = THREAD_PRIORITY_TIME_CRITICAL;
578         break;
579 
580     case InheritPriority:
581     default:
582         prio = GetThreadPriority(GetCurrentThread());
583         break;
584     }
585 
586     if (!SetThreadPriority(d->handle, prio)) {
587         qErrnoWarning("QThread::start: Failed to set thread priority");
588     }
589 
590     if (ResumeThread(d->handle) == (DWORD) -1) {
591         qErrnoWarning("QThread::start: Failed to resume new thread");
592     }
593 }
594 
595 void QThread::terminate()
596 {
597     Q_D(QThread);
598     QMutexLocker locker(&d->mutex);
599     if (!d->running)
600         return;
601     if (!d->terminationEnabled) {
602         d->terminatePending = true;
603         return;
604     }
605 
606     // Calling ExitThread() in setTerminationEnabled is all we can do on WinRT
607 #ifndef Q_OS_WINRT
608     TerminateThread(d->handle, 0);
609 #endif
610     QThreadPrivate::finish(this, false);
611 }
612 
613 bool QThread::wait(QDeadlineTimer deadline)
614 {
615     Q_D(QThread);
616     QMutexLocker locker(&d->mutex);
617 
618     if (d->id == GetCurrentThreadId()) {
619         qWarning("QThread::wait: Thread tried to wait on itself");
620         return false;
621     }
622     if (d->finished || !d->running)
623         return true;
624 
625     ++d->waiters;
626     locker.mutex()->unlock();
627 
628     bool ret = false;
629 #ifndef Q_OS_WINRT
630     switch (WaitForSingleObject(d->handle, deadline.remainingTime())) {
631 #else
632     switch (WaitForSingleObjectEx(d->handle, deadline.remainingTime(), false)) {
633 #endif
634     case WAIT_OBJECT_0:
635         ret = true;
636         break;
637     case WAIT_FAILED:
638         qErrnoWarning("QThread::wait: Thread wait failure");
639         break;
640     case WAIT_ABANDONED:
641     case WAIT_TIMEOUT:
642     default:
643         break;
644     }
645 
646     locker.mutex()->lock();
647     --d->waiters;
648 
649     if (ret && !d->finished) {
650         // thread was terminated by someone else
651 
652         QThreadPrivate::finish(this, false);
653     }
654 
655     if (d->finished && !d->waiters) {
656         CloseHandle(d->handle);
657         d->handle = 0;
658     }
659 
660     return ret;
661 }
662 
663 void QThread::setTerminationEnabled(bool enabled)
664 {
665     QThread *thr = currentThread();
666     Q_ASSERT_X(thr != 0, "QThread::setTerminationEnabled()",
667                "Current thread was not started with QThread.");
668     QThreadPrivate *d = thr->d_func();
669     QMutexLocker locker(&d->mutex);
670     d->terminationEnabled = enabled;
671     if (enabled && d->terminatePending) {
672         QThreadPrivate::finish(thr, false);
673         locker.unlock(); // don't leave the mutex locked!
674 #ifndef Q_OS_WINRT
675         _endthreadex(0);
676 #else
677         ExitThread(0);
678 #endif
679     }
680 }
681 
682 // Caller must hold the mutex
683 void QThreadPrivate::setPriority(QThread::Priority threadPriority)
684 {
685     // copied from start() with a few modifications:
686 
687     int prio;
688     priority = threadPriority;
689     switch (priority) {
690     case QThread::IdlePriority:
691         prio = THREAD_PRIORITY_IDLE;
692         break;
693 
694     case QThread::LowestPriority:
695         prio = THREAD_PRIORITY_LOWEST;
696         break;
697 
698     case QThread::LowPriority:
699         prio = THREAD_PRIORITY_BELOW_NORMAL;
700         break;
701 
702     case QThread::NormalPriority:
703         prio = THREAD_PRIORITY_NORMAL;
704         break;
705 
706     case QThread::HighPriority:
707         prio = THREAD_PRIORITY_ABOVE_NORMAL;
708         break;
709 
710     case QThread::HighestPriority:
711         prio = THREAD_PRIORITY_HIGHEST;
712         break;
713 
714     case QThread::TimeCriticalPriority:
715         prio = THREAD_PRIORITY_TIME_CRITICAL;
716         break;
717 
718     default:
719         return;
720     }
721 
722     if (!SetThreadPriority(handle, prio)) {
723         qErrnoWarning("QThread::setPriority: Failed to set thread priority");
724     }
725 }
726 
727 #endif // QT_CONFIG(thread)
728 
729 QT_END_NAMESPACE
730