-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Description
Describe the bug
Lines 616 to 618 in a83d8c0
| const long long _Whole = (_Ctr / _Freq) * period::den; | |
| const long long _Part = (_Ctr % _Freq) * period::den / _Freq; | |
| return time_point(duration(_Whole + _Part)); |
Line 617 causes overflow if _Ctr * period::den overflows 64 bit signed maximum, and _Freq is at least one greater than _Ctr.
Resulting _Freq that causes overflow is only 922 times greater than _Freq observed on my system. Future Windows version or future hardware may cause overflow.
Command-line test case
C:\Temp>type repro.cpp
#include <iostream>
#include <chrono>
#include <atomic>
#include <limits>
int main() {
constexpr long long max_itermediate_value = (std::numeric_limits<long long>::max)();
constexpr long long _Freq_max = max_itermediate_value / std::chrono::steady_clock::period::den + 1;
constexpr long long _Freq_ovr = _Freq_max + 1;
constexpr long long _Ctr_ovr = _Freq_ovr - 1;
constexpr long long _Ctr_pre_ovr = _Ctr_ovr - 1;
std::cout << _Freq_max << " is maximum _Query_perf_frequency() value not causing overflow\n\n";
std::cout << "Otherwise with _Query_perf_frequency()=" << _Freq_ovr << " and _Query_perf_counter()=" << _Ctr_ovr <<"\n\n";
std::cout << "The result of 'const long long _Part = (_Ctr % _Freq) * period::den / _Freq;'\n"
" with _Ctr=" << _Ctr_pre_ovr << " and _Freq=" << _Freq_ovr << " is:\n";
std::cout << (_Ctr_pre_ovr % _Freq_ovr) * std::chrono::steady_clock::period::den / _Freq_ovr << "\n";
std::cout << "The result of 'const long long _Part = (_Ctr % _Freq) * period::den / _Freq;' \n"
" with _Ctr=" << _Ctr_ovr << " and _Freq=" << _Freq_ovr << " is:\n";
std::cout << (_Ctr_ovr % _Freq_ovr) * std::chrono::steady_clock::period::den / _Freq_ovr << "\n\n";
std::cout << _Query_perf_frequency() << " is the current frequency\n"
<< "It is only " << _Freq_ovr / _Query_perf_frequency() << " times away\n";
}
C:\Temp>cl /EHsc /W4 .\repro.cpp
Microsoft (R) C/C++ Optimizing Compiler Version 19.26.28720.3 for x86
Copyright (C) Microsoft Corporation. All rights reserved.
repro.cpp
.\repro.cpp(26): warning C4307: '*': signed integral constant overflow
Microsoft (R) Incremental Linker Version 14.26.28720.3
Copyright (C) Microsoft Corporation. All rights reserved.
/out:repro.exe
repro.obj
C:\Temp>repro
9223372037 is maximum _Query_perf_frequency() value not causing overflow
Otherwise with _Query_perf_frequency()=9223372038 and _Query_perf_counter()=9223372037
The result of 'const long long _Part = (_Ctr % _Freq) * period::den / _Freq;'
with _Ctr=9223372036 and _Freq=9223372038 is:
999999999
The result of 'const long long _Part = (_Ctr % _Freq) * period::den / _Freq;'
with _Ctr=9223372037 and _Freq=9223372038 is:
-999999999
10000000 is the current frequency
It is only 922 times away
Expected behavior
I expect some algorithm that is robust for any values of _Freq and _Ctr. Maybe _div128 / _mul128. Maybe floating point.
STL version
- Option 1: Visual Studio version
Microsoft Visual Studio Professional 2019 Preview
Version 16.6.0 Preview 2.0
Additional context
I've tried to optimize the algorithm in PR #653 .
I assumed that the original code did not suffer from overflow, so changed would not suffer either.
But the overflow exists, and the proposed optimization changes overflow condition to a more complex but more likely condition.
So I closed that PR and reporting this issue.