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

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

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