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 <sys/types.h>
#include <sys/sysctl.h>
#include <time.h>
#include <mach/mach_time.h>
#include <IOKit/pwr_mgt/IOPMLib.h>
#include "common/Pcsx2Types.h"
#include "common/General.h"
#include "common/Threading.h"
#include "common/WindowInfo.h"
// Darwin (OSX) is a bit different from Linux when requesting properties of
@ -47,13 +49,13 @@ u64 GetPhysicalMemory()
}
static u64 tickfreq;
static mach_timebase_info_data_t s_timebase_info;
void InitCPUTicks()
{
mach_timebase_info_data_t info;
if (mach_timebase_info(&info) != KERN_SUCCESS)
if (mach_timebase_info(&s_timebase_info) != KERN_SUCCESS)
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)
@ -110,4 +112,27 @@ bool WindowInfo::InhibitScreensaver(const WindowInfo& wi, bool inhibit)
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

View File

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

View File

@ -28,6 +28,7 @@
#include "common/Pcsx2Types.h"
#include "common/General.h"
#include "common/StringUtil.h"
#include "common/Threading.h"
#include "common/WindowInfo.h"
// Returns 0 on failure (not supported by the operating system).
@ -149,4 +150,17 @@ bool Common::PlaySoundAsync(const char* path)
#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

View File

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

View File

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

View File

@ -19,6 +19,7 @@
#include "common/RedtapeWindows.h"
#include "common/Exceptions.h"
#include "common/StringUtil.h"
#include "common/Threading.h"
#include "common/General.h"
#include "common/WindowInfo.h"
@ -30,6 +31,23 @@
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()
{
QueryPerformanceFrequency(&lfreq);
@ -91,4 +109,34 @@ bool Common::PlaySoundAsync(const char* path)
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

View File

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