1// Copyright (C) 2016 The Qt Company Ltd.
2// Copyright (C) 2016 Intel Corporation.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4
5#include "qthread.h"
6
7#include "qplatformdefs.h"
8
9#include <private/qcoreapplication_p.h>
10#include <private/qcore_unix_p.h>
11#include <private/qtools_p.h>
12
13#if defined(Q_OS_DARWIN)
14# include <private/qeventdispatcher_cf_p.h>
15#elif defined(Q_OS_WASM)
16# include <private/qeventdispatcher_wasm_p.h>
17#else
18# if !defined(QT_NO_GLIB)
19# include "../kernel/qeventdispatcher_glib_p.h"
20# endif
21#endif
22
23#if !defined(Q_OS_WASM)
24# include <private/qeventdispatcher_unix_p.h>
25#endif
26
27#include "qthreadstorage.h"
28
29#include "qthread_p.h"
30
31#include "qdebug.h"
32
33#ifdef __GLIBCXX__
34#include <cxxabi.h>
35#endif
36
37#include <sched.h>
38#include <errno.h>
39
40#if defined(Q_OS_FREEBSD)
41# include <sys/cpuset.h>
42#elif defined(Q_OS_BSD4)
43# include <sys/sysctl.h>
44#endif
45#ifdef Q_OS_VXWORKS
46# include <vxCpuLib.h>
47# include <cpuset.h>
48#endif
49
50#ifdef Q_OS_HPUX
51#include <sys/pstat.h>
52#endif
53
54#if defined(Q_OS_LINUX) && !defined(QT_LINUXBASE)
55#include <sys/prctl.h>
56#endif
57
58#if defined(Q_OS_LINUX) && !defined(SCHED_IDLE)
59// from linux/sched.h
60# define SCHED_IDLE 5
61#endif
62
63#if defined(Q_OS_DARWIN) || !defined(Q_OS_ANDROID) && !defined(Q_OS_OPENBSD) && defined(_POSIX_THREAD_PRIORITY_SCHEDULING) && (_POSIX_THREAD_PRIORITY_SCHEDULING-0 >= 0)
64#define QT_HAS_THREAD_PRIORITY_SCHEDULING
65#endif
66
67#if defined(Q_OS_QNX)
68#include <sys/neutrino.h>
69#endif
70
71QT_BEGIN_NAMESPACE
72
73using namespace QtMiscUtils;
74
75#if QT_CONFIG(thread)
76
77static_assert(sizeof(pthread_t) <= sizeof(Qt::HANDLE));
78
79enum { ThreadPriorityResetFlag = 0x80000000 };
80
81#if QT_CONFIG(broken_threadlocal_dtors)
82// On most modern platforms, the C runtime has a helper function that helps the
83// C++ runtime run the thread_local non-trivial destructors when threads exit
84// and that code ensures that they are run in the correct order on program exit
85// too ([basic.start.term]/2: "The destruction of all constructed objects with
86// thread storage duration within that thread strongly happens before
87// destroying any object with static storage duration."). In the absence of
88// this function, the ordering can be wrong depending on when the first
89// non-trivial thread_local object was created relative to other statics.
90// Moreover, this can be racy and having our own thread_local early in
91// QThreadPrivate::start() made it even more so. See QTBUG-129846 for analysis.
92//
93// For the platforms where this C++11 feature is not properly implemented yet,
94// we fall back to a pthread_setspecific() call and do not perform late
95// clean-up, because then the order of registration of those pthread_specific_t
96// keys matters and Glib uses them too.
97//
98// https://gcc.gnu.org/git/?p=gcc.git;a=blob;f=libstdc%2B%2B-v3/libsupc%2B%2B/atexit_thread.cc;hb=releases/gcc-14.2.0#l133
99// https://github.com/llvm/llvm-project/blob/llvmorg-19.1.0/libcxxabi/src/cxa_thread_atexit.cpp#L118-L120
100#endif // QT_CONFIG(broken_threadlocal_dtors)
101
102// Always access this through the {get,set,clear}_thread_data() functions.
103Q_CONSTINIT static thread_local QThreadData *currentThreadData = nullptr;
104
105static void destroy_current_thread_data(void *p)
106{
107 QThreadData *data = static_cast<QThreadData *>(p);
108 QThread *thread = data->thread.loadAcquire();
109
110 if (data->isAdopted) {
111 // If this is an adopted thread, then QThreadData owns the QThread and
112 // this is very likely the last reference. These pointers cannot be
113 // null and there is no race.
114 QThreadPrivate *thread_p = static_cast<QThreadPrivate *>(QObjectPrivate::get(o: thread));
115 thread_p->finish();
116 if constexpr (!QT_CONFIG(broken_threadlocal_dtors))
117 thread_p->cleanup();
118 } else if constexpr (!QT_CONFIG(broken_threadlocal_dtors)) {
119 // We may be racing the QThread destructor in another thread. With
120 // two-phase clean-up enabled, there's also no race because it will
121 // stop in a call to QThread::wait() until we call cleanup().
122 QThreadPrivate *thread_p = static_cast<QThreadPrivate *>(QObjectPrivate::get(o: thread));
123 thread_p->cleanup();
124 } else {
125 // We may be racing the QThread destructor in another thread and it may
126 // have begun destruction; we must not dereference the QThread pointer.
127 }
128
129 // the QThread object may still have a reference, so this may not delete
130 data->deref();
131
132 // ... but we must reset it to zero before returning so we aren't
133 // leaving a dangling pointer.
134 currentThreadData = nullptr;
135}
136
137// Utility functions for getting, setting and clearing thread specific data.
138static QThreadData *get_thread_data()
139{
140 return currentThreadData;
141}
142
143static void set_thread_data(QThreadData *data)
144{
145 // Only activate the late cleanup for auxiliary threads. We can't use
146 // QThread::isMainThread() here because theMainThreadId will not have been
147 // set yet.
148 if (data && QCoreApplicationPrivate::theMainThreadId.loadAcquire()) {
149 if constexpr (QT_CONFIG(broken_threadlocal_dtors)) {
150 static pthread_key_t tls_key;
151 struct TlsKey {
152 TlsKey() { pthread_key_create(key: &tls_key, destr_function: destroy_current_thread_data); }
153 ~TlsKey() { pthread_key_delete(key: tls_key); }
154 };
155 static TlsKey currentThreadCleanup;
156 pthread_setspecific(key: tls_key, pointer: data);
157 } else {
158 struct Cleanup {
159 ~Cleanup() { destroy_current_thread_data(p: currentThreadData); }
160 };
161 static thread_local Cleanup currentThreadCleanup;
162 }
163 }
164 currentThreadData = data;
165}
166
167static void clear_thread_data()
168{
169 set_thread_data(nullptr);
170}
171
172template <typename T>
173static typename std::enable_if<std::is_integral_v<T>, Qt::HANDLE>::type to_HANDLE(T id)
174{
175 return reinterpret_cast<Qt::HANDLE>(static_cast<intptr_t>(id));
176}
177
178template <typename T>
179static typename std::enable_if<std::is_integral_v<T>, T>::type from_HANDLE(Qt::HANDLE id)
180{
181 return static_cast<T>(reinterpret_cast<intptr_t>(id));
182}
183
184template <typename T>
185static typename std::enable_if<std::is_pointer_v<T>, Qt::HANDLE>::type to_HANDLE(T id)
186{
187 return id;
188}
189
190template <typename T>
191static typename std::enable_if<std::is_pointer_v<T>, T>::type from_HANDLE(Qt::HANDLE id)
192{
193 return static_cast<T>(id);
194}
195
196void QThreadData::clearCurrentThreadData()
197{
198 clear_thread_data();
199}
200
201QThreadData *QThreadData::current(bool createIfNecessary)
202{
203 QThreadData *data = get_thread_data();
204 if (!data && createIfNecessary) {
205 data = new QThreadData;
206 QT_TRY {
207 set_thread_data(data);
208 data->thread.storeRelease(newValue: new QAdoptedThread(data));
209 } QT_CATCH(...) {
210 clear_thread_data();
211 data->deref();
212 data = nullptr;
213 QT_RETHROW;
214 }
215 data->deref();
216 data->isAdopted = true;
217 data->threadId.storeRelaxed(newValue: QThread::currentThreadId());
218 if (!QCoreApplicationPrivate::theMainThreadId.loadAcquire()) {
219 auto *mainThread = data->thread.loadRelaxed();
220 mainThread->setObjectName("Qt mainThread");
221 QCoreApplicationPrivate::theMainThread.storeRelease(newValue: mainThread);
222 QCoreApplicationPrivate::theMainThreadId.storeRelaxed(newValue: data->threadId.loadRelaxed());
223 }
224 }
225 return data;
226}
227
228
229void QAdoptedThread::init()
230{
231}
232
233/*
234 QThreadPrivate
235*/
236
237extern "C" {
238typedef void *(*QtThreadCallback)(void *);
239}
240
241#endif // QT_CONFIG(thread)
242
243QAbstractEventDispatcher *QThreadPrivate::createEventDispatcher(QThreadData *data)
244{
245 Q_UNUSED(data);
246#if defined(Q_OS_DARWIN)
247 bool ok = false;
248 int value = qEnvironmentVariableIntValue("QT_EVENT_DISPATCHER_CORE_FOUNDATION", &ok);
249 if (ok && value > 0)
250 return new QEventDispatcherCoreFoundation;
251 else
252 return new QEventDispatcherUNIX;
253#elif defined(Q_OS_WASM)
254 return new QEventDispatcherWasm();
255#elif !defined(QT_NO_GLIB)
256 const bool isQtMainThread = data->thread.loadAcquire() == QCoreApplicationPrivate::mainThread();
257 if (qEnvironmentVariableIsEmpty(varName: "QT_NO_GLIB")
258 && (isQtMainThread || qEnvironmentVariableIsEmpty(varName: "QT_NO_THREADED_GLIB"))
259 && QEventDispatcherGlib::versionSupported())
260 return new QEventDispatcherGlib;
261 else
262 return new QEventDispatcherUNIX;
263#else
264 return new QEventDispatcherUNIX;
265#endif
266}
267
268#if QT_CONFIG(thread)
269
270#if (defined(Q_OS_LINUX) || defined(Q_OS_DARWIN) || defined(Q_OS_QNX))
271static void setCurrentThreadName(const char *name)
272{
273# if defined(Q_OS_LINUX) && !defined(QT_LINUXBASE)
274 prctl(PR_SET_NAME, (unsigned long)name, 0, 0, 0);
275# elif defined(Q_OS_DARWIN)
276 pthread_setname_np(name);
277# elif defined(Q_OS_QNX)
278 pthread_setname_np(pthread_self(), name);
279# endif
280}
281#endif
282
283namespace {
284template <typename T>
285void terminate_on_exception(T &&t)
286{
287#ifndef QT_NO_EXCEPTIONS
288 try {
289#endif
290 std::forward<T>(t)();
291#ifndef QT_NO_EXCEPTIONS
292#ifdef __GLIBCXX__
293 // POSIX thread cancellation under glibc is implemented by throwing an exception
294 // of this type. Do what libstdc++ is doing and handle it specially in order not to
295 // abort the application if user's code calls a cancellation function.
296 } catch (abi::__forced_unwind &) {
297 throw;
298#endif // __GLIBCXX__
299 } catch (...) {
300 qTerminate();
301 }
302#endif // QT_NO_EXCEPTIONS
303}
304} // unnamed namespace
305
306void *QThreadPrivate::start(void *arg)
307{
308#ifdef PTHREAD_CANCEL_DISABLE
309 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, oldstate: nullptr);
310#endif
311 QThread *thr = reinterpret_cast<QThread *>(arg);
312 QThreadData *data = QThreadData::get2(thread: thr);
313
314 // this ensures the thread-local is created as early as possible
315 set_thread_data(data);
316
317 pthread_cleanup_push([](void *arg) { static_cast<QThread *>(arg)->d_func()->finish(); }, arg);
318 terminate_on_exception(t: [&] {
319 {
320 QMutexLocker locker(&thr->d_func()->mutex);
321
322 // do we need to reset the thread priority?
323 if (thr->d_func()->priority & ThreadPriorityResetFlag) {
324 thr->d_func()->setPriority(QThread::Priority(thr->d_func()->priority & ~ThreadPriorityResetFlag));
325 }
326
327 // threadId is set in QThread::start()
328 Q_ASSERT(data->threadId.loadRelaxed() == QThread::currentThreadId());
329
330 data->ref();
331 data->quitNow = thr->d_func()->exited;
332 }
333
334 data->ensureEventDispatcher();
335 data->eventDispatcher.loadRelaxed()->startingUp();
336
337#if (defined(Q_OS_LINUX) || defined(Q_OS_DARWIN) || defined(Q_OS_QNX))
338 {
339 // Sets the name of the current thread. We can only do this
340 // when the thread is starting, as we don't have a cross
341 // platform way of setting the name of an arbitrary thread.
342 if (Q_LIKELY(thr->d_func()->objectName.isEmpty()))
343 setCurrentThreadName(thr->metaObject()->className());
344 else
345 setCurrentThreadName(std::exchange(obj&: thr->d_func()->objectName, new_val: {}).toLocal8Bit());
346 }
347#endif
348
349 emit thr->started(QThread::QPrivateSignal());
350#ifdef PTHREAD_CANCEL_DISABLE
351 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, oldstate: nullptr);
352 pthread_testcancel();
353#endif
354 thr->run();
355 });
356
357 // This calls finish(); later, the currentThreadCleanup thread-local
358 // destructor will call cleanup().
359 pthread_cleanup_pop(1);
360 return nullptr;
361}
362
363void QThreadPrivate::finish()
364{
365 terminate_on_exception(t: [&] {
366 QThreadPrivate *d = this;
367 QThread *thr = q_func();
368
369 // Disable cancellation; we're already in the finishing touches of this
370 // thread, and we don't want cleanup to be disturbed by
371 // abi::__forced_unwind being thrown from all kinds of functions.
372#ifdef PTHREAD_CANCEL_DISABLE
373 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, oldstate: nullptr);
374#endif
375
376 QMutexLocker locker(&d->mutex);
377
378 d->isInFinish = true;
379 d->priority = QThread::InheritPriority;
380 locker.unlock();
381 emit thr->finished(QThread::QPrivateSignal());
382 qCDebug(lcDeleteLater) << "Sending deferred delete events as part of finishing thread" << thr;
383 QCoreApplication::sendPostedEvents(receiver: nullptr, event_type: QEvent::DeferredDelete);
384
385 void *data = &d->data->tls;
386 QThreadStorageData::finish((void **)data);
387 });
388
389 if constexpr (QT_CONFIG(broken_threadlocal_dtors))
390 cleanup();
391}
392
393void QThreadPrivate::cleanup()
394{
395 terminate_on_exception(t: [&] {
396 QThreadPrivate *d = this;
397
398 // Disable cancellation again: we did it above, but some user code
399 // running between finish() and cleanup() may have turned them back on.
400#ifdef PTHREAD_CANCEL_DISABLE
401 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, oldstate: nullptr);
402#endif
403
404 QMutexLocker locker(&d->mutex);
405 d->priority = QThread::InheritPriority;
406
407 QAbstractEventDispatcher *eventDispatcher = d->data->eventDispatcher.loadRelaxed();
408 if (eventDispatcher) {
409 d->data->eventDispatcher = nullptr;
410 locker.unlock();
411 eventDispatcher->closingDown();
412 delete eventDispatcher;
413 locker.relock();
414 }
415
416 d->running = false;
417 d->finished = true;
418 d->interruptionRequested.store(i: false, m: std::memory_order_relaxed);
419
420 d->isInFinish = false;
421 d->data->threadId.storeRelaxed(newValue: nullptr);
422
423 d->thread_done.wakeAll();
424 });
425}
426
427
428/**************************************************************************
429 ** QThread
430 *************************************************************************/
431
432/*
433 CI tests fails on ARM architectures if we try to use the assembler, so
434 stick to the pthread version there. The assembler would be
435
436 // http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0344k/Babeihid.html
437 asm volatile ("mrc p15, 0, %0, c13, c0, 3" : "=r" (tid));
438
439 and
440
441 // see glibc/sysdeps/aarch64/nptl/tls.h
442 asm volatile ("mrs %0, tpidr_el0" : "=r" (tid));
443
444 for 32 and 64bit versions, respectively.
445*/
446Qt::HANDLE QThread::currentThreadIdImpl() noexcept
447{
448 return to_HANDLE(id: pthread_self());
449}
450
451#if defined(QT_LINUXBASE) && !defined(_SC_NPROCESSORS_ONLN)
452// LSB doesn't define _SC_NPROCESSORS_ONLN.
453# define _SC_NPROCESSORS_ONLN 84
454#endif
455
456#ifdef Q_OS_WASM
457int QThreadPrivate::idealThreadCount = 1;
458#endif
459
460int QThread::idealThreadCount() noexcept
461{
462 int cores = 1;
463
464#if defined(Q_OS_HPUX)
465 // HP-UX
466 struct pst_dynamic psd;
467 if (pstat_getdynamic(&psd, sizeof(psd), 1, 0) == -1) {
468 perror("pstat_getdynamic");
469 } else {
470 cores = (int)psd.psd_proc_cnt;
471 }
472#elif (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)) || defined(Q_OS_FREEBSD)
473# if defined(Q_OS_FREEBSD) && !defined(CPU_COUNT_S)
474# define CPU_COUNT_S(setsize, cpusetp) ((int)BIT_COUNT(setsize, cpusetp))
475 // match the Linux API for simplicity
476 using cpu_set_t = cpuset_t;
477 auto sched_getaffinity = [](pid_t, size_t cpusetsize, cpu_set_t *mask) {
478 return cpuset_getaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, -1, cpusetsize, mask);
479 };
480# endif
481
482 // get the number of threads we're assigned, not the total in the system
483 QVarLengthArray<cpu_set_t, 1> cpuset(1);
484 int size = 1;
485 if (Q_UNLIKELY(sched_getaffinity(0, sizeof(cpu_set_t), cpuset.data()) < 0)) {
486 for (size = 2; size <= 4; size *= 2) {
487 cpuset.resize(sz: size);
488 if (sched_getaffinity(pid: 0, cpusetsize: sizeof(cpu_set_t) * size, cpuset: cpuset.data()) == 0)
489 break;
490 }
491 if (size > 4)
492 return 1;
493 }
494 cores = CPU_COUNT_S(sizeof(cpu_set_t) * size, cpuset.data());
495#elif defined(Q_OS_BSD4)
496 // OpenBSD, NetBSD, BSD/OS, Darwin (macOS, iOS, etc.)
497 size_t len = sizeof(cores);
498 int mib[2];
499 mib[0] = CTL_HW;
500 mib[1] = HW_NCPU;
501 if (sysctl(mib, 2, &cores, &len, NULL, 0) != 0) {
502 perror("sysctl");
503 }
504#elif defined(Q_OS_INTEGRITY)
505#if (__INTEGRITY_MAJOR_VERSION >= 10)
506 // Integrity V10+ does support multicore CPUs
507 Value processorCount;
508 if (GetProcessorCount(CurrentTask(), &processorCount) == 0)
509 cores = processorCount;
510 else
511#endif
512 // as of aug 2008 Integrity only supports one single core CPU
513 cores = 1;
514#elif defined(Q_OS_VXWORKS)
515 cpuset_t cpus = vxCpuEnabledGet();
516 cores = 0;
517
518 // 128 cores should be enough for everyone ;)
519 for (int i = 0; i < 128 && !CPUSET_ISZERO(cpus); ++i) {
520 if (CPUSET_ISSET(cpus, i)) {
521 CPUSET_CLR(cpus, i);
522 cores++;
523 }
524 }
525#elif defined(Q_OS_WASM)
526 cores = QThreadPrivate::idealThreadCount;
527#else
528 // the rest: Solaris, AIX, Tru64
529 cores = (int)sysconf(_SC_NPROCESSORS_ONLN);
530 if (cores == -1)
531 return 1;
532#endif
533 return cores;
534}
535
536void QThread::yieldCurrentThread()
537{
538 sched_yield();
539}
540
541#endif // QT_CONFIG(thread)
542
543static void qt_nanosleep(timespec amount)
544{
545 // We'd like to use clock_nanosleep.
546 //
547 // But clock_nanosleep is from POSIX.1-2001 and both are *not*
548 // affected by clock changes when using relative sleeps, even for
549 // CLOCK_REALTIME.
550 //
551 // nanosleep is POSIX.1-1993
552
553 int r;
554 QT_EINTR_LOOP(r, nanosleep(&amount, &amount));
555}
556
557void QThread::sleep(unsigned long secs)
558{
559 sleep(nsec: std::chrono::seconds{secs});
560}
561
562void QThread::msleep(unsigned long msecs)
563{
564 sleep(nsec: std::chrono::milliseconds{msecs});
565}
566
567void QThread::usleep(unsigned long usecs)
568{
569 sleep(nsec: std::chrono::microseconds{usecs});
570}
571
572void QThread::sleep(std::chrono::nanoseconds nsec)
573{
574 qt_nanosleep(amount: durationToTimespec(timeout: nsec));
575}
576
577#if QT_CONFIG(thread)
578
579#ifdef QT_HAS_THREAD_PRIORITY_SCHEDULING
580#if defined(Q_OS_QNX)
581static bool calculateUnixPriority(int priority, int *sched_policy, int *sched_priority)
582{
583 // On QNX, NormalPriority is mapped to 10. A QNX system could use a value different
584 // than 10 for the "normal" priority but it's difficult to achieve this so we'll
585 // assume that no one has ever created such a system. This makes the mapping from
586 // Qt priorities to QNX priorities lopsided. There's usually more space available
587 // to map into above the "normal" priority than below it. QNX also has a privileged
588 // priority range (for threads that assist the kernel). We'll assume that no Qt
589 // thread needs to use priorities in that range.
590 int priority_norm = 10;
591 // _sched_info::priority_priv isn't documented. You'd think that it's the start of the
592 // privileged priority range but it's actually the end of the unpriviledged range.
593 struct _sched_info info;
594 if (SchedInfo_r(0, *sched_policy, &info) != EOK)
595 return false;
596
597 if (priority == QThread::IdlePriority) {
598 *sched_priority = info.priority_min;
599 return true;
600 }
601
602 if (priority_norm < info.priority_min)
603 priority_norm = info.priority_min;
604 if (priority_norm > info.priority_priv)
605 priority_norm = info.priority_priv;
606
607 int to_min, to_max;
608 int from_min, from_max;
609 int prio;
610 if (priority < QThread::NormalPriority) {
611 to_min = info.priority_min;
612 to_max = priority_norm;
613 from_min = QThread::LowestPriority;
614 from_max = QThread::NormalPriority;
615 } else {
616 to_min = priority_norm;
617 to_max = info.priority_priv;
618 from_min = QThread::NormalPriority;
619 from_max = QThread::TimeCriticalPriority;
620 }
621
622 prio = ((priority - from_min) * (to_max - to_min)) / (from_max - from_min) + to_min;
623 prio = qBound(to_min, prio, to_max);
624
625 *sched_priority = prio;
626 return true;
627}
628#else
629// Does some magic and calculate the Unix scheduler priorities
630// sched_policy is IN/OUT: it must be set to a valid policy before calling this function
631// sched_priority is OUT only
632static bool calculateUnixPriority(int priority, int *sched_policy, int *sched_priority)
633{
634#ifdef SCHED_IDLE
635 if (priority == QThread::IdlePriority) {
636 *sched_policy = SCHED_IDLE;
637 *sched_priority = 0;
638 return true;
639 }
640 const int lowestPriority = QThread::LowestPriority;
641#else
642 const int lowestPriority = QThread::IdlePriority;
643#endif
644 const int highestPriority = QThread::TimeCriticalPriority;
645
646 int prio_min;
647 int prio_max;
648#if defined(Q_OS_VXWORKS) && defined(VXWORKS_DKM)
649 // for other scheduling policies than SCHED_RR or SCHED_FIFO
650 prio_min = SCHED_FIFO_LOW_PRI;
651 prio_max = SCHED_FIFO_HIGH_PRI;
652
653 if ((*sched_policy == SCHED_RR) || (*sched_policy == SCHED_FIFO))
654#endif
655 {
656 prio_min = sched_get_priority_min(algorithm: *sched_policy);
657 prio_max = sched_get_priority_max(algorithm: *sched_policy);
658 }
659
660 if (prio_min == -1 || prio_max == -1)
661 return false;
662
663 int prio;
664 // crudely scale our priority enum values to the prio_min/prio_max
665 prio = ((priority - lowestPriority) * (prio_max - prio_min) / highestPriority) + prio_min;
666 prio = qMax(a: prio_min, b: qMin(a: prio_max, b: prio));
667
668 *sched_priority = prio;
669 return true;
670}
671#endif
672#endif
673
674void QThread::start(Priority priority)
675{
676 Q_D(QThread);
677 QMutexLocker locker(&d->mutex);
678
679 if (d->isInFinish)
680 d->thread_done.wait(lockedMutex: locker.mutex());
681
682 if (d->running)
683 return;
684
685 d->running = true;
686 d->finished = false;
687 d->returnCode = 0;
688 d->exited = false;
689 d->interruptionRequested.store(i: false, m: std::memory_order_relaxed);
690 d->terminated = false;
691
692 pthread_attr_t attr;
693 pthread_attr_init(attr: &attr);
694 pthread_attr_setdetachstate(attr: &attr, PTHREAD_CREATE_DETACHED);
695
696 d->priority = priority;
697
698#if defined(QT_HAS_THREAD_PRIORITY_SCHEDULING)
699 switch (priority) {
700 case InheritPriority:
701 {
702 pthread_attr_setinheritsched(attr: &attr, PTHREAD_INHERIT_SCHED);
703 break;
704 }
705
706 default:
707 {
708 int sched_policy;
709 if (pthread_attr_getschedpolicy(attr: &attr, policy: &sched_policy) != 0) {
710 // failed to get the scheduling policy, don't bother
711 // setting the priority
712 qWarning(msg: "QThread::start: Cannot determine default scheduler policy");
713 break;
714 }
715
716 int prio;
717 if (!calculateUnixPriority(priority, sched_policy: &sched_policy, sched_priority: &prio)) {
718 // failed to get the scheduling parameters, don't
719 // bother setting the priority
720 qWarning(msg: "QThread::start: Cannot determine scheduler priority range");
721 break;
722 }
723
724 sched_param sp;
725 sp.sched_priority = prio;
726
727 if (pthread_attr_setinheritsched(attr: &attr, PTHREAD_EXPLICIT_SCHED) != 0
728 || pthread_attr_setschedpolicy(attr: &attr, policy: sched_policy) != 0
729 || pthread_attr_setschedparam(attr: &attr, param: &sp) != 0) {
730 // could not set scheduling hints, fallback to inheriting them
731 // we'll try again from inside the thread
732 pthread_attr_setinheritsched(attr: &attr, PTHREAD_INHERIT_SCHED);
733 d->priority = qToUnderlying(e: priority) | ThreadPriorityResetFlag;
734 }
735 break;
736 }
737 }
738#endif // QT_HAS_THREAD_PRIORITY_SCHEDULING
739
740
741 if (d->stackSize > 0) {
742#if defined(_POSIX_THREAD_ATTR_STACKSIZE) && (_POSIX_THREAD_ATTR_STACKSIZE-0 > 0)
743 int code = pthread_attr_setstacksize(attr: &attr, stacksize: d->stackSize);
744#else
745 int code = ENOSYS; // stack size not supported, automatically fail
746#endif // _POSIX_THREAD_ATTR_STACKSIZE
747
748 if (code) {
749 qErrnoWarning(code, msg: "QThread::start: Thread stack size error");
750
751 // we failed to set the stacksize, and as the documentation states,
752 // the thread will fail to run...
753 d->running = false;
754 d->finished = false;
755 return;
756 }
757 }
758
759#ifdef Q_OS_INTEGRITY
760 if (Q_LIKELY(objectName().isEmpty()))
761 pthread_attr_setthreadname(&attr, metaObject()->className());
762 else
763 pthread_attr_setthreadname(&attr, objectName().toLocal8Bit());
764#else
765 // avoid interacting with the binding system
766 d->objectName = d->extraData ? d->extraData->objectName.valueBypassingBindings()
767 : QString();
768#endif
769
770 pthread_t threadId;
771 int code = pthread_create(newthread: &threadId, attr: &attr, start_routine: QThreadPrivate::start, arg: this);
772 if (code == EPERM) {
773 // caller does not have permission to set the scheduling
774 // parameters/policy
775#if defined(QT_HAS_THREAD_PRIORITY_SCHEDULING)
776 pthread_attr_setinheritsched(attr: &attr, PTHREAD_INHERIT_SCHED);
777#endif
778 code = pthread_create(newthread: &threadId, attr: &attr, start_routine: QThreadPrivate::start, arg: this);
779 }
780 d->data->threadId.storeRelaxed(newValue: to_HANDLE(id: threadId));
781
782 pthread_attr_destroy(attr: &attr);
783
784 if (code) {
785 qErrnoWarning(code, msg: "QThread::start: Thread creation error");
786
787 d->running = false;
788 d->finished = false;
789 d->data->threadId.storeRelaxed(newValue: nullptr);
790 }
791}
792
793void QThread::terminate()
794{
795#if !defined(Q_OS_ANDROID)
796 Q_D(QThread);
797 QMutexLocker locker(&d->mutex);
798
799 const auto id = d->data->threadId.loadRelaxed();
800 if (!id)
801 return;
802
803 if (d->terminated) // don't try again, avoids killing the wrong thread on threadId reuse (ABA)
804 return;
805
806 d->terminated = true;
807
808 const bool selfCancelling = d->data == get_thread_data();
809 if (selfCancelling) {
810 // Posix doesn't seem to specify whether the stack of cancelled threads
811 // is unwound, and there's nothing preventing a QThread from
812 // terminate()ing itself, so drop the mutex before calling
813 // pthread_cancel():
814 locker.unlock();
815 }
816
817 if (int code = pthread_cancel(th: from_HANDLE<pthread_t>(id))) {
818 if (selfCancelling)
819 locker.relock();
820 d->terminated = false; // allow to try again
821 qErrnoWarning(code, msg: "QThread::start: Thread termination error");
822 }
823#endif
824}
825
826bool QThread::wait(QDeadlineTimer deadline)
827{
828 Q_D(QThread);
829 QMutexLocker locker(&d->mutex);
830
831 if (isCurrentThread()) {
832 qWarning(msg: "QThread::wait: Thread tried to wait on itself");
833 return false;
834 }
835
836 if (d->finished || !d->running)
837 return true;
838
839 return d->wait(locker, deadline);
840}
841
842bool QThreadPrivate::wait(QMutexLocker<QMutex> &locker, QDeadlineTimer deadline)
843{
844 Q_ASSERT(locker.isLocked());
845 QThreadPrivate *d = this;
846
847 while (d->running) {
848 if (!d->thread_done.wait(lockedMutex: locker.mutex(), deadline))
849 return false;
850 }
851 Q_ASSERT(d->data->threadId.loadRelaxed() == nullptr);
852
853 return true;
854}
855
856void QThread::setTerminationEnabled(bool enabled)
857{
858 QThread *thr = currentThread();
859 Q_ASSERT_X(thr != nullptr, "QThread::setTerminationEnabled()",
860 "Current thread was not started with QThread.");
861
862 Q_UNUSED(thr);
863#if defined(Q_OS_ANDROID)
864 Q_UNUSED(enabled);
865#else
866 pthread_setcancelstate(state: enabled ? PTHREAD_CANCEL_ENABLE : PTHREAD_CANCEL_DISABLE, oldstate: nullptr);
867 if (enabled)
868 pthread_testcancel();
869#endif
870}
871
872// Caller must lock the mutex
873void QThreadPrivate::setPriority(QThread::Priority threadPriority)
874{
875 priority = threadPriority;
876
877 // copied from start() with a few modifications:
878
879#ifdef QT_HAS_THREAD_PRIORITY_SCHEDULING
880 int sched_policy;
881 sched_param param;
882
883 if (pthread_getschedparam(target_thread: from_HANDLE<pthread_t>(id: data->threadId.loadRelaxed()), policy: &sched_policy, param: &param) != 0) {
884 // failed to get the scheduling policy, don't bother setting
885 // the priority
886 qWarning(msg: "QThread::setPriority: Cannot get scheduler parameters");
887 return;
888 }
889
890 int prio;
891 if (!calculateUnixPriority(priority, sched_policy: &sched_policy, sched_priority: &prio)) {
892 // failed to get the scheduling parameters, don't
893 // bother setting the priority
894 qWarning(msg: "QThread::setPriority: Cannot determine scheduler priority range");
895 return;
896 }
897
898 param.sched_priority = prio;
899 int status = pthread_setschedparam(target_thread: from_HANDLE<pthread_t>(id: data->threadId.loadRelaxed()), policy: sched_policy, param: &param);
900
901# ifdef SCHED_IDLE
902 // were we trying to set to idle priority and failed?
903 if (status == -1 && sched_policy == SCHED_IDLE && errno == EINVAL) {
904 // reset to lowest priority possible
905 pthread_getschedparam(target_thread: from_HANDLE<pthread_t>(id: data->threadId.loadRelaxed()), policy: &sched_policy, param: &param);
906 param.sched_priority = sched_get_priority_min(algorithm: sched_policy);
907 pthread_setschedparam(target_thread: from_HANDLE<pthread_t>(id: data->threadId.loadRelaxed()), policy: sched_policy, param: &param);
908 }
909# else
910 Q_UNUSED(status);
911# endif // SCHED_IDLE
912#endif
913}
914
915#endif // QT_CONFIG(thread)
916
917QT_END_NAMESPACE
918
919

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

source code of qtbase/src/corelib/thread/qthread_unix.cpp