diff --git a/common/Darwin/DarwinThreads.cpp b/common/Darwin/DarwinThreads.cpp index 28ed4894eb..016d56fb53 100644 --- a/common/Darwin/DarwinThreads.cpp +++ b/common/Darwin/DarwinThreads.cpp @@ -94,6 +94,52 @@ u64 Threading::GetThreadCpuTime() return us; } +Threading::ThreadHandle::ThreadHandle() = default; + +Threading::ThreadHandle::ThreadHandle(const ThreadHandle& handle) + : m_native_handle(handle.m_native_handle) +{ +} + +Threading::ThreadHandle::ThreadHandle(ThreadHandle&& handle) + : m_native_handle(handle.m_native_handle) +{ + handle.m_native_handle = nullptr; +} + +Threading::ThreadHandle::~ThreadHandle() = default; + +Threading::ThreadHandle Threading::ThreadHandle::GetForCallingThread() +{ + ThreadHandle ret; + ret.m_native_handle = pthread_self(); + return ret; +} + +Threading::ThreadHandle& Threading::ThreadHandle::operator=(ThreadHandle&& handle) +{ + m_native_handle = handle.m_native_handle; + handle.m_native_handle = nullptr; + return *this; +} + +Threading::ThreadHandle& Threading::ThreadHandle::operator=(const ThreadHandle& handle) +{ + m_native_handle = handle.m_native_handle; + return *this; +} + +u64 Threading::ThreadHandle::GetCPUTime() const +{ + return getthreadtime(pthread_mach_thread_np((pthread_t)m_native_handle)); +} + +bool Threading::ThreadHandle::SetAffinity(u64 processor_mask) const +{ + // Doesn't appear to be possible to set affinity. + return false; +} + u64 Threading::pxThread::GetCpuTime() const { // Get the cpu time for the thread belonging to this object. Use m_native_id and/or diff --git a/common/Linux/LnxThreads.cpp b/common/Linux/LnxThreads.cpp index f57eb31a86..280f36d320 100644 --- a/common/Linux/LnxThreads.cpp +++ b/common/Linux/LnxThreads.cpp @@ -14,9 +14,22 @@ */ #if !defined(_WIN32) && !defined(__APPLE__) +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + #include #if defined(__linux__) #include +#include +#include + +// glibc < v2.30 doesn't define gettid... +#if __GLIBC__ == 2 && __GLIBC_MINOR__ < 30 +#include +#define gettid() syscall(SYS_gettid) +#endif + #elif defined(__unix__) #include #endif @@ -96,6 +109,96 @@ u64 Threading::GetThreadCpuTime() return get_thread_time(); } +Threading::ThreadHandle::ThreadHandle() = default; + +Threading::ThreadHandle::ThreadHandle(const ThreadHandle& handle) + : m_native_handle(handle.m_native_handle) +#ifdef __linux__ + , m_native_id(handle.m_native_id) +#endif +{ +} + +Threading::ThreadHandle::ThreadHandle(ThreadHandle&& handle) + : m_native_handle(handle.m_native_handle) +#ifdef __linux__ + , m_native_id(handle.m_native_id) +#endif +{ + handle.m_native_handle = nullptr; +#ifdef __linux__ + handle.m_native_id = 0; +#endif +} + +Threading::ThreadHandle::~ThreadHandle() = default; + +Threading::ThreadHandle Threading::ThreadHandle::GetForCallingThread() +{ + ThreadHandle ret; + ret.m_native_handle = (void*)pthread_self(); +#ifdef __linux__ + ret.m_native_id = gettid(); +#endif + return ret; +} + +Threading::ThreadHandle& Threading::ThreadHandle::operator=(ThreadHandle&& handle) +{ + m_native_handle = handle.m_native_handle; + handle.m_native_handle = nullptr; +#ifdef __linux__ + m_native_id = handle.m_native_id; + handle.m_native_id = 0; +#endif + return *this; +} + +Threading::ThreadHandle& Threading::ThreadHandle::operator=(const ThreadHandle& handle) +{ + m_native_handle = handle.m_native_handle; +#ifdef __linux__ + m_native_id = handle.m_native_id; +#endif + return *this; +} + +u64 Threading::ThreadHandle::GetCPUTime() const +{ + return m_native_handle ? get_thread_time((uptr)m_native_handle) : 0; +} + +bool Threading::ThreadHandle::SetAffinity(u64 processor_mask) const +{ +#if defined(__linux__) + cpu_set_t set; + CPU_ZERO(&set); + + if (processor_mask != 0) + { + for (u32 i = 0; i < 64; i++) + { + if (processor_mask & (static_cast(1) << i)) + { + CPU_SET(i, &set); + } + } + } + else + { + long num_processors = sysconf(_SC_NPROCESSORS_CONF); + for (long i = 0; i < num_processors; i++) + { + CPU_SET(i, &set); + } + } + + return sched_setaffinity((pid_t)m_native_id, sizeof(set), &set) >= 0; +#else + return false; +#endif +} + u64 Threading::pxThread::GetCpuTime() const { // Get the cpu time for the thread belonging to this object. Use m_native_id and/or diff --git a/common/Threading.h b/common/Threading.h index 64ba4dc1be..b74f96ea89 100644 --- a/common/Threading.h +++ b/common/Threading.h @@ -90,6 +90,7 @@ class wxTimeSpan; namespace Threading { + class ThreadHandle; class pxThread; class RwMutex; @@ -198,6 +199,45 @@ namespace Threading }; #endif + // -------------------------------------------------------------------------------------- + // ThreadHandle + // -------------------------------------------------------------------------------------- + // Abstracts an OS's handle to a thread, closing the handle when necessary. Currently, + // only used for getting the CPU time for a thread. + // + class ThreadHandle + { + public: + ThreadHandle(); + ThreadHandle(ThreadHandle&& handle); + ThreadHandle(const ThreadHandle& handle); + ~ThreadHandle(); + + /// Returns a new handle for the calling thread. + static ThreadHandle GetForCallingThread(); + + ThreadHandle& operator=(ThreadHandle&& handle); + ThreadHandle& operator=(const ThreadHandle& handle); + + operator void*() const { return m_native_handle; } + operator bool() const { return (m_native_handle != nullptr); } + + /// Returns the amount of CPU time consumed by the thread, at the GetThreadTicksPerSecond() frequency. + u64 GetCPUTime() const; + + /// Sets the affinity for a thread to the specified processors. + /// Obviously, only works up to 64 processors. + bool SetAffinity(u64 processor_mask) const; + + private: + void* m_native_handle = nullptr; + + // We need the thread ID for affinity adjustments on Linux. +#if defined(__linux__) + unsigned int m_native_id = 0; +#endif + }; + // -------------------------------------------------------------------------------------- // NonblockingMutex // -------------------------------------------------------------------------------------- diff --git a/common/Windows/WinThreads.cpp b/common/Windows/WinThreads.cpp index 8b0f381f61..938af62db3 100644 --- a/common/Windows/WinThreads.cpp +++ b/common/Windows/WinThreads.cpp @@ -47,6 +47,84 @@ __fi void Threading::DisableHiresScheduler() timeEndPeriod(1); } +Threading::ThreadHandle::ThreadHandle() = default; + +Threading::ThreadHandle::ThreadHandle(const ThreadHandle& handle) +{ + if (handle.m_native_handle) + { + HANDLE new_handle; + if (DuplicateHandle(GetCurrentProcess(), (HANDLE)handle.m_native_handle, + GetCurrentProcess(), &new_handle, THREAD_QUERY_INFORMATION | THREAD_SET_LIMITED_INFORMATION, FALSE, 0)) + { + m_native_handle = (void*)new_handle; + } + } +} + +Threading::ThreadHandle::ThreadHandle(ThreadHandle&& handle) + : m_native_handle(handle.m_native_handle) +{ + handle.m_native_handle = nullptr; +} + + +Threading::ThreadHandle::~ThreadHandle() +{ + if (m_native_handle) + CloseHandle(m_native_handle); +} + +Threading::ThreadHandle Threading::ThreadHandle::GetForCallingThread() +{ + ThreadHandle ret; + ret.m_native_handle = (void*)OpenThread(THREAD_QUERY_INFORMATION | THREAD_SET_LIMITED_INFORMATION, FALSE, GetCurrentThreadId()); + return ret; +} + +Threading::ThreadHandle& Threading::ThreadHandle::operator=(ThreadHandle&& handle) +{ + if (m_native_handle) + CloseHandle((HANDLE)m_native_handle); + m_native_handle = handle.m_native_handle; + handle.m_native_handle = nullptr; + return *this; +} + +Threading::ThreadHandle& Threading::ThreadHandle::operator=(const ThreadHandle& handle) +{ + if (m_native_handle) + { + CloseHandle((HANDLE)m_native_handle); + m_native_handle = nullptr; + } + + HANDLE new_handle; + if (DuplicateHandle(GetCurrentProcess(), (HANDLE)handle.m_native_handle, + GetCurrentProcess(), &new_handle, THREAD_QUERY_INFORMATION | THREAD_SET_LIMITED_INFORMATION, FALSE, 0)) + { + m_native_handle = (void*)new_handle; + } + + return *this; +} + +u64 Threading::ThreadHandle::GetCPUTime() const +{ + u64 ret = 0; + if (m_native_handle) + QueryThreadCycleTime((HANDLE)m_native_handle, &ret); + return ret; +} + +bool Threading::ThreadHandle::SetAffinity(u64 processor_mask) const +{ + if (processor_mask == 0) + processor_mask = ~processor_mask; + + return (SetThreadAffinityMask(GetCurrentThread(), (DWORD_PTR)processor_mask) != 0 || GetLastError() != ERROR_SUCCESS); +} + u64 Threading::GetThreadCpuTime() { u64 ret = 0;