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
4763using StopwatchUniquePtr = std::unique_ptr<Stopwatch>;
@@ -52,8 +68,12 @@ class AtomicStopwatch
5268public:
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- };
0 commit comments