diff options
author | Lukas Rusak <lorusak@gmail.com> | 2022-03-26 10:34:32 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-03-26 10:34:32 -0700 |
commit | f0125d68a9ff0332dd61ae6eb764cb76b9476d7a (patch) | |
tree | a18a1747c2e534e7df06f1e6706e3bf1e1338c48 | |
parent | 1858297881e105f04bf975aa4e901105662791bc (diff) | |
parent | ec7ed895f87a292e9dbc2b006890c1867c0ea076 (diff) |
Merge pull request #21153 from lrusak/threading-cleanup-PR-4
threads: split CThreadImplPosix into CThreadImplLinux
-rw-r--r-- | cmake/treedata/android/subdirs.txt | 1 | ||||
-rw-r--r-- | cmake/treedata/linux/subdirs.txt | 1 | ||||
-rw-r--r-- | xbmc/platform/linux/threads/CMakeLists.txt | 4 | ||||
-rw-r--r-- | xbmc/platform/linux/threads/ThreadImplLinux.cpp | 152 | ||||
-rw-r--r-- | xbmc/platform/linux/threads/ThreadImplLinux.h | 26 | ||||
-rw-r--r-- | xbmc/platform/posix/threads/CMakeLists.txt | 11 | ||||
-rw-r--r-- | xbmc/platform/posix/threads/ThreadImplPosix.cpp | 152 | ||||
-rw-r--r-- | xbmc/platform/posix/threads/ThreadImplPosix.h | 6 |
8 files changed, 195 insertions, 158 deletions
diff --git a/cmake/treedata/android/subdirs.txt b/cmake/treedata/android/subdirs.txt index 506b9a5607..d3be3bf246 100644 --- a/cmake/treedata/android/subdirs.txt +++ b/cmake/treedata/android/subdirs.txt @@ -15,6 +15,7 @@ xbmc/platform/android/powermanagement platform/android/powermanagement xbmc/platform/android/storage platform/android/storage xbmc/platform/android/utils platform/android/utils xbmc/platform/linux/peripherals platform/linux/peripherals +xbmc/platform/linux/threads platform/linux/threads xbmc/platform/posix platform/posix xbmc/platform/posix/filesystem platform/posix/filesystem xbmc/platform/posix/threads platform/posix/threads diff --git a/cmake/treedata/linux/subdirs.txt b/cmake/treedata/linux/subdirs.txt index 440bea9afd..d25c36de51 100644 --- a/cmake/treedata/linux/subdirs.txt +++ b/cmake/treedata/linux/subdirs.txt @@ -7,6 +7,7 @@ xbmc/platform/linux/peripherals platform/linux/peripherals xbmc/platform/linux/powermanagement platform/linux/powermanagement xbmc/platform/linux/sse4 platform/linux/sse4 xbmc/platform/linux/storage platform/linux/storage +xbmc/platform/linux/threads platform/linux/threads xbmc/platform/posix platform/posix xbmc/platform/posix/filesystem platform/posix/filesystem xbmc/platform/posix/network platform/posix/network diff --git a/xbmc/platform/linux/threads/CMakeLists.txt b/xbmc/platform/linux/threads/CMakeLists.txt new file mode 100644 index 0000000000..b90d55ee36 --- /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 0000000000..1617e50941 --- /dev/null +++ b/xbmc/platform/linux/threads/ThreadImplLinux.cpp @@ -0,0 +1,152 @@ +/* + * 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/log.h" + +#include <algorithm> +#include <array> +#include <limits.h> + +#include <sys/resource.h> +#include <unistd.h> + +namespace +{ + +constexpr std::array<ThreadPriorityStruct, 5> nativeThreadPriorityMap = {{ + {ThreadPriority::LOWEST, -1}, + {ThreadPriority::BELOW_NORMAL, -1}, + {ThreadPriority::NORMAL, 0}, + {ThreadPriority::ABOVE_NORMAL, 1}, + {ThreadPriority::HIGHEST, 1}, +}}; + +//! @todo: c++20 has constexpr std::find_if +int ThreadPriorityToNativePriority(const ThreadPriority& priority) +{ + auto it = std::find_if(nativeThreadPriorityMap.cbegin(), nativeThreadPriorityMap.cend(), + [&priority](const auto& map) { return map.priority == priority; }); + + if (it != nativeThreadPriorityMap.cend()) + { + return it->nativePriority; + } + + throw std::runtime_error("priority not implemented"); +} + +} // 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 0000000000..986ffe5ef3 --- /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; +}; diff --git a/xbmc/platform/posix/threads/CMakeLists.txt b/xbmc/platform/posix/threads/CMakeLists.txt index dd9498a3a2..251a7e1ac7 100644 --- a/xbmc/platform/posix/threads/CMakeLists.txt +++ b/xbmc/platform/posix/threads/CMakeLists.txt @@ -1,6 +1,9 @@ -set(SOURCES RecursiveMutex.cpp - ThreadImplPosix.cpp) -set(HEADERS RecursiveMutex.h - ThreadImplPosix.h) +set(SOURCES RecursiveMutex.cpp) +set(HEADERS RecursiveMutex.h) + +if(NOT CORE_SYSTEM_NAME STREQUAL linux AND NOT CORE_SYSTEM_NAME STREQUAL android) + list(APPEND SOURCES ThreadImplPosix.cpp) + list(APPEND HEADERS ThreadImplPosix.h) +endif() core_add_library(platform_posix_threads) diff --git a/xbmc/platform/posix/threads/ThreadImplPosix.cpp b/xbmc/platform/posix/threads/ThreadImplPosix.cpp index 022c6b8fb8..ae5b849f36 100644 --- a/xbmc/platform/posix/threads/ThreadImplPosix.cpp +++ b/xbmc/platform/posix/threads/ThreadImplPosix.cpp @@ -8,113 +8,16 @@ #include "ThreadImplPosix.h" -#include <limits.h> -#include <mutex> -#if defined(TARGET_ANDROID) -#include <unistd.h> -#else -#include <sys/syscall.h> -#endif -#include <sys/resource.h> -#include <string.h> -#ifdef TARGET_FREEBSD -#include <sys/param.h> -#include <pthread_np.h> -#endif - #include "utils/log.h" -#include <array> -#include <signal.h> - -#if defined(RLIMIT_NICE) -namespace -{ - -constexpr std::array<ThreadPriorityStruct, 5> nativeThreadPriorityMap = {{ - {ThreadPriority::LOWEST, -1}, - {ThreadPriority::BELOW_NORMAL, -1}, - {ThreadPriority::NORMAL, 0}, - {ThreadPriority::ABOVE_NORMAL, 1}, - {ThreadPriority::HIGHEST, 1}, -}}; - -//! @todo: c++20 has constexpr std::find_if -int ThreadPriorityToNativePriority(const ThreadPriority& priority) -{ - auto it = std::find_if(nativeThreadPriorityMap.cbegin(), nativeThreadPriorityMap.cend(), - [&priority](const auto& map) { return map.priority == priority; }); - - if (it != nativeThreadPriorityMap.cend()) - { - return it->nativePriority; - } - - throw std::runtime_error("priority not implemented"); -} - -} // namespace -#endif - -static pid_t GetCurrentThreadPid_() -{ -#ifdef TARGET_FREEBSD - return pthread_getthreadid_np(); -#elif defined(TARGET_ANDROID) - return gettid(); -#elif TARGET_DARWIN - return pthread_mach_thread_np(pthread_self()); -#else - return syscall(SYS_gettid); -#endif -} - -#ifdef RLIMIT_NICE -// 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 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) - { - 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. - return std::min(maxPriority, bestUserSetPriority); // - } - else - // If we fail getting the limit for nice we just assume we can't raise the priority - return 0; -} -#endif +#include <pthread.h> std::unique_ptr<IThreadImpl> IThreadImpl::CreateThreadImpl(std::thread::native_handle_type handle) { return std::make_unique<CThreadImplPosix>(handle); } -CThreadImplPosix::CThreadImplPosix(std::thread::native_handle_type handle) - : IThreadImpl(handle), m_threadID(GetCurrentThreadPid_()) +CThreadImplPosix::CThreadImplPosix(std::thread::native_handle_type handle) : IThreadImpl(handle) { } @@ -122,58 +25,11 @@ void CThreadImplPosix::SetThreadInfo(const std::string& name) { #if defined(TARGET_DARWIN) pthread_setname_np(name.c_str()); -#elif defined(TARGET_LINUX) && defined(__GLIBC__) - // mthread must be set by here. - pthread_setname_np(m_handle, name.c_str()); -#endif - -#ifdef RLIMIT_NICE - // get user max prio - int userMaxPrio = GetUserMaxPriority(ThreadPriorityToNativePriority(ThreadPriority::HIGHEST)); - - // 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 - int appNice = getpriority(PRIO_PROCESS, getpid()); - if (setpriority(PRIO_PROCESS, m_threadID, appNice) != 0) - CLog::Log(LOGERROR, "{}: error {}", __FUNCTION__, strerror(errno)); - } #endif } bool CThreadImplPosix::SetPriority(const ThreadPriority& priority) { - bool bReturn = false; - - std::unique_lock<CCriticalSection> lockIt(m_criticalSection); - - if (!m_handle) - bReturn = false; -#ifdef RLIMIT_NICE - else - { - // get user max prio given max prio (will take the min) - int userMaxPrio = GetUserMaxPriority(ThreadPriorityToNativePriority(ThreadPriority::HIGHEST)); - - // keep priority in bounds - int prio = ThreadPriorityToNativePriority(priority); - if (prio >= ThreadPriorityToNativePriority(ThreadPriority::HIGHEST)) - prio = userMaxPrio; // this is already the min of GetMaxPriority and what the user can set. - if (prio < ThreadPriorityToNativePriority(ThreadPriority::LOWEST)) - prio = ThreadPriorityToNativePriority(ThreadPriority::LOWEST); - - // nice level of application - const int appNice = getpriority(PRIO_PROCESS, getpid()); - const int newNice = appNice - prio; - - if (setpriority(PRIO_PROCESS, m_threadID, newNice) == 0) - bReturn = true; - else - CLog::Log(LOGERROR, "{}: error {}", __FUNCTION__, strerror(errno)); - } -#endif - - return bReturn; + CLog::Log(LOGDEBUG, "[threads] setting priority is not supported on this platform"); + return false; } diff --git a/xbmc/platform/posix/threads/ThreadImplPosix.h b/xbmc/platform/posix/threads/ThreadImplPosix.h index 6e2a98de42..7bba5e141e 100644 --- a/xbmc/platform/posix/threads/ThreadImplPosix.h +++ b/xbmc/platform/posix/threads/ThreadImplPosix.h @@ -9,7 +9,6 @@ #pragma once #include "threads/IThreadImpl.h" -#include "threads/SingleLock.h" class CThreadImplPosix : public IThreadImpl { @@ -21,9 +20,4 @@ public: void SetThreadInfo(const std::string& name) override; bool SetPriority(const ThreadPriority& priority) override; - -private: - CCriticalSection m_criticalSection; - - pid_t m_threadID; }; |