timer.cpp (9162B)
1 // SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com> 2 // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) 3 4 #include "timer.h" 5 #include "types.h" 6 #include <cstdio> 7 #include <cstdlib> 8 9 #ifdef _WIN32 10 #include "windows_headers.h" 11 #else 12 #include <errno.h> 13 #include <sys/time.h> 14 #include <time.h> 15 #include <unistd.h> 16 #endif 17 18 namespace Common { 19 20 #ifdef _WIN32 21 22 static double s_counter_frequency; 23 static bool s_counter_initialized = false; 24 25 // This gets leaked... oh well. 26 static thread_local HANDLE s_sleep_timer; 27 static thread_local bool s_sleep_timer_created = false; 28 29 static HANDLE GetSleepTimer() 30 { 31 if (s_sleep_timer_created) 32 return s_sleep_timer; 33 34 s_sleep_timer_created = true; 35 s_sleep_timer = CreateWaitableTimerEx(nullptr, nullptr, CREATE_WAITABLE_TIMER_HIGH_RESOLUTION, TIMER_ALL_ACCESS); 36 if (!s_sleep_timer) 37 { 38 s_sleep_timer = CreateWaitableTimer(nullptr, TRUE, nullptr); 39 if (!s_sleep_timer) 40 std::fprintf(stderr, "CreateWaitableTimer() failed, falling back to Sleep()\n"); 41 } 42 43 return s_sleep_timer; 44 } 45 46 double Timer::GetFrequency() 47 { 48 // even if this races, it should still result in the same value.. 49 if (!s_counter_initialized) 50 { 51 LARGE_INTEGER Freq; 52 QueryPerformanceFrequency(&Freq); 53 s_counter_frequency = static_cast<double>(Freq.QuadPart) / 1000000000.0; 54 s_counter_initialized = true; 55 } 56 57 return s_counter_frequency; 58 } 59 60 Timer::Value Timer::GetCurrentValue() 61 { 62 Timer::Value ReturnValue; 63 QueryPerformanceCounter(reinterpret_cast<LARGE_INTEGER*>(&ReturnValue)); 64 return ReturnValue; 65 } 66 67 double Timer::ConvertValueToNanoseconds(Timer::Value value) 68 { 69 return (static_cast<double>(value) / GetFrequency()); 70 } 71 72 double Timer::ConvertValueToMilliseconds(Timer::Value value) 73 { 74 return ((static_cast<double>(value) / GetFrequency()) / 1000000.0); 75 } 76 77 double Timer::ConvertValueToSeconds(Timer::Value value) 78 { 79 return ((static_cast<double>(value) / GetFrequency()) / 1000000000.0); 80 } 81 82 Timer::Value Timer::ConvertSecondsToValue(double s) 83 { 84 return static_cast<Value>((s * 1000000000.0) * GetFrequency()); 85 } 86 87 Timer::Value Timer::ConvertMillisecondsToValue(double ms) 88 { 89 return static_cast<Value>((ms * 1000000.0) * GetFrequency()); 90 } 91 92 Timer::Value Timer::ConvertNanosecondsToValue(double ns) 93 { 94 return static_cast<Value>(ns * GetFrequency()); 95 } 96 97 void Timer::SleepUntil(Value value, bool exact) 98 { 99 if (exact) 100 { 101 // Even with the high-precision timer, it's not precise enough to wake us up *exactly* when we want 102 // to. Dropping off the last 0.5ms and spinning for it seems enough on my system (Win11 22H2). 103 const Value wake_at = value - ConvertMillisecondsToValue(0.5); 104 Value current = GetCurrentValue(); 105 if (wake_at > current) 106 SleepUntil(wake_at, false); 107 108 // And spin off whatever time is left. 109 do 110 { 111 current = GetCurrentValue(); 112 } while (current < value); 113 } 114 else 115 { 116 const s64 diff = static_cast<s64>(value - GetCurrentValue()); 117 if (diff <= 0) 118 return; 119 120 HANDLE timer = GetSleepTimer(); 121 if (timer) 122 { 123 const u64 one_hundred_nanos_diff = static_cast<u64>(ConvertValueToNanoseconds(diff) / 100.0); 124 if (one_hundred_nanos_diff == 0) 125 return; 126 127 LARGE_INTEGER fti; 128 fti.QuadPart = -static_cast<s64>(one_hundred_nanos_diff); 129 130 if (SetWaitableTimer(timer, &fti, 0, nullptr, nullptr, FALSE)) 131 { 132 WaitForSingleObject(timer, INFINITE); 133 return; 134 } 135 } 136 137 // falling back to sleep... bad. 138 Sleep(static_cast<DWORD>(static_cast<u64>(diff) / 1000000)); 139 } 140 } 141 142 #else 143 144 double Timer::GetFrequency() 145 { 146 return 1.0; 147 } 148 149 Timer::Value Timer::GetCurrentValue() 150 { 151 struct timespec tv; 152 clock_gettime(CLOCK_MONOTONIC, &tv); 153 return ((Value)tv.tv_nsec + (Value)tv.tv_sec * 1000000000); 154 } 155 156 double Timer::ConvertValueToNanoseconds(Timer::Value value) 157 { 158 return static_cast<double>(value); 159 } 160 161 double Timer::ConvertValueToMilliseconds(Timer::Value value) 162 { 163 return (static_cast<double>(value) / 1000000.0); 164 } 165 166 double Timer::ConvertValueToSeconds(Timer::Value value) 167 { 168 return (static_cast<double>(value) / 1000000000.0); 169 } 170 171 Timer::Value Timer::ConvertSecondsToValue(double s) 172 { 173 return static_cast<Value>(s * 1000000000.0); 174 } 175 176 Timer::Value Timer::ConvertMillisecondsToValue(double ms) 177 { 178 return static_cast<Value>(ms * 1000000.0); 179 } 180 181 Timer::Value Timer::ConvertNanosecondsToValue(double ns) 182 { 183 return static_cast<Value>(ns); 184 } 185 186 void Timer::SleepUntil(Value value, bool exact) 187 { 188 if (exact) 189 { 190 static constexpr Value min_sleep_time = static_cast<Value>(0.5 * 1000000); 191 const Value wake_at = value - min_sleep_time; 192 Value current = GetCurrentValue(); 193 if (wake_at > current) 194 SleepUntil(wake_at, false); 195 196 // And spin off whatever time is left. 197 do 198 { 199 current = GetCurrentValue(); 200 } while (current < value); 201 } 202 else 203 { 204 // Apple doesn't have TIMER_ABSTIME, so fall back to nanosleep in such a case. 205 #ifdef __APPLE__ 206 for (;;) 207 { 208 const Value current_time = GetCurrentValue(); 209 if (value <= current_time) 210 return; 211 212 const Value diff = value - current_time; 213 struct timespec ts; 214 ts.tv_sec = diff / UINT64_C(1000000000); 215 ts.tv_nsec = diff % UINT64_C(1000000000); 216 217 // nanosleep() can return EINTR if interrupted by a signal. 218 if (nanosleep(&ts, nullptr) == EINTR) 219 continue; 220 else 221 break; 222 } 223 #else 224 struct timespec ts; 225 ts.tv_sec = value / UINT64_C(1000000000); 226 ts.tv_nsec = value % UINT64_C(1000000000); 227 228 for (;;) 229 { 230 // clock_nanosleep() can return EINTR if interrupted by a signal. 231 if (clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &ts, nullptr) == EINTR) 232 continue; 233 else 234 break; 235 } 236 #endif 237 } 238 } 239 240 #endif 241 242 Timer::Timer() 243 { 244 Reset(); 245 } 246 247 void Timer::Reset() 248 { 249 m_tvStartValue = GetCurrentValue(); 250 } 251 252 double Timer::GetTimeSeconds() const 253 { 254 return ConvertValueToSeconds(GetCurrentValue() - m_tvStartValue); 255 } 256 257 double Timer::GetTimeMilliseconds() const 258 { 259 return ConvertValueToMilliseconds(GetCurrentValue() - m_tvStartValue); 260 } 261 262 double Timer::GetTimeNanoseconds() const 263 { 264 return ConvertValueToNanoseconds(GetCurrentValue() - m_tvStartValue); 265 } 266 267 double Timer::GetTimeSecondsAndReset() 268 { 269 const Value value = GetCurrentValue(); 270 const double ret = ConvertValueToSeconds(value - m_tvStartValue); 271 m_tvStartValue = value; 272 return ret; 273 } 274 275 double Timer::GetTimeMillisecondsAndReset() 276 { 277 const Value value = GetCurrentValue(); 278 const double ret = ConvertValueToMilliseconds(value - m_tvStartValue); 279 m_tvStartValue = value; 280 return ret; 281 } 282 283 double Timer::GetTimeNanosecondsAndReset() 284 { 285 const Value value = GetCurrentValue(); 286 const double ret = ConvertValueToNanoseconds(value - m_tvStartValue); 287 m_tvStartValue = value; 288 return ret; 289 } 290 291 bool Timer::ResetIfSecondsPassed(double s) 292 { 293 const Value value = GetCurrentValue(); 294 const double ret = ConvertValueToSeconds(value - m_tvStartValue); 295 if (ret < s) 296 return false; 297 298 m_tvStartValue = value; 299 return true; 300 } 301 302 bool Timer::ResetIfMillisecondsPassed(double s) 303 { 304 const Value value = GetCurrentValue(); 305 const double ret = ConvertValueToMilliseconds(value - m_tvStartValue); 306 if (ret < s) 307 return false; 308 309 m_tvStartValue = value; 310 return true; 311 } 312 313 bool Timer::ResetIfNanosecondsPassed(double s) 314 { 315 const Value value = GetCurrentValue(); 316 const double ret = ConvertValueToNanoseconds(value - m_tvStartValue); 317 if (ret < s) 318 return false; 319 320 m_tvStartValue = value; 321 return true; 322 } 323 324 void Timer::BusyWait(std::uint64_t ns) 325 { 326 const Value start = GetCurrentValue(); 327 const Value end = start + ConvertNanosecondsToValue(static_cast<double>(ns)); 328 if (end < start) 329 { 330 // overflow, unlikely 331 while (GetCurrentValue() > end) 332 ; 333 } 334 335 while (GetCurrentValue() < end) 336 ; 337 } 338 339 void Timer::HybridSleep(std::uint64_t ns, std::uint64_t min_sleep_time) 340 { 341 const std::uint64_t start = GetCurrentValue(); 342 const std::uint64_t end = start + ConvertNanosecondsToValue(static_cast<double>(ns)); 343 if (end < start) 344 { 345 // overflow, unlikely 346 while (GetCurrentValue() > end) 347 ; 348 } 349 350 std::uint64_t current = GetCurrentValue(); 351 while (current < end) 352 { 353 const std::uint64_t remaining = end - current; 354 if (remaining >= min_sleep_time) 355 NanoSleep(min_sleep_time); 356 357 current = GetCurrentValue(); 358 } 359 } 360 361 void Timer::NanoSleep(std::uint64_t ns) 362 { 363 #if defined(_WIN32) 364 HANDLE timer = GetSleepTimer(); 365 if (timer) 366 { 367 LARGE_INTEGER due_time; 368 due_time.QuadPart = -static_cast<std::int64_t>(static_cast<std::uint64_t>(ns) / 100u); 369 if (SetWaitableTimer(timer, &due_time, 0, nullptr, nullptr, FALSE)) 370 WaitForSingleObject(timer, INFINITE); 371 else 372 std::fprintf(stderr, "SetWaitableTimer() failed: %08X\n", static_cast<unsigned>(GetLastError())); 373 } 374 else 375 { 376 Sleep(static_cast<std::uint32_t>(ns / 1000000)); 377 } 378 #elif defined(__ANDROID__) 379 // Round down to the next millisecond. 380 usleep(static_cast<useconds_t>((ns / 1000000) * 1000)); 381 #else 382 const struct timespec ts = {0, static_cast<long>(ns)}; 383 nanosleep(&ts, nullptr); 384 #endif 385 } 386 387 } // namespace Common