Common/Timer: Add additional sleep functions

This commit is contained in:
Connor McLaughlin 2021-01-26 02:20:38 +10:00
parent 0d473e8681
commit 52f5ca7e28
2 changed files with 130 additions and 3 deletions

View file

@ -1,10 +1,13 @@
#include "timer.h" #include "timer.h"
#include <cstdio>
#include <cstdlib>
#ifdef WIN32 #ifdef WIN32
#include "windows_headers.h" #include "windows_headers.h"
#else #else
#include <sys/time.h> #include <sys/time.h>
#include <time.h> #include <time.h>
#include <unistd.h>
#endif #endif
namespace Common { namespace Common {
@ -45,6 +48,21 @@ double Timer::ConvertValueToSeconds(Timer::Value value)
return ((static_cast<double>(value) / s_counter_frequency) / 1000000000.0); return ((static_cast<double>(value) / s_counter_frequency) / 1000000000.0);
} }
Timer::Value Timer::ConvertSecondsToValue(double s)
{
return static_cast<Value>((s * 1000000000.0) * s_counter_frequency);
}
Timer::Value Timer::ConvertMillisecondsToValue(double ms)
{
return static_cast<Value>((ms * 1000000.0) * s_counter_frequency);
}
Timer::Value Timer::ConvertNanosecondsToValue(double ns)
{
return static_cast<Value>(ns * s_counter_frequency);
}
#else #else
#if 1 // using clock_gettime() #if 1 // using clock_gettime()
@ -71,6 +89,21 @@ double Timer::ConvertValueToSeconds(Timer::Value value)
return (static_cast<double>(value) / 1000000000.0); return (static_cast<double>(value) / 1000000000.0);
} }
Timer::Value Timer::ConvertSecondsToValue(double s)
{
return static_cast<Value>(s * 1000000000.0);
}
Timer::Value Timer::ConvertMillisecondsToValue(double ms)
{
return static_cast<Value>(ms * 1000000.0);
}
Timer::Value Timer::ConvertNanosecondsToValue(double ns)
{
return static_cast<Value>(ns);
}
#else // using gettimeofday() #else // using gettimeofday()
Timer::Value Timer::GetValue() Timer::Value Timer::GetValue()
@ -95,6 +128,21 @@ double Timer::ConvertValueToSeconds(Timer::Value value)
return ((double)value / 1000000.0); return ((double)value / 1000000.0);
} }
Timer::Value Timer::ConvertSecondsToValue(double s)
{
return static_cast<Value>(ms * 1000000.0);
}
Timer::Value Timer::ConvertMillisecondsToValue(double ms)
{
return static_cast<Value>(ms * 1000.0);
}
Timer::Value Timer::ConvertNanosecondsToValue(double ns)
{
return static_cast<Value>(ns / 1000.0);
}
#endif #endif
#endif #endif
@ -124,4 +172,78 @@ double Timer::GetTimeNanoseconds() const
return ConvertValueToNanoseconds(GetValue() - m_tvStartValue); return ConvertValueToNanoseconds(GetValue() - m_tvStartValue);
} }
void Timer::BusyWait(std::uint64_t ns)
{
const Value start = GetValue();
const Value end = start + ConvertNanosecondsToValue(static_cast<double>(ns));
if (end < start)
{
// overflow, unlikely
while (GetValue() > end)
;
}
while (GetValue() < end)
;
}
void Timer::HybridSleep(std::uint64_t ns, std::uint64_t min_sleep_time)
{
const std::uint64_t start = GetValue();
const std::uint64_t end = start + ConvertNanosecondsToValue(static_cast<double>(ns));
if (end < start)
{
// overflow, unlikely
while (GetValue() > end)
;
}
std::uint64_t current = GetValue();
while (current < end)
{
const std::uint64_t remaining = end - current;
if (remaining >= min_sleep_time)
NanoSleep(min_sleep_time);
current = GetValue();
}
}
void Timer::NanoSleep(std::uint64_t ns)
{
#if defined(WIN32)
static HANDLE throttle_timer;
static bool throttle_timer_created = false;
if (!throttle_timer_created)
{
throttle_timer_created = true;
throttle_timer = CreateWaitableTimer(nullptr, TRUE, nullptr);
if (throttle_timer)
std::atexit([]() { CloseHandle(throttle_timer); });
else
std::fprintf(stderr, "CreateWaitableTimer() failed, falling back to Sleep()\n");
}
if (throttle_timer)
{
LARGE_INTEGER due_time;
due_time.QuadPart = -static_cast<std::int64_t>(static_cast<std::uint64_t>(ns) / 100u);
if (SetWaitableTimer(throttle_timer, &due_time, 0, nullptr, nullptr, FALSE))
WaitForSingleObject(throttle_timer, INFINITE);
else
std::fprintf(stderr, "SetWaitableTimer() failed: %08X\n", GetLastError());
}
else
{
Sleep(static_cast<std::uint32_t>(ns / 1000000));
}
#elif defined(__ANDROID__)
// Round down to the next millisecond.
usleep(static_cast<useconds_t>((ns / 1000000) * 1000));
#else
const struct timespec ts = {0, static_cast<long>(ns)};
nanosleep(&ts, nullptr);
#endif
}
} // namespace Common } // namespace Common

View file

@ -1,5 +1,4 @@
#pragma once #pragma once
#include "types.h"
#include <cstdint> #include <cstdint>
namespace Common { namespace Common {
@ -7,7 +6,7 @@ namespace Common {
class Timer class Timer
{ {
public: public:
using Value = u64; using Value = std::uint64_t;
Timer(); Timer();
@ -15,6 +14,12 @@ public:
static double ConvertValueToSeconds(Value value); static double ConvertValueToSeconds(Value value);
static double ConvertValueToMilliseconds(Value value); static double ConvertValueToMilliseconds(Value value);
static double ConvertValueToNanoseconds(Value value); static double ConvertValueToNanoseconds(Value value);
static Value ConvertSecondsToValue(double s);
static Value ConvertMillisecondsToValue(double s);
static Value ConvertNanosecondsToValue(double ns);
static void BusyWait(std::uint64_t ns);
static void HybridSleep(std::uint64_t ns, std::uint64_t min_sleep_time = UINT64_C(2000000));
static void NanoSleep(std::uint64_t ns);
void Reset(); void Reset();