summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorlawnjelly <lawnjelly@gmail.com>2020-05-22 12:20:19 +0100
committerlawnjelly <lawnjelly@gmail.com>2020-05-22 12:46:35 +0100
commitdb9fa8816056b367a5d97f57c2498241aaef974a (patch)
treebe0f9b76563b00fc7c12e49e97939769a85a4dc8
parent5f1107aa30295e686be6f41cb6d17fc2cff1e036 (diff)
Fix overflow condition with QueryPerformanceCounter
The previous code for OS_Windows::get_ticks_usec() multiplied the tick count by 1000000 before dividing by ticks_per_second. The ticks is counted in a 64 bit integer and is susceptible to overflow when a machine has been running for a long period of time (days) with a high frequency timer. This PR separates the overall calculation into one for seconds and one for the remainder, removing the possibility of overflow due to the multiplier.
-rw-r--r--platform/uwp/os_uwp.cpp21
-rw-r--r--platform/windows/os_windows.cpp21
2 files changed, 38 insertions, 4 deletions
diff --git a/platform/uwp/os_uwp.cpp b/platform/uwp/os_uwp.cpp
index 4ddb5463d0..5ef1a5ca25 100644
--- a/platform/uwp/os_uwp.cpp
+++ b/platform/uwp/os_uwp.cpp
@@ -575,11 +575,28 @@ void OS_UWP::delay_usec(uint32_t p_usec) const {
uint64_t OS_UWP::get_ticks_usec() const {
uint64_t ticks;
- uint64_t time;
+
// This is the number of clock ticks since start
QueryPerformanceCounter((LARGE_INTEGER *)&ticks);
+
// Divide by frequency to get the time in seconds
- time = ticks * 1000000L / ticks_per_second;
+ // original calculation shown below is subject to overflow
+ // with high ticks_per_second and a number of days since the last reboot.
+ // time = ticks * 1000000L / ticks_per_second;
+
+ // we can prevent this by either using 128 bit math
+ // or separating into a calculation for seconds, and the fraction
+ uint64_t seconds = ticks / ticks_per_second;
+
+ // compiler will optimize these two into one divide
+ uint64_t leftover = ticks % ticks_per_second;
+
+ // remainder
+ uint64_t time = (leftover * 1000000L) / ticks_per_second;
+
+ // seconds
+ time += seconds * 1000000L;
+
// Subtract the time at game start to get
// the time since the game started
time -= ticks_start;
diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp
index f78c87be93..c32e50e472 100644
--- a/platform/windows/os_windows.cpp
+++ b/platform/windows/os_windows.cpp
@@ -418,12 +418,29 @@ void OS_Windows::delay_usec(uint32_t p_usec) const {
uint64_t OS_Windows::get_ticks_usec() const {
uint64_t ticks;
- uint64_t time;
+
// This is the number of clock ticks since start
if (!QueryPerformanceCounter((LARGE_INTEGER *)&ticks))
ticks = (UINT64)timeGetTime();
+
// Divide by frequency to get the time in seconds
- time = ticks * 1000000L / ticks_per_second;
+ // original calculation shown below is subject to overflow
+ // with high ticks_per_second and a number of days since the last reboot.
+ // time = ticks * 1000000L / ticks_per_second;
+
+ // we can prevent this by either using 128 bit math
+ // or separating into a calculation for seconds, and the fraction
+ uint64_t seconds = ticks / ticks_per_second;
+
+ // compiler will optimize these two into one divide
+ uint64_t leftover = ticks % ticks_per_second;
+
+ // remainder
+ uint64_t time = (leftover * 1000000L) / ticks_per_second;
+
+ // seconds
+ time += seconds * 1000000L;
+
// Subtract the time at game start to get
// the time since the game started
time -= ticks_start;