Format digits10
[folly.git] / folly / stop_watch.h
1 /*
2  * Copyright 2017 Facebook, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #pragma once
18
19 #include <folly/portability/Time.h>
20 #include <chrono>
21 #include <stdexcept>
22 #include <utility>
23
24 namespace folly {
25
26 #ifdef CLOCK_MONOTONIC_COARSE
27 struct monotonic_coarse_clock {
28   typedef std::chrono::milliseconds::rep rep;
29   typedef std::chrono::milliseconds::period period;
30   typedef std::chrono::milliseconds duration;
31   typedef std::chrono::time_point<monotonic_coarse_clock> time_point;
32   constexpr static bool is_steady = true;
33
34   static time_point now() {
35     timespec ts;
36     auto ret = clock_gettime(CLOCK_MONOTONIC_COARSE, &ts);
37     if (ret != 0) {
38       throw std::runtime_error("Error using CLOCK_MONOTONIC_COARSE.");
39     }
40     return time_point(
41         duration((ts.tv_sec * 1000) + ((ts.tv_nsec / 1000) / 1000)));
42   }
43 };
44 #else
45 using monotonic_coarse_clock = std::chrono::steady_clock;
46 #endif
47
48 using monotonic_clock = std::chrono::steady_clock;
49
50 /**
51  * Calculates the duration of time intervals. Prefer this over directly using
52  * monotonic clocks. It is very lightweight and provides convenient facilitles
53  * to avoid common pitfalls.
54  *
55  * There are two type aliases that should be preferred over instantiating this
56  * class directly: `coarse_stop_watch` and `stop_watch`.
57  *
58  * Arguments:
59  *  - Clock: the monotonic clock to use when calculating time intervals
60  *  - Duration: (optional) the duration to use when reporting elapsed time.
61  *              Defaults to the clock's duration.
62  *
63  * Example 1:
64  *
65  *  coarse_stop_watch<std::chrono::seconds> watch;
66  *  do_something();
67  *  std::cout << "time elapsed: " << watch.elapsed().count() << std::endl;
68  *
69  *  auto const ttl = 60_s;
70  *  if (watch.elapsed(ttl)) {
71  *    process_expiration();
72  *  }
73  *
74  * Example 2:
75  *
76  *  struct run_every_n_seconds {
77  *    using callback = std::function<void()>;
78  *    run_every_n_seconds(std::chrono::seconds period, callback action)
79  *      period_(period),
80  *      action_(std::move(action))
81  *    {
82  *      // watch_ is correctly initialized to the current time
83  *    }
84  *
85  *    void run() {
86  *      while (true) {
87  *        if (watch_.lap(period_)) {
88  *          action_();
89  *        }
90  *        std::this_thread::yield();
91  *      }
92  *    }
93  *
94  *  private:
95  *    stop_watch<> watch_;
96  *    std::chrono::seconds period_;
97  *    callback action_;
98  *  };
99  *
100  * @author: Marcelo Juchem <marcelo@fb.com>
101  */
102 template <typename Clock, typename Duration = typename Clock::duration>
103 struct custom_stop_watch {
104   using clock_type = Clock;
105   using duration = Duration;
106   using time_point = std::chrono::time_point<clock_type, duration>;
107
108   static_assert(
109       std::ratio_less_equal<
110           typename clock_type::duration::period,
111           typename duration::period>::value,
112       "clock must be at least as precise as the requested duration");
113
114   static_assert(
115       Clock::is_steady,
116       "only monotonic clocks should be used to track time intervals");
117
118   /**
119    * Initializes the stop watch with the current time as its checkpoint.
120    *
121    * Example:
122    *
123    *  stop_watch<> watch;
124    *  do_something();
125    *  std::cout << "time elapsed: " << watch.elapsed() << std::endl;
126    *
127    * @author: Marcelo Juchem <marcelo@fb.com>
128    */
129   custom_stop_watch() : checkpoint_(clock_type::now()) {}
130
131   /**
132    * Initializes the stop watch with the given time as its checkpoint.
133    *
134    * NOTE: this constructor should be seldomly used. It is only provided so
135    * that, in the rare occasions it is needed, one does not have to reimplement
136    * the `custom_stop_watch` class.
137    *
138    * Example:
139    *
140    *  custom_stop_watch<monotonic_clock> watch(monotonic_clock::now());
141    *  do_something();
142    *  std::cout << "time elapsed: " << watch.elapsed() << std::endl;
143    *
144    * @author: Marcelo Juchem <marcelo@fb.com>
145    */
146   explicit custom_stop_watch(typename clock_type::time_point checkpoint)
147       : checkpoint_(std::move(checkpoint)) {}
148
149   /**
150    * Updates the stop watch checkpoint to the current time.
151    *
152    * Example:
153    *
154    *  struct some_resource {
155    *    // ...
156    *
157    *    void on_reloaded() {
158    *      time_alive.reset();
159    *    }
160    *
161    *    void report() {
162    *      std::cout << "resource has been alive for " << time_alive.elapsed();
163    *    }
164    *
165    *  private:
166    *    stop_watch<> time_alive;
167    *  };
168    *
169    * @author: Marcelo Juchem <marcelo@fb.com>
170    */
171   void reset() {
172     checkpoint_ = clock_type::now();
173   }
174
175   /**
176    * Tells the elapsed time since the last update.
177    *
178    * The stop watch's checkpoint remains unchanged.
179    *
180    * Example:
181    *
182    *  stop_watch<> watch;
183    *  do_something();
184    *  std::cout << "time elapsed: " << watch.elapsed() << std::endl;
185    *
186    * @author: Marcelo Juchem <marcelo@fb.com>
187    */
188   duration elapsed() const {
189     return std::chrono::duration_cast<duration>(
190         clock_type::now() - checkpoint_);
191   }
192
193   /**
194    * Tells whether the given duration has already elapsed since the last
195    * checkpoint.
196    *
197    * Example:
198    *
199    *  auto const ttl = 60_s;
200    *  stop_watch<> watch;
201    *
202    *  do_something();
203    *
204    *  std::cout << "has the TTL expired? " std::boolalpha<< watch.elapsed(ttl);
205    *
206    * @author: Marcelo Juchem <marcelo@fb.com>
207    */
208   template <typename UDuration>
209   bool elapsed(UDuration&& amount) const {
210     return clock_type::now() - checkpoint_ >= amount;
211   }
212
213   /**
214    * Tells the elapsed time since the last update, and updates the checkpoint
215    * to the current time.
216    *
217    * Example:
218    *
219    *  struct some_resource {
220    *    // ...
221    *
222    *    void on_reloaded() {
223    *      auto const alive = time_alive.lap();
224    *      std::cout << "resource reloaded after being alive for " << alive;
225    *    }
226    *
227    *  private:
228    *    stop_watch<> time_alive;
229    *  };
230    *
231    * @author: Marcelo Juchem <marcelo@fb.com>
232    */
233   duration lap() {
234     auto lastCheckpoint = checkpoint_;
235
236     checkpoint_ = clock_type::now();
237
238     return std::chrono::duration_cast<duration>(checkpoint_ - lastCheckpoint);
239   }
240
241   /**
242    * Tells whether the given duration has already elapsed since the last
243    * checkpoint. If so, update the checkpoint to the current time. If not,
244    * the checkpoint remains unchanged.
245    *
246    * Example:
247    *
248    *  void run_every_n_seconds(
249    *    std::chrono::seconds period,
250    *    std::function<void()> action
251    *  ) {
252    *    for (stop_watch<> watch;; ) {
253    *      if (watch.lap(period)) {
254    *        action();
255    *      }
256    *      std::this_thread::yield();
257    *    }
258    *  }
259    *
260    * @author: Marcelo Juchem <marcelo@fb.com>
261    */
262   template <typename UDuration>
263   bool lap(UDuration&& amount) {
264     auto now = clock_type::now();
265
266     if (now - checkpoint_ < amount) {
267       return false;
268     }
269
270     checkpoint_ = now;
271     return true;
272   }
273
274   /**
275    * Returns the current checkpoint
276    */
277   typename clock_type::time_point getCheckpoint() const {
278     return checkpoint_;
279   }
280
281  private:
282   typename clock_type::time_point checkpoint_;
283 };
284
285 /**
286  * A type alias for `custom_stop_watch` that uses a coarse monotonic clock as
287  * the time source.  Refer to the documentation of `custom_stop_watch` for full
288  * documentation.
289  *
290  * Arguments:
291  *  - Duration: (optional) the duration to use when reporting elapsed time.
292  *              Defaults to the clock's duration.
293  *
294  * Example:
295  *
296  *  coarse_stop_watch<std::chrono::seconds> watch;
297  *  do_something();
298  *  std::cout << "time elapsed: " << watch.elapsed().count() << std::endl;
299  *
300  * @author: Marcelo Juchem <marcelo@fb.com>
301  */
302 template <typename Duration = monotonic_coarse_clock::duration>
303 using coarse_stop_watch = custom_stop_watch<monotonic_coarse_clock, Duration>;
304
305 /**
306  * A type alias for `custom_stop_watch` that uses a monotonic clock as the time
307  * source.  Refer to the documentation of `custom_stop_watch` for full
308  * documentation.
309  *
310  * Arguments:
311  *  - Duration: (optional) the duration to use when reporting elapsed time.
312  *              Defaults to the clock's duration.
313  *
314  * Example:
315  *
316  *  stop_watch<std::chrono::seconds> watch;
317  *  do_something();
318  *  std::cout << "time elapsed: " << watch.elapsed().count() << std::endl;
319  *
320  * @author: Marcelo Juchem <marcelo@fb.com>
321  */
322 template <typename Duration = monotonic_clock::duration>
323 using stop_watch = custom_stop_watch<monotonic_clock, Duration>;
324 } // namespace folly