summaryrefslogtreecommitdiffstats
path: root/src/foundation/linux/Qt3DSLinuxThread.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/foundation/linux/Qt3DSLinuxThread.cpp')
-rw-r--r--src/foundation/linux/Qt3DSLinuxThread.cpp384
1 files changed, 384 insertions, 0 deletions
diff --git a/src/foundation/linux/Qt3DSLinuxThread.cpp b/src/foundation/linux/Qt3DSLinuxThread.cpp
new file mode 100644
index 0000000..fa6e069
--- /dev/null
+++ b/src/foundation/linux/Qt3DSLinuxThread.cpp
@@ -0,0 +1,384 @@
+/****************************************************************************
+**
+** Copyright (C) 2008-2012 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "foundation/Qt3DS.h"
+#include "foundation/Qt3DSFoundation.h"
+#include "foundation/Qt3DSAtomic.h"
+#include "foundation/Qt3DSThread.h"
+#include "foundation/Qt3DSAssert.h"
+#include "foundation/Qt3DSIntrinsics.h"
+#include "foundation/Qt3DSBroadcastingAllocator.h"
+#if !defined(QT3DS_APPLE) && !defined(ANDROID) && !defined(__CYGWIN__) && !defined(__QNX__) && !defined(__INTEGRITY)
+#include <bits/local_lim.h> // PTHREAD_STACK_MIN
+#endif
+#include <stdio.h>
+#include <pthread.h>
+#if !defined(__QNX__) && !defined(__INTEGRITY)
+#include <unistd.h>
+#include <sys/syscall.h>
+#if !defined(QT3DS_APPLE)
+#include <asm/unistd.h>
+#include <sys/resource.h>
+#endif
+#endif
+
+#define NVSpinLockPause() asm("nop")
+
+namespace qt3ds {
+namespace foundation {
+ using namespace intrinsics;
+
+ typedef enum { _NVThreadNotStarted, _NVThreadStarted, _NVThreadStopped } NVThreadState;
+
+ class ThreadImpl
+ {
+ public:
+ ThreadImpl(NVFoundationBase &foundation)
+ : mFoundation(foundation)
+ {
+ }
+ NVFoundationBase &mFoundation;
+ Thread::ExecuteFn fn;
+ void *arg;
+ volatile QT3DSI32 quitNow;
+ volatile QT3DSI32 threadStarted;
+ NVThreadState state;
+
+ pthread_t thread;
+ pid_t tid;
+ };
+
+ void *NVThreadStart(void *arg);
+
+ Thread::Id Thread::getId() { return Id(pthread_self()); }
+
+ Thread::Thread(NVFoundationBase &foundation)
+ {
+ mImpl = QT3DS_NEW(foundation.getAllocator(), ThreadImpl)(foundation);
+ mImpl->thread = 0;
+ mImpl->tid = 0;
+ mImpl->state = _NVThreadNotStarted;
+ mImpl->quitNow = 0;
+ mImpl->threadStarted = 0;
+ mImpl->fn = NULL;
+ mImpl->arg = NULL;
+ }
+
+ Thread::Thread(NVFoundationBase &foundation, Thread::ExecuteFn fn, void *arg)
+ {
+ mImpl = (ThreadImpl *)QT3DS_NEW(foundation.getAllocator(), ThreadImpl)(foundation);
+ mImpl->thread = 0;
+ mImpl->tid = 0;
+ mImpl->state = _NVThreadNotStarted;
+ mImpl->quitNow = 0;
+ mImpl->threadStarted = 0;
+ mImpl->fn = fn;
+ mImpl->arg = arg;
+
+ start(0);
+ }
+
+ Thread::~Thread()
+ {
+ if (mImpl->state == _NVThreadStarted)
+ kill();
+ QT3DS_FREE(mImpl->mFoundation.getAllocator(), mImpl);
+ }
+
+ void Thread::start(QT3DSU32 stackSize)
+ {
+ if (mImpl->state != _NVThreadNotStarted)
+ return;
+
+ if (stackSize == 0)
+ stackSize = DEFAULT_STACK_SIZE;
+
+#if defined(PTHREAD_STACK_MIN) && !defined(ANDROID)
+ if (stackSize < PTHREAD_STACK_MIN) {
+ qCWarning(WARNING, "Thread::start(): stack size was set below PTHREAD_STACK_MIN");
+ stackSize = PTHREAD_STACK_MIN;
+ }
+#endif
+
+ pthread_attr_t attr;
+ int status = pthread_attr_init(&attr);
+ QT3DS_ASSERT(!status);
+
+ status = pthread_attr_setstacksize(&attr, stackSize);
+ QT3DS_ASSERT(!status);
+ status = pthread_create(&mImpl->thread, &attr, NVThreadStart, this);
+ QT3DS_ASSERT(!status);
+
+#ifndef __QNX__
+ // wait for thread to startup and write out TID
+ // otherwise TID dependent calls like setAffinity will fail.
+ while (atomicCompareExchange(&(mImpl->threadStarted), 1, 1) == 0)
+ yield();
+#endif
+
+ mImpl->state = _NVThreadStarted;
+
+ status = pthread_attr_destroy(&attr);
+ QT3DS_ASSERT(!status);
+ }
+
+ static void setTid(ThreadImpl &threadImpl)
+ {
+#ifndef __QNX__
+// query TID
+#ifdef QT3DS_APPLE
+ threadImpl.tid = syscall(SYS_gettid);
+#elif defined(__INTEGRITY)
+ pthread_t ptid = pthread_self();
+ uint64_t threadId = 0;
+ memcpy(&threadId, &ptid, std::min(sizeof(threadId), sizeof(ptid)));
+ threadImpl.tid = threadId;
+#else
+ threadImpl.tid = syscall(__NR_gettid);
+#endif
+
+ // notify/unblock parent thread
+ atomicCompareExchange(&(threadImpl.threadStarted), 1, 0);
+#else
+ QT3DS_ASSERT(false);
+#endif
+ }
+
+ void *NVThreadStart(void *arg)
+ {
+ // run execute from base class to run gettid in the new thread's context
+ ((Thread *)arg)->Thread::execute();
+ return 0;
+ }
+
+ void Thread::execute(void)
+ {
+ // run setTid in thread's context
+ setTid(*mImpl);
+
+ // then run either the passed in function or execute from the derived class.
+ if (mImpl->fn)
+ (*mImpl->fn)(mImpl->arg);
+ else
+ this->execute();
+ }
+
+ void Thread::signalQuit() { atomicIncrement(&(mImpl->quitNow)); }
+
+ bool Thread::waitForQuit()
+ {
+ if (mImpl->state == _NVThreadNotStarted)
+ return false;
+
+ pthread_join(mImpl->thread, NULL);
+ return true;
+ }
+
+ bool Thread::quitIsSignalled()
+ {
+#ifndef __QNX__
+ return atomicCompareExchange(&(mImpl->quitNow), 0, 0) != 0;
+#else
+ // Hope for memory locking on the arm.
+ return mImpl->quitNow != 0;
+#endif
+ }
+
+ void Thread::quit()
+ {
+ mImpl->state = _NVThreadStopped;
+ pthread_exit(0);
+ }
+
+ void Thread::kill()
+ {
+#ifndef ANDROID
+ if (mImpl->state == _NVThreadStarted)
+ pthread_cancel(mImpl->thread);
+ mImpl->state = _NVThreadStopped;
+#else
+ qCWarning(WARNING, "Thread::kill() called, but is not implemented");
+#endif
+ }
+
+ void Thread::sleep(QT3DSU32 ms)
+ {
+ timespec sleepTime;
+ QT3DSU32 remainder = ms % 1000;
+ sleepTime.tv_sec = ms - remainder;
+ sleepTime.tv_nsec = remainder * 1000000L;
+
+ while (nanosleep(&sleepTime, &sleepTime) == -1)
+ continue;
+ }
+
+ void Thread::yield() { sched_yield(); }
+
+ QT3DSU32 Thread::setAffinityMask(QT3DSU32 mask)
+ {
+ // Same as windows impl if mask is zero
+ if (!mask)
+ return 0;
+
+#ifndef __QNX__
+ QT3DSU64 prevMask = 0;
+
+// Apple doesn't support syscall with getaffinity and setaffinity
+#if !defined(QT3DS_APPLE) && !defined(__INTEGRITY)
+ QT3DSI32 errGet = syscall(__NR_sched_getaffinity, mImpl->tid, sizeof(prevMask), &prevMask);
+ if (errGet < 0)
+ return 0;
+
+ QT3DSI32 errSet = syscall(__NR_sched_setaffinity, mImpl->tid, sizeof(mask), &mask);
+ if (errSet != 0)
+ return 0;
+#endif
+
+ return QT3DSU32(prevMask);
+#else
+ QT3DS_ASSERT(false);
+ return 0;
+#endif
+ }
+
+ void Thread::setName(const char *name)
+ {
+#if (defined(ANDROID) && (__ANDROID_API__ > 8))
+ pthread_setname_np(mImpl->thread, name);
+#else
+// not implemented because most unix APIs expect setName()
+// to be called from the thread's context. Example see next comment:
+
+// this works only with the current thread and can rename
+// the main process if used in the wrong context:
+// prctl(PR_SET_NAME, reinterpret_cast<unsigned long>(name) ,0,0,0);
+#endif
+ }
+
+#if !defined(QT3DS_APPLE) && !defined(__INTEGRITY)
+ static ThreadPriority::Enum convertPriorityFromLinux(QT3DSU32 inPrio, int policy)
+ {
+ QT3DS_COMPILE_TIME_ASSERT(ThreadPriority::eLOW > ThreadPriority::eHIGH);
+ QT3DS_COMPILE_TIME_ASSERT(ThreadPriority::eHIGH == 0);
+
+ int maxL = sched_get_priority_max(policy);
+ int minL = sched_get_priority_min(policy);
+ int rangeL = maxL - minL;
+ int rangeNV = ThreadPriority::eLOW - ThreadPriority::eHIGH;
+
+ // case for default scheduler policy
+ if (rangeL == 0)
+ return ThreadPriority::eNORMAL;
+
+ float floatPrio = (float(maxL - inPrio) * float(rangeNV)) / float(rangeL);
+
+ return ThreadPriority::Enum(int(roundf(floatPrio)));
+ }
+
+ static int convertPriorityToLinux(ThreadPriority::Enum inPrio, int policy)
+ {
+ int maxL = sched_get_priority_max(policy);
+ int minL = sched_get_priority_min(policy);
+ int rangeL = maxL - minL;
+ int rangeNV = ThreadPriority::eLOW - ThreadPriority::eHIGH;
+
+ // case for default scheduler policy
+ if (rangeL == 0)
+ return 0;
+
+ float floatPrio = (float(ThreadPriority::eLOW - inPrio) * float(rangeL)) / float(rangeNV);
+
+ return minL + int(roundf(floatPrio));
+ }
+#endif
+
+ void Thread::setPriority(ThreadPriority::Enum val)
+ {
+#if !defined(QT3DS_APPLE) && !defined(__INTEGRITY)
+ int policy;
+ sched_param s_param;
+ pthread_getschedparam(mImpl->thread, &policy, &s_param);
+ s_param.sched_priority = convertPriorityToLinux(val, policy);
+ pthread_setschedparam(mImpl->thread, policy, &s_param);
+#endif
+ }
+
+ ThreadPriority::Enum Thread::getPriority(Id pthread)
+ {
+#if !defined(QT3DS_APPLE) && !defined(__INTEGRITY)
+ int policy;
+ sched_param s_param;
+ int ret = pthread_getschedparam(pthread_t(pthread), &policy, &s_param);
+ if (ret == 0)
+ return convertPriorityFromLinux(s_param.sched_priority, policy);
+ else
+ return ThreadPriority::eNORMAL;
+#else
+ return ThreadPriority::eNORMAL;
+#endif
+ }
+
+ QT3DSU32 TlsAlloc()
+ {
+#if !defined(__INTEGRITY)
+ pthread_key_t key;
+ int status = pthread_key_create(&key, NULL);
+ QT3DS_ASSERT(!status);
+ (void)status;
+ return (QT3DSU32)key;
+#else
+ QT3DS_ASSERT(false);
+ return 0;
+#endif
+ }
+
+ void TlsFree(QT3DSU32 index)
+ {
+ int status = pthread_key_delete((pthread_key_t)index);
+ QT3DS_ASSERT(!status);
+ (void)status;
+ }
+
+ void *TlsGet(QT3DSU32 index) { return (void *)pthread_getspecific((pthread_key_t)index); }
+
+ QT3DSU32 TlsSet(QT3DSU32 index, void *value)
+ {
+ int status = pthread_setspecific((pthread_key_t)index, value);
+ QT3DS_ASSERT(!status);
+ return !status;
+ }
+
+ // DM: On Linux x86-32, without implementation-specific restrictions
+ // the default stack size for a new thread should be 2 megabytes (kernel.org).
+ // NOTE: take care of this value on other architecutres!
+ const QT3DSU32 Thread::DEFAULT_STACK_SIZE = 1 << 21;
+
+} // namespace foundation
+} // namespace qt3ds