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