diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-10 18:07:22 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-10 18:07:22 +0000 |
commit | c04dcc2e7d834218ef2d4194331e383402495ae1 (patch) | |
tree | 7333e38d10d75386e60f336b80c2443c1166031d /xbmc/platform/linux/threads | |
parent | Initial commit. (diff) | |
download | kodi-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.txt | 4 | ||||
-rw-r--r-- | xbmc/platform/linux/threads/ThreadImplLinux.cpp | 171 | ||||
-rw-r--r-- | xbmc/platform/linux/threads/ThreadImplLinux.h | 26 |
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; +}; |