From 52f5ca7e286099cc8e72518baf4b7dfcf50b6be5 Mon Sep 17 00:00:00 2001
From: Connor McLaughlin <stenzek@gmail.com>
Date: Tue, 26 Jan 2021 02:20:38 +1000
Subject: [PATCH] Common/Timer: Add additional sleep functions

---
 src/common/timer.cpp | 124 ++++++++++++++++++++++++++++++++++++++++++-
 src/common/timer.h   |   9 +++-
 2 files changed, 130 insertions(+), 3 deletions(-)

diff --git a/src/common/timer.cpp b/src/common/timer.cpp
index 84589eaf8..fd668492c 100644
--- a/src/common/timer.cpp
+++ b/src/common/timer.cpp
@@ -1,10 +1,13 @@
 #include "timer.h"
+#include <cstdio>
+#include <cstdlib>
 
 #ifdef WIN32
 #include "windows_headers.h"
 #else
 #include <sys/time.h>
 #include <time.h>
+#include <unistd.h>
 #endif
 
 namespace Common {
@@ -45,6 +48,21 @@ double Timer::ConvertValueToSeconds(Timer::Value value)
   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
 
 #if 1 // using clock_gettime()
@@ -71,6 +89,21 @@ double Timer::ConvertValueToSeconds(Timer::Value value)
   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()
 
 Timer::Value Timer::GetValue()
@@ -95,6 +128,21 @@ double Timer::ConvertValueToSeconds(Timer::Value value)
   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
@@ -124,4 +172,78 @@ double Timer::GetTimeNanoseconds() const
   return ConvertValueToNanoseconds(GetValue() - m_tvStartValue);
 }
 
-} // namespace Common
\ No newline at end of file
+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
diff --git a/src/common/timer.h b/src/common/timer.h
index 5d105d868..3afa1e2ce 100644
--- a/src/common/timer.h
+++ b/src/common/timer.h
@@ -1,5 +1,4 @@
 #pragma once
-#include "types.h"
 #include <cstdint>
 
 namespace Common {
@@ -7,7 +6,7 @@ namespace Common {
 class Timer
 {
 public:
-  using Value = u64;
+  using Value = std::uint64_t;
 
   Timer();
 
@@ -15,6 +14,12 @@ public:
   static double ConvertValueToSeconds(Value value);
   static double ConvertValueToMilliseconds(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();