Skip to content

Commit c5d631c

Browse files
committed
fix overflow in Stopwatch
1 parent a9e357a commit c5d631c

File tree

3 files changed

+27
-94
lines changed

3 files changed

+27
-94
lines changed

src/Common/Stopwatch.cpp

Lines changed: 0 additions & 19 deletions
This file was deleted.

src/Common/Stopwatch.h

Lines changed: 26 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22

33
#include <base/time.h>
44
#include <base/types.h>
5+
#include <base/defines.h>
56

7+
#include <cassert>
68
#include <atomic>
79
#include <memory>
810

@@ -14,6 +16,20 @@ inline UInt64 clock_gettime_ns(clockid_t clock_type = CLOCK_MONOTONIC)
1416
return UInt64(ts.tv_sec * 1000000000LL + ts.tv_nsec);
1517
}
1618

19+
/// Sometimes monotonic clock may not be monotonic (due to bug in kernel?).
20+
/// It may cause some operations to fail with "Timeout exceeded: elapsed 18446744073.709553 seconds".
21+
/// Takes previously returned value and returns it again if time stepped back for some reason.
22+
inline UInt64 clock_gettime_ns_adjusted(UInt64 prev_time, clockid_t clock_type = CLOCK_MONOTONIC)
23+
{
24+
UInt64 current_time = clock_gettime_ns(clock_type);
25+
if (likely(prev_time <= current_time))
26+
return current_time;
27+
28+
/// Something probably went completely wrong if time stepped back for more than 1 second.
29+
assert(prev_time - current_time <= 1000000000ULL);
30+
return prev_time;
31+
}
32+
1733
/** Differs from Poco::Stopwatch only by using 'clock_gettime' instead of 'gettimeofday',
1834
* returns nanoseconds instead of microseconds, and also by other minor differencies.
1935
*/
@@ -41,7 +57,7 @@ class Stopwatch
4157
clockid_t clock_type;
4258
bool is_running = false;
4359

44-
UInt64 nanoseconds() const { return clock_gettime_ns(clock_type); }
60+
UInt64 nanoseconds() const { return clock_gettime_ns_adjusted(start_ns, clock_type); }
4561
};
4662

4763
using StopwatchUniquePtr = std::unique_ptr<Stopwatch>;
@@ -52,8 +68,12 @@ class AtomicStopwatch
5268
public:
5369
explicit AtomicStopwatch(clockid_t clock_type_ = CLOCK_MONOTONIC) : clock_type(clock_type_) { restart(); }
5470

55-
void restart() { start_ns = nanoseconds(); }
56-
UInt64 elapsed() const { return nanoseconds() - start_ns; }
71+
void restart() { start_ns = nanoseconds(0); }
72+
UInt64 elapsed() const
73+
{
74+
UInt64 current_start_ns = start_ns;
75+
return nanoseconds(current_start_ns) - current_start_ns;
76+
}
5777
UInt64 elapsedMilliseconds() const { return elapsed() / 1000000UL; }
5878
double elapsedSeconds() const { return static_cast<double>(elapsed()) / 1000000000ULL; }
5979

@@ -64,8 +84,8 @@ class AtomicStopwatch
6484
bool compareAndRestart(double seconds)
6585
{
6686
UInt64 threshold = static_cast<UInt64>(seconds * 1000000000.0);
67-
UInt64 current_ns = nanoseconds();
6887
UInt64 current_start_ns = start_ns;
88+
UInt64 current_ns = nanoseconds(current_start_ns);
6989

7090
while (true)
7191
{
@@ -108,8 +128,8 @@ class AtomicStopwatch
108128
Lock compareAndRestartDeferred(double seconds)
109129
{
110130
UInt64 threshold = UInt64(seconds * 1000000000.0);
111-
UInt64 current_ns = nanoseconds();
112131
UInt64 current_start_ns = start_ns;
132+
UInt64 current_ns = nanoseconds(current_start_ns);
113133

114134
while (true)
115135
{
@@ -130,74 +150,6 @@ class AtomicStopwatch
130150
clockid_t clock_type;
131151

132152
/// Most significant bit is a lock. When it is set, compareAndRestartDeferred method will return false.
133-
UInt64 nanoseconds() const { return clock_gettime_ns(clock_type) & 0x7FFFFFFFFFFFFFFFULL; }
153+
UInt64 nanoseconds(UInt64 prev_time) const { return clock_gettime_ns_adjusted(prev_time, clock_type) & 0x7FFFFFFFFFFFFFFFULL; }
134154
};
135155

136-
137-
/// Like ordinary StopWatch, but uses getrusage() system call
138-
struct StopwatchRUsage
139-
{
140-
StopwatchRUsage() = default;
141-
142-
void start() { start_ts = Timestamp::current(); is_running = true; }
143-
void stop() { stop_ts = Timestamp::current(); is_running = false; }
144-
void reset() { start_ts = Timestamp(); stop_ts = Timestamp(); is_running = false; }
145-
void restart() { start(); }
146-
147-
UInt64 elapsed(bool count_user = true, bool count_sys = true) const
148-
{
149-
return elapsedNanoseconds(count_user, count_sys);
150-
}
151-
152-
UInt64 elapsedNanoseconds(bool count_user = true, bool count_sys = true) const
153-
{
154-
return (is_running ? Timestamp::current() : stop_ts).nanoseconds(count_user, count_sys) - start_ts.nanoseconds(count_user, count_sys);
155-
}
156-
157-
UInt64 elapsedMicroseconds(bool count_user = true, bool count_sys = true) const
158-
{
159-
return elapsedNanoseconds(count_user, count_sys) / 1000UL;
160-
}
161-
162-
UInt64 elapsedMilliseconds(bool count_user = true, bool count_sys = true) const
163-
{
164-
return elapsedNanoseconds(count_user, count_sys) / 1000000UL;
165-
}
166-
167-
double elapsedSeconds(bool count_user = true, bool count_sys = true) const
168-
{
169-
return static_cast<double>(elapsedNanoseconds(count_user, count_sys)) / 1000000000.0;
170-
}
171-
172-
private:
173-
174-
struct Timestamp
175-
{
176-
UInt64 user_ns = 0;
177-
UInt64 sys_ns = 0;
178-
179-
static Timestamp current();
180-
181-
UInt64 nanoseconds(bool count_user = true, bool count_sys = true) const
182-
{
183-
return (count_user ? user_ns : 0) + (count_sys ? sys_ns : 0);
184-
}
185-
};
186-
187-
Timestamp start_ts;
188-
Timestamp stop_ts;
189-
bool is_running = false;
190-
};
191-
192-
193-
template <typename TStopwatch>
194-
class StopwatchGuard : public TStopwatch
195-
{
196-
public:
197-
explicit StopwatchGuard(UInt64 & elapsed_ns_) : elapsed_ns(elapsed_ns_) {}
198-
199-
~StopwatchGuard() { elapsed_ns += TStopwatch::elapsedNanoseconds(); }
200-
201-
private:
202-
UInt64 & elapsed_ns;
203-
};

src/Common/Throttler.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ void Throttler::add(size_t amount)
3535
{
3636
std::lock_guard lock(mutex);
3737

38-
auto now = clock_gettime_ns();
38+
auto now = clock_gettime_ns_adjusted(prev_ns);
3939
/// If prev_ns is equal to zero (first `add` call) we known nothing about speed
4040
/// and don't track anything.
4141
if (max_speed && prev_ns != 0)

0 commit comments

Comments
 (0)