summaryrefslogtreecommitdiffstats
path: root/xbmc/platform/linux/threads
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-10 18:07:22 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-10 18:07:22 +0000
commitc04dcc2e7d834218ef2d4194331e383402495ae1 (patch)
tree7333e38d10d75386e60f336b80c2443c1166031d /xbmc/platform/linux/threads
parentInitial commit. (diff)
downloadkodi-c04dcc2e7d834218ef2d4194331e383402495ae1.tar.xz
kodi-c04dcc2e7d834218ef2d4194331e383402495ae1.zip
Adding upstream version 2:20.4+dfsg.upstream/2%20.4+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'xbmc/platform/linux/threads')
-rw-r--r--xbmc/platform/linux/threads/CMakeLists.txt4
-rw-r--r--xbmc/platform/linux/threads/ThreadImplLinux.cpp171
-rw-r--r--xbmc/platform/linux/threads/ThreadImplLinux.h26
3 files changed, 201 insertions, 0 deletions
diff --git a/xbmc/platform/linux/threads/CMakeLists.txt b/xbmc/platform/linux/threads/CMakeLists.txt
new file mode 100644
index 0000000..b90d55e
--- /dev/null
+++ b/xbmc/platform/linux/threads/CMakeLists.txt
@@ -0,0 +1,4 @@
+set(SOURCES ThreadImplLinux.cpp)
+set(HEADERS ThreadImplLinux.h)
+
+core_add_library(platform_linux_threads)
diff --git a/xbmc/platform/linux/threads/ThreadImplLinux.cpp b/xbmc/platform/linux/threads/ThreadImplLinux.cpp
new file mode 100644
index 0000000..f4810cf
--- /dev/null
+++ b/xbmc/platform/linux/threads/ThreadImplLinux.cpp
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "ThreadImplLinux.h"
+
+#include "utils/Map.h"
+#include "utils/log.h"
+
+#include <algorithm>
+#include <array>
+#include <limits.h>
+
+#include <sys/resource.h>
+#include <unistd.h>
+
+#if !defined(TARGET_ANDROID) && (defined(__GLIBC__) || defined(__UCLIBC__))
+#if defined(__UCLIBC__) || !__GLIBC_PREREQ(2, 30)
+#include <sys/syscall.h>
+#endif
+#endif
+
+namespace
+{
+
+constexpr auto nativeThreadPriorityMap = make_map<ThreadPriority, int>({
+ {ThreadPriority::LOWEST, -1},
+ {ThreadPriority::BELOW_NORMAL, -1},
+ {ThreadPriority::NORMAL, 0},
+ {ThreadPriority::ABOVE_NORMAL, 1},
+ {ThreadPriority::HIGHEST, 1},
+});
+
+static_assert(static_cast<size_t>(ThreadPriority::PRIORITY_COUNT) == nativeThreadPriorityMap.size(),
+ "nativeThreadPriorityMap doesn't match the size of ThreadPriority, did you forget to "
+ "add/remove a mapping?");
+
+constexpr int ThreadPriorityToNativePriority(const ThreadPriority& priority)
+{
+ const auto it = nativeThreadPriorityMap.find(priority);
+ if (it != nativeThreadPriorityMap.cend())
+ {
+ return it->second;
+ }
+ else
+ {
+ throw std::range_error("Priority not found");
+ }
+}
+
+#if !defined(TARGET_ANDROID) && (defined(__GLIBC__) || defined(__UCLIBC__))
+#if defined(__UCLIBC__) || !__GLIBC_PREREQ(2, 30)
+static pid_t gettid()
+{
+ return syscall(__NR_gettid);
+}
+#endif
+#endif
+
+} // namespace
+
+static int s_maxPriority;
+static bool s_maxPriorityIsSet{false};
+
+// We need to return what the best number than can be passed
+// to SetPriority is. It will basically be relative to the
+// the main thread's nice level, inverted (since "higher" priority
+// nice levels are actually lower numbers).
+static int GetUserMaxPriority(int maxPriority)
+{
+ if (s_maxPriorityIsSet)
+ return s_maxPriority;
+
+ // if we're root, then we can do anything. So we'll allow
+ // max priority.
+ if (geteuid() == 0)
+ return maxPriority;
+
+ // get user max prio
+ struct rlimit limit;
+ if (getrlimit(RLIMIT_NICE, &limit) != 0)
+ {
+ // If we fail getting the limit for nice we just assume we can't raise the priority
+ return 0;
+ }
+
+ const int appNice = getpriority(PRIO_PROCESS, getpid());
+ const int rlimVal = limit.rlim_cur;
+
+ // according to the docs, limit.rlim_cur shouldn't be zero, yet, here we are.
+ // if a user has no entry in limits.conf rlim_cur is zero. In this case the best
+ // nice value we can hope to achieve is '0' as a regular user
+ const int userBestNiceValue = (rlimVal == 0) ? 0 : (20 - rlimVal);
+
+ // running the app with nice -n 10 ->
+ // e.g. +10 10 - 0 // default non-root user.
+ // e.g. +30 10 - -20 // if root with rlimits set.
+ // running the app default ->
+ // e.g. 0 0 - 0 // default non-root user.
+ // e.g. +20 0 - -20 // if root with rlimits set.
+ const int bestUserSetPriority = appNice - userBestNiceValue; // nice is inverted from prio.
+
+ // static because we only need to check this once.
+ // we shouldn't expect a user to change RLIMIT_NICE while running
+ // and it won't work anyway for threads that already set their priority.
+ s_maxPriority = std::min(maxPriority, bestUserSetPriority);
+ s_maxPriorityIsSet = true;
+
+ return s_maxPriority;
+}
+
+std::unique_ptr<IThreadImpl> IThreadImpl::CreateThreadImpl(std::thread::native_handle_type handle)
+{
+ return std::make_unique<CThreadImplLinux>(handle);
+}
+
+CThreadImplLinux::CThreadImplLinux(std::thread::native_handle_type handle)
+ : IThreadImpl(handle), m_threadID(gettid())
+{
+}
+
+void CThreadImplLinux::SetThreadInfo(const std::string& name)
+{
+#if defined(__GLIBC__)
+ pthread_setname_np(m_handle, name.c_str());
+#endif
+
+ // get user max prio
+ const int maxPrio = ThreadPriorityToNativePriority(ThreadPriority::HIGHEST);
+ const int userMaxPrio = GetUserMaxPriority(maxPrio);
+
+ // if the user does not have an entry in limits.conf the following
+ // call will fail
+ if (userMaxPrio > 0)
+ {
+ // start thread with nice level of application
+ const int appNice = getpriority(PRIO_PROCESS, getpid());
+ if (setpriority(PRIO_PROCESS, m_threadID, appNice) != 0)
+ CLog::Log(LOGERROR, "[threads] failed to set priority: {}", strerror(errno));
+ }
+}
+
+bool CThreadImplLinux::SetPriority(const ThreadPriority& priority)
+{
+ // keep priority in bounds
+ const int prio = ThreadPriorityToNativePriority(priority);
+ const int maxPrio = ThreadPriorityToNativePriority(ThreadPriority::HIGHEST);
+ const int minPrio = ThreadPriorityToNativePriority(ThreadPriority::LOWEST);
+
+ // get user max prio given max prio (will take the min)
+ const int userMaxPrio = GetUserMaxPriority(maxPrio);
+
+ // clamp to min and max priorities
+ const int adjustedPrio = std::clamp(prio, minPrio, userMaxPrio);
+
+ // nice level of application
+ const int appNice = getpriority(PRIO_PROCESS, getpid());
+ const int newNice = appNice - adjustedPrio;
+
+ if (setpriority(PRIO_PROCESS, m_threadID, newNice) != 0)
+ {
+ CLog::Log(LOGERROR, "[threads] failed to set priority: {}", strerror(errno));
+ return false;
+ }
+
+ return true;
+}
diff --git a/xbmc/platform/linux/threads/ThreadImplLinux.h b/xbmc/platform/linux/threads/ThreadImplLinux.h
new file mode 100644
index 0000000..986ffe5
--- /dev/null
+++ b/xbmc/platform/linux/threads/ThreadImplLinux.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include "threads/IThreadImpl.h"
+
+class CThreadImplLinux : public IThreadImpl
+{
+public:
+ CThreadImplLinux(std::thread::native_handle_type handle);
+
+ ~CThreadImplLinux() override = default;
+
+ void SetThreadInfo(const std::string& name) override;
+
+ bool SetPriority(const ThreadPriority& priority) override;
+
+private:
+ pid_t m_threadID;
+};