// SPDX-FileCopyrightText: 2013 Dolphin Emulator Project // SPDX-FileCopyrightText: 2014 Citra Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include #include #include "common/error.h" #include "common/logging/log.h" #include "common/thread.h" #include "ntapi.h" #ifdef __APPLE__ #include #include #include #elif defined(_WIN32) #include #include "common/string_util.h" #else #if defined(__Bitrig__) || defined(__DragonFly__) || defined(__FreeBSD__) || defined(__OpenBSD__) #include #else #include #endif #include #endif #ifndef _WIN32 #include #endif #ifdef __FreeBSD__ #define cpu_set_t cpuset_t #endif namespace Common { #ifdef __APPLE__ void SetCurrentThreadRealtime(const std::chrono::nanoseconds period_ns) { // CPU time to grant. const std::chrono::nanoseconds computation_ns = period_ns / 2; // Determine the timebase for converting time to ticks. struct mach_timebase_info timebase {}; mach_timebase_info(&timebase); const auto ticks_per_ns = static_cast(timebase.denom) / static_cast(timebase.numer); const auto period_ticks = static_cast(static_cast(period_ns.count()) * ticks_per_ns); const auto computation_ticks = static_cast(static_cast(computation_ns.count()) * ticks_per_ns); thread_time_constraint_policy policy = { .period = period_ticks, .computation = computation_ticks, // Should not matter since preemptible is false, but needs to be >= computation regardless. .constraint = computation_ticks, .preemptible = false, }; int ret = thread_policy_set( pthread_mach_thread_np(pthread_self()), THREAD_TIME_CONSTRAINT_POLICY, reinterpret_cast(&policy), THREAD_TIME_CONSTRAINT_POLICY_COUNT); if (ret != KERN_SUCCESS) { LOG_ERROR(Common, "Could not set thread to real-time with period {} ns: {}", period_ns.count(), ret); } } #else void SetCurrentThreadRealtime(const std::chrono::nanoseconds period_ns) { // Not implemented } #endif #ifdef _WIN32 void SetCurrentThreadPriority(ThreadPriority new_priority) { auto handle = GetCurrentThread(); int windows_priority = 0; switch (new_priority) { case ThreadPriority::Low: windows_priority = THREAD_PRIORITY_BELOW_NORMAL; break; case ThreadPriority::Normal: windows_priority = THREAD_PRIORITY_NORMAL; break; case ThreadPriority::High: windows_priority = THREAD_PRIORITY_ABOVE_NORMAL; break; case ThreadPriority::VeryHigh: windows_priority = THREAD_PRIORITY_HIGHEST; break; case ThreadPriority::Critical: windows_priority = THREAD_PRIORITY_TIME_CRITICAL; break; default: windows_priority = THREAD_PRIORITY_NORMAL; break; } SetThreadPriority(handle, windows_priority); } static void AccurateSleep(std::chrono::nanoseconds duration) { LARGE_INTEGER interval{ .QuadPart = -1 * (duration.count() / 100u), }; HANDLE timer = ::CreateWaitableTimer(NULL, TRUE, NULL); SetWaitableTimer(timer, &interval, 0, NULL, NULL, 0); WaitForSingleObject(timer, INFINITE); ::CloseHandle(timer); } #else void SetCurrentThreadPriority(ThreadPriority new_priority) { pthread_t this_thread = pthread_self(); const auto scheduling_type = SCHED_OTHER; s32 max_prio = sched_get_priority_max(scheduling_type); s32 min_prio = sched_get_priority_min(scheduling_type); u32 level = std::max(static_cast(new_priority) + 1, 4U); struct sched_param params; if (max_prio > min_prio) { params.sched_priority = min_prio + ((max_prio - min_prio) * level) / 4; } else { params.sched_priority = min_prio - ((min_prio - max_prio) * level) / 4; } pthread_setschedparam(this_thread, scheduling_type, ¶ms); } static void AccurateSleep(std::chrono::nanoseconds duration) { std::this_thread::sleep_for(duration); } #endif #ifdef _MSC_VER // Sets the debugger-visible name of the current thread. void SetCurrentThreadName(const char* name) { SetThreadDescription(GetCurrentThread(), UTF8ToUTF16W(name).data()); } #else // !MSVC_VER, so must be POSIX threads // MinGW with the POSIX threading model does not support pthread_setname_np #if !defined(_WIN32) || defined(_MSC_VER) void SetCurrentThreadName(const char* name) { #ifdef __APPLE__ pthread_setname_np(name); #elif defined(__Bitrig__) || defined(__DragonFly__) || defined(__FreeBSD__) || defined(__OpenBSD__) pthread_set_name_np(pthread_self(), name); #elif defined(__NetBSD__) pthread_setname_np(pthread_self(), "%s", (void*)name); #elif defined(__linux__) // Linux limits thread names to 15 characters and will outright reject any // attempt to set a longer name with ERANGE. std::string truncated(name, std::min(strlen(name), static_cast(15))); if (int e = pthread_setname_np(pthread_self(), truncated.c_str())) { errno = e; LOG_ERROR(Common, "Failed to set thread name to '{}': {}", truncated, GetLastErrorMsg()); } #else pthread_setname_np(pthread_self(), name); #endif } #endif #if defined(_WIN32) void SetCurrentThreadName(const char*) { // Do Nothing on MingW } #endif #endif AccurateTimer::AccurateTimer(std::chrono::nanoseconds target_interval) : target_interval(target_interval) {} void AccurateTimer::Start() { auto begin_sleep = std::chrono::high_resolution_clock::now(); if (total_wait.count() > 0) { AccurateSleep(total_wait); } start_time = std::chrono::high_resolution_clock::now(); total_wait -= std::chrono::duration_cast(start_time - begin_sleep); } void AccurateTimer::End() { auto now = std::chrono::high_resolution_clock::now(); total_wait += target_interval - std::chrono::duration_cast(now - start_time); } } // namespace Common