Common: Add Threading::SleepUntil()

This commit is contained in:
Connor McLaughlin 2022-12-04 14:52:26 +10:00 committed by refractionpcsx2
parent a346cff472
commit aea6a9f534
7 changed files with 93 additions and 18 deletions

View File

@ -19,11 +19,13 @@
#include <cstdlib> #include <cstdlib>
#include <sys/types.h> #include <sys/types.h>
#include <sys/sysctl.h> #include <sys/sysctl.h>
#include <time.h>
#include <mach/mach_time.h> #include <mach/mach_time.h>
#include <IOKit/pwr_mgt/IOPMLib.h> #include <IOKit/pwr_mgt/IOPMLib.h>
#include "common/Pcsx2Types.h" #include "common/Pcsx2Types.h"
#include "common/General.h" #include "common/General.h"
#include "common/Threading.h"
#include "common/WindowInfo.h" #include "common/WindowInfo.h"
// Darwin (OSX) is a bit different from Linux when requesting properties of // Darwin (OSX) is a bit different from Linux when requesting properties of
@ -47,13 +49,13 @@ u64 GetPhysicalMemory()
} }
static u64 tickfreq; static u64 tickfreq;
static mach_timebase_info_data_t s_timebase_info;
void InitCPUTicks() void InitCPUTicks()
{ {
mach_timebase_info_data_t info; if (mach_timebase_info(&s_timebase_info) != KERN_SUCCESS)
if (mach_timebase_info(&info) != KERN_SUCCESS)
abort(); abort();
tickfreq = (u64)1e9 * (u64)info.denom / (u64)info.numer; tickfreq = (u64)1e9 * (u64)s_timebase_info.denom / (u64)s_timebase_info.numer;
} }
// returns the performance-counter frequency: ticks per second (Hz) // returns the performance-counter frequency: ticks per second (Hz)
@ -110,4 +112,27 @@ bool WindowInfo::InhibitScreensaver(const WindowInfo& wi, bool inhibit)
return true; return true;
} }
void Threading::Sleep(int ms)
{
usleep(1000 * ms);
}
void Threading::SleepUntil(u64 ticks)
{
// This is definitely sub-optimal, but apparently clock_nanosleep() doesn't exist.
const s64 diff = static_cast<s64>(ticks - GetCPUTicks());
if (diff <= 0)
return;
const u64 nanos = (static_cast<u64>(diff) * static_cast<u64>(s_timebase_info.denom)) / static_cast<u64>(s_timebase_info.numer);
if (nanos == 0)
return;
struct timespec ts;
ts.tv_sec = nanos / 1000000000ULL;
ts.tv_nsec = nanos % 1000000000ULL;
nanosleep(&ts, nullptr);
}
#endif #endif

View File

@ -30,11 +30,6 @@
// the LOCK prefix. The prefix works on single core CPUs fine (but is slow), but not // the LOCK prefix. The prefix works on single core CPUs fine (but is slow), but not
// having the LOCK prefix is very bad indeed. // having the LOCK prefix is very bad indeed.
__forceinline void Threading::Sleep(int ms)
{
usleep(1000 * ms);
}
__forceinline void Threading::Timeslice() __forceinline void Threading::Timeslice()
{ {
sched_yield(); sched_yield();

View File

@ -28,6 +28,7 @@
#include "common/Pcsx2Types.h" #include "common/Pcsx2Types.h"
#include "common/General.h" #include "common/General.h"
#include "common/StringUtil.h" #include "common/StringUtil.h"
#include "common/Threading.h"
#include "common/WindowInfo.h" #include "common/WindowInfo.h"
// Returns 0 on failure (not supported by the operating system). // Returns 0 on failure (not supported by the operating system).
@ -149,4 +150,17 @@ bool Common::PlaySoundAsync(const char* path)
#endif #endif
} }
void Threading::Sleep(int ms)
{
usleep(1000 * ms);
}
void Threading::SleepUntil(u64 ticks)
{
struct timespec ts;
ts.tv_sec = static_cast<time_t>(ticks / 1000000000ULL);
ts.tv_nsec = static_cast<long>(ticks % 1000000000ULL);
clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &ts, nullptr);
}
#endif #endif

View File

@ -54,11 +54,6 @@
// the LOCK prefix. The prefix works on single core CPUs fine (but is slow), but not // the LOCK prefix. The prefix works on single core CPUs fine (but is slow), but not
// having the LOCK prefix is very bad indeed. // having the LOCK prefix is very bad indeed.
__forceinline void Threading::Sleep(int ms)
{
usleep(1000 * ms);
}
__forceinline void Threading::Timeslice() __forceinline void Threading::Timeslice()
{ {
sched_yield(); sched_yield();

View File

@ -55,6 +55,9 @@ namespace Threading
// sleeps the current thread for the given number of milliseconds. // sleeps the current thread for the given number of milliseconds.
extern void Sleep(int ms); extern void Sleep(int ms);
// sleeps the current thread until the specified time point, or later.
extern void SleepUntil(u64 ticks);
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
// ThreadHandle // ThreadHandle
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------

View File

@ -19,6 +19,7 @@
#include "common/RedtapeWindows.h" #include "common/RedtapeWindows.h"
#include "common/Exceptions.h" #include "common/Exceptions.h"
#include "common/StringUtil.h" #include "common/StringUtil.h"
#include "common/Threading.h"
#include "common/General.h" #include "common/General.h"
#include "common/WindowInfo.h" #include "common/WindowInfo.h"
@ -30,6 +31,23 @@
alignas(16) static LARGE_INTEGER lfreq; alignas(16) static LARGE_INTEGER lfreq;
// This gets leaked... oh well.
static thread_local HANDLE s_sleep_timer;
static thread_local bool s_sleep_timer_created = false;
static HANDLE GetSleepTimer()
{
if (s_sleep_timer_created)
return s_sleep_timer;
s_sleep_timer_created = true;
s_sleep_timer = CreateWaitableTimerEx(nullptr, nullptr, CREATE_WAITABLE_TIMER_HIGH_RESOLUTION, TIMER_ALL_ACCESS);
if (!s_sleep_timer)
s_sleep_timer = CreateWaitableTimer(nullptr, TRUE, nullptr);
return s_sleep_timer;
}
void InitCPUTicks() void InitCPUTicks()
{ {
QueryPerformanceFrequency(&lfreq); QueryPerformanceFrequency(&lfreq);
@ -91,4 +109,34 @@ bool Common::PlaySoundAsync(const char* path)
return PlaySoundW(wpath.c_str(), NULL, SND_ASYNC | SND_NODEFAULT); return PlaySoundW(wpath.c_str(), NULL, SND_ASYNC | SND_NODEFAULT);
} }
void Threading::Sleep(int ms)
{
::Sleep(ms);
}
void Threading::SleepUntil(u64 ticks)
{
// This is definitely sub-optimal, but there's no way to sleep until a QPC timestamp on Win32.
const s64 diff = static_cast<s64>(ticks - GetCPUTicks());
if (diff <= 0)
return;
const HANDLE hTimer = GetSleepTimer();
if (!hTimer)
return;
const u64 one_hundred_nanos_diff = (static_cast<u64>(diff) * 10000000ULL) / GetTickFrequency();
if (one_hundred_nanos_diff == 0)
return;
LARGE_INTEGER fti;
fti.QuadPart = -static_cast<s64>(one_hundred_nanos_diff);
if (SetWaitableTimer(hTimer, &fti, 0, nullptr, nullptr, FALSE))
{
WaitForSingleObject(hTimer, INFINITE);
return;
}
}
#endif #endif

View File

@ -23,11 +23,6 @@
#include <process.h> #include <process.h>
#include <timeapi.h> #include <timeapi.h>
__fi void Threading::Sleep(int ms)
{
::Sleep(ms);
}
__fi void Threading::Timeslice() __fi void Threading::Timeslice()
{ {
::Sleep(0); ::Sleep(0);