VLink  2.0.0
A high-performance communication middleware
elapsed_timer.h
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2026 by Thun Lu. All rights reserved.
3  * Author: Thun Lu <thun.lu@zohomail.cn>
4  * Repo: https://github.com/thun-res/vlink
5  * _ __ __ _ __
6  * | | / / / / (_) ____ / /__
7  * | | / / / / / / / __ \ / //_/
8  * | |/ / / /___ / / / / / / / ,<
9  * |___/ /_____/ /_/ /_/ /_/ /_/|_|
10  *
11  * Licensed under the Apache License, Version 2.0 (the "License");
12  * you may not use this file except in compliance with the License.
13  * You may obtain a copy of the License at
14  *
15  * http://www.apache.org/licenses/LICENSE-2.0
16  *
17  * Unless required by applicable law or agreed to in writing, software
18  * distributed under the License is distributed on an "AS IS" BASIS,
19  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20  * See the License for the specific language governing permissions and
21  * limitations under the License.
22  */
23 
24 /**
25  * @file elapsed_timer.h
26  * @brief Lowest-level elapsed-time primitive used by deadline tracking, profilers and task metrics.
27  *
28  * @details
29  * @c ElapsedTimer measures how much time has passed since @c start was called, using either a
30  * monotonic wall clock or process CPU time, with millisecond / microsecond / nanosecond
31  * resolution. It backs @c DeadlineTimer, @c CpuProfiler and the message-loop task latency
32  * counters.
33  *
34  * @par API table
35  *
36  * | Method | Purpose | Mutates state |
37  * | ----------------------- | --------------------------------------------- | ------------- |
38  * | @c start | Latch the current time as the start reference | yes |
39  * | @c stop | Mark the timer inactive (start = @c -1) | yes |
40  * | @c restart | Read elapsed, latch new start atomically | yes |
41  * | @c get | Read elapsed without changing state | no |
42  * | @c is_active | Report whether @c start has set the reference | no |
43  * | @c get_sys_timestamp | Sample wall-clock time (CLOCK_REALTIME) | no |
44  * | @c get_cpu_timestamp | Sample monotonic time (CLOCK_MONOTONIC_RAW) | no |
45  * | @c get_cpu_active_time | Sample cumulative process CPU time | no |
46  *
47  * @par Clock source (Method)
48  *
49  * | Value | Clock source | Notes |
50  * | ------------------ | ------------------------------------- | --------------------------------- |
51  * | kCpuTimestamp | @c std::chrono::steady_clock | Monotonic wall time, never jumps |
52  * | kCpuActiveTime | @c getrusage / @c GetProcessTimes | Total process user + kernel time |
53  *
54  * @par Precision (Accuracy)
55  *
56  * | Value | Unit | 64-bit dynamic range |
57  * | ------- | ------------ | ----------------------- |
58  * | kMilli | milliseconds | ~292 million years |
59  * | kMicro | microseconds | ~292 thousand years |
60  * | kNano | nanoseconds | ~292 years |
61  *
62  * @par Example
63  * @code
64  * vlink::ElapsedTimer t(vlink::ElapsedTimer::kCpuTimestamp, vlink::ElapsedTimer::kMicro);
65  * t.start();
66  * do_work();
67  * const int64_t us = t.get(); // microseconds since start; -1 if not started
68  * const int64_t since = t.restart(); // read + atomic reset
69  * t.stop();
70  * @endcode
71  *
72  * @note The timer is @b not started by the constructor; call @c start explicitly. The internal
73  * start time is held in a 64-byte aligned @c std::atomic<int64_t>; concurrent reads are
74  * safe, but interleaved @c start / @c stop from different threads race on the active flag.
75  */
76 
77 #pragma once
78 
79 #include <atomic>
80 #include <cstdint>
81 
82 #include "./macros.h"
83 
84 namespace vlink {
85 
86 /**
87  * @class ElapsedTimer
88  * @brief Atomic high-resolution timer with selectable clock source and unit.
89  *
90  * @details
91  * Records the elapsed time between @c start and @c get / @c restart. Marked @c final. Copy
92  * and move constructors copy the snapshot value (not exclusive ownership), allowing trivial
93  * propagation through wrapper structures.
94  */
96  public:
97  /**
98  * @brief Clock source selector.
99  */
100  enum Method : uint8_t {
101  kCpuTimestamp = 0, ///< Monotonic wall clock (@c steady_clock for instance timing).
102  kCpuActiveTime = 1 ///< Process CPU time (user + kernel via @c getrusage / @c GetProcessTimes).
103  };
104 
105  /**
106  * @brief Precision selector for stored and returned time values.
107  */
108  enum Accuracy : uint8_t {
109  kMilli = 0, ///< Millisecond precision.
110  kMicro = 1, ///< Microsecond precision.
111  kNano = 2 ///< Nanosecond precision.
112  };
113 
114  /**
115  * @brief Constructs a timer with default method (@c kCpuTimestamp) and accuracy (@c kMilli).
116  *
117  * @details
118  * The timer is inactive on construction; call @c start to begin measuring.
119  */
120  ElapsedTimer() noexcept;
121 
122  /**
123  * @brief Constructs a timer with the given clock source and default millisecond accuracy.
124  *
125  * @param method Clock source.
126  */
127  explicit ElapsedTimer(Method method) noexcept;
128 
129  /**
130  * @brief Constructs a timer with the default clock source and the given accuracy.
131  *
132  * @param accuracy Precision of returned values.
133  */
134  explicit ElapsedTimer(Accuracy accuracy) noexcept;
135 
136  /**
137  * @brief Constructs a timer with the given clock source and accuracy.
138  *
139  * @param method Clock source.
140  * @param accuracy Precision of returned values.
141  */
142  explicit ElapsedTimer(Method method, Accuracy accuracy) noexcept;
143 
144  /**
145  * @brief Copy constructor; copies the snapshot value and configuration.
146  *
147  * @param target Source timer.
148  */
149  ElapsedTimer(const ElapsedTimer& target) noexcept;
150 
151  /**
152  * @brief Move constructor; equivalent to copy for this snapshot-valued type.
153  *
154  * @param target Source timer.
155  */
156  ElapsedTimer(ElapsedTimer&& target) noexcept;
157 
158  /**
159  * @brief Destructor.
160  */
161  ~ElapsedTimer() noexcept;
162 
163  /**
164  * @brief Copy assignment; copies the snapshot value and configuration.
165  *
166  * @param target Source timer.
167  * @return Reference to @c *this.
168  */
169  ElapsedTimer& operator=(const ElapsedTimer& target) noexcept;
170 
171  /**
172  * @brief Move assignment; equivalent to copy assignment for this snapshot-valued type.
173  *
174  * @return Reference to @c *this.
175  */
176  ElapsedTimer& operator=(ElapsedTimer&&) noexcept;
177 
178  /**
179  * @brief Returns the current wall-clock timestamp.
180  *
181  * @details
182  * On Linux uses @c clock_gettime with @c CLOCK_REALTIME when @p high_resolution is @c true,
183  * falling back to @c std::chrono::system_clock otherwise. On Windows always uses
184  * @c std::chrono::system_clock.
185  *
186  * @param accuracy Desired precision. Default: @c kMilli.
187  * @param high_resolution Linux-only switch for @c clock_gettime. Default: @c true.
188  * @return Wall-clock timestamp in the requested unit.
189  */
190  [[nodiscard]] static uint64_t get_sys_timestamp(Accuracy accuracy = kMilli, bool high_resolution = true) noexcept;
191 
192  /**
193  * @brief Returns the current monotonic CPU timestamp.
194  *
195  * @details
196  * On Linux uses @c CLOCK_MONOTONIC_RAW (immune to NTP) when @p high_resolution is @c true;
197  * otherwise uses @c std::chrono::steady_clock. Windows always uses @c steady_clock.
198  *
199  * @param accuracy Desired precision. Default: @c kMilli.
200  * @param high_resolution Linux-only switch for @c clock_gettime. Default: @c true.
201  * @return Monotonic timestamp in the requested unit.
202  */
203  [[nodiscard]] static uint64_t get_cpu_timestamp(Accuracy accuracy = kMilli, bool high_resolution = true) noexcept;
204 
205  /**
206  * @brief Returns the cumulative CPU time consumed by the current process.
207  *
208  * @details
209  * On POSIX uses @c getrusage(RUSAGE_SELF); on Windows uses @c GetProcessTimes. The value is
210  * cumulative; subtract two samples to obtain a delta.
211  *
212  * @param accuracy Desired precision. Default: @c kMilli.
213  * @return Process CPU time in the requested unit.
214  */
215  [[nodiscard]] static uint64_t get_cpu_active_time(Accuracy accuracy = kMilli) noexcept;
216 
217  /**
218  * @brief Returns the clock source this timer was configured with.
219  *
220  * @return Configured clock source.
221  */
222  [[nodiscard]] Method get_method() const noexcept;
223 
224  /**
225  * @brief Returns the precision this timer was configured with.
226  *
227  * @return Configured precision.
228  */
229  [[nodiscard]] Accuracy get_accuracy() const noexcept;
230 
231  /**
232  * @brief Records the current time as the start reference if the timer is inactive.
233  *
234  * @details
235  * Uses a compare-and-swap on the internal atomic so concurrent calls compete and only one wins.
236  * Already-active timers are unchanged; call @c stop first to rearm.
237  */
238  void start() noexcept;
239 
240  /**
241  * @brief Marks the timer inactive by writing @c -1 into the start reference.
242  *
243  * @details
244  * After @c stop the timer reports @c is_active() @c == @c false and @c get() returns @c -1.
245  */
246  void stop() noexcept;
247 
248  /**
249  * @brief Atomically reads the elapsed time and starts a new measurement from now.
250  *
251  * @details
252  * Performs a single atomic exchange. When the timer was already active the returned value is
253  * the elapsed time since the last start; when it was inactive the return is negative (the raw
254  * sentinel) and the timer is left active starting from this call.
255  *
256  * @return Elapsed time in the configured unit, or a negative value if the timer was inactive.
257  */
258  int64_t restart() noexcept;
259 
260  /**
261  * @brief Reports whether the timer is currently measuring.
262  *
263  * @return @c true when @c start has produced a non-sentinel reference.
264  */
265  [[nodiscard]] bool is_active() const noexcept;
266 
267  /**
268  * @brief Returns the elapsed time since @c start without altering state.
269  *
270  * @return Elapsed value in the configured unit, or @c -1 when inactive.
271  */
272  [[nodiscard]] int64_t get() const noexcept;
273 
274  private:
275  alignas(64) std::atomic<int64_t> start_time_{-1};
276  Method method_{kCpuTimestamp};
277  Accuracy accuracy_{kMilli};
278 };
279 
280 } // namespace vlink
Cross-platform macros for visibility, branch hints, copy prevention, singletons and string helpers.
#define VLINK_EXPORT
Definition: macros.h:81