1 /*
2  * Copyright (c) Facebook, Inc. and its affiliates.
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 <chrono>
20 #include <stdexcept>
21 #include <utility>
22 
23 #include <folly/Chrono.h>
24 #include <folly/portability/Time.h>
25 
26 namespace folly {
27 
28 using monotonic_clock = std::chrono::steady_clock;
29 
30 /**
31  * Calculates the duration of time intervals. Prefer this over directly using
32  * monotonic clocks. It is very lightweight and provides convenient facilities
33  * to avoid common pitfalls.
34  *
35  * There are two type aliases that should be preferred over instantiating this
36  * class directly: `coarse_stop_watch` and `stop_watch`.
37  *
38  * Arguments:
39  *  - Clock: the monotonic clock to use when calculating time intervals
40  *  - Duration: (optional) the duration to use when reporting elapsed time.
41  *              Defaults to the clock's duration.
42  *
43  * Example 1:
44  *
45  *  coarse_stop_watch<std::chrono::seconds> watch;
46  *  do_something();
47  *  std::cout << "time elapsed: " << watch.elapsed().count() << std::endl;
48  *
49  *  auto const ttl = 60_s;
50  *  if (watch.elapsed(ttl)) {
51  *    process_expiration();
52  *  }
53  *
54  * Example 2:
55  *
56  *  struct run_every_n_seconds {
57  *    using callback = std::function<void()>;
58  *    run_every_n_seconds(std::chrono::seconds period, callback action)
59  *      period_(period),
60  *      action_(std::move(action))
61  *    {
62  *      // watch_ is correctly initialized to the current time
63  *    }
64  *
65  *    void run() {
66  *      while (true) {
67  *        if (watch_.lap(period_)) {
68  *          action_();
69  *        }
70  *        std::this_thread::yield();
71  *      }
72  *    }
73  *
74  *  private:
75  *    stop_watch<> watch_;
76  *    std::chrono::seconds period_;
77  *    callback action_;
78  *  };
79  *
80  * @author: Marcelo Juchem <marcelo@fb.com>
81  */
82 template <typename Clock, typename Duration = typename Clock::duration>
83 struct custom_stop_watch {
84   using clock_type = Clock;
85   using duration = Duration;
86   using time_point = std::chrono::time_point<clock_type, duration>;
87 
88   static_assert(
89       std::ratio_less_equal<
90           typename clock_type::duration::period,
91           typename duration::period>::value,
92       "clock must be at least as precise as the requested duration");
93 
94   static_assert(
95       Clock::is_steady,
96       "only monotonic clocks should be used to track time intervals");
97 
98   /**
99    * Initializes the stop watch with the current time as its checkpoint.
100    *
101    * Example:
102    *
103    *  stop_watch<> watch;
104    *  do_something();
105    *  std::cout << "time elapsed: " << watch.elapsed() << std::endl;
106    *
107    * @author: Marcelo Juchem <marcelo@fb.com>
108    */
custom_stop_watchcustom_stop_watch109   custom_stop_watch() : checkpoint_(clock_type::now()) {}
110 
111   /**
112    * Initializes the stop watch with the given time as its checkpoint.
113    *
114    * NOTE: this constructor should be seldomly used. It is only provided so
115    * that, in the rare occasions it is needed, one does not have to reimplement
116    * the `custom_stop_watch` class.
117    *
118    * Example:
119    *
120    *  custom_stop_watch<monotonic_clock> watch(monotonic_clock::now());
121    *  do_something();
122    *  std::cout << "time elapsed: " << watch.elapsed() << std::endl;
123    *
124    * @author: Marcelo Juchem <marcelo@fb.com>
125    */
custom_stop_watchcustom_stop_watch126   explicit custom_stop_watch(typename clock_type::time_point checkpoint)
127       : checkpoint_(std::move(checkpoint)) {}
128 
129   /**
130    * Updates the stop watch checkpoint to the current time.
131    *
132    * Example:
133    *
134    *  struct some_resource {
135    *    // ...
136    *
137    *    void on_reloaded() {
138    *      time_alive.reset();
139    *    }
140    *
141    *    void report() {
142    *      std::cout << "resource has been alive for " << time_alive.elapsed();
143    *    }
144    *
145    *  private:
146    *    stop_watch<> time_alive;
147    *  };
148    *
149    * @author: Marcelo Juchem <marcelo@fb.com>
150    */
resetcustom_stop_watch151   void reset() { checkpoint_ = clock_type::now(); }
152 
153   /**
154    * Tells the elapsed time since the last update.
155    *
156    * The stop watch's checkpoint remains unchanged.
157    *
158    * Example:
159    *
160    *  stop_watch<> watch;
161    *  do_something();
162    *  std::cout << "time elapsed: " << watch.elapsed() << std::endl;
163    *
164    * @author: Marcelo Juchem <marcelo@fb.com>
165    */
elapsedcustom_stop_watch166   duration elapsed() const {
167     return std::chrono::duration_cast<duration>(
168         clock_type::now() - checkpoint_);
169   }
170 
171   /**
172    * Tells whether the given duration has already elapsed since the last
173    * checkpoint.
174    *
175    * Example:
176    *
177    *  auto const ttl = 60_s;
178    *  stop_watch<> watch;
179    *
180    *  do_something();
181    *
182    *  std::cout << "ttl expired? " << std::boolalpha << watch.elapsed(ttl);
183    *
184    * @author: Marcelo Juchem <marcelo@fb.com>
185    */
186   template <typename UDuration>
elapsedcustom_stop_watch187   bool elapsed(UDuration&& amount) const {
188     return clock_type::now() - checkpoint_ >= amount;
189   }
190 
191   /**
192    * Tells the elapsed time since the last update, and updates the checkpoint
193    * to the current time.
194    *
195    * Example:
196    *
197    *  struct some_resource {
198    *    // ...
199    *
200    *    void on_reloaded() {
201    *      auto const alive = time_alive.lap();
202    *      std::cout << "resource reloaded after being alive for " << alive;
203    *    }
204    *
205    *  private:
206    *    stop_watch<> time_alive;
207    *  };
208    *
209    * @author: Marcelo Juchem <marcelo@fb.com>
210    */
lapcustom_stop_watch211   duration lap() {
212     auto lastCheckpoint = checkpoint_;
213 
214     checkpoint_ = clock_type::now();
215 
216     return std::chrono::duration_cast<duration>(checkpoint_ - lastCheckpoint);
217   }
218 
219   /**
220    * Tells whether the given duration has already elapsed since the last
221    * checkpoint. If so, update the checkpoint to the current time. If not,
222    * the checkpoint remains unchanged.
223    *
224    * Example:
225    *
226    *  void run_every_n_seconds(
227    *    std::chrono::seconds period,
228    *    std::function<void()> action
229    *  ) {
230    *    for (stop_watch<> watch;; ) {
231    *      if (watch.lap(period)) {
232    *        action();
233    *      }
234    *      std::this_thread::yield();
235    *    }
236    *  }
237    *
238    * @author: Marcelo Juchem <marcelo@fb.com>
239    */
240   template <typename UDuration>
lapcustom_stop_watch241   bool lap(UDuration&& amount) {
242     auto now = clock_type::now();
243 
244     if (now - checkpoint_ < amount) {
245       return false;
246     }
247 
248     checkpoint_ = now;
249     return true;
250   }
251 
252   /**
253    * Returns the current checkpoint
254    */
getCheckpointcustom_stop_watch255   typename clock_type::time_point getCheckpoint() const { return checkpoint_; }
256 
257  private:
258   typename clock_type::time_point checkpoint_;
259 };
260 
261 /**
262  * A type alias for `custom_stop_watch` that uses a coarse monotonic clock as
263  * the time source.  Refer to the documentation of `custom_stop_watch` for full
264  * documentation.
265  *
266  * Arguments:
267  *  - Duration: (optional) the duration to use when reporting elapsed time.
268  *              Defaults to the clock's duration.
269  *
270  * Example:
271  *
272  *  coarse_stop_watch<std::chrono::seconds> watch;
273  *  do_something();
274  *  std::cout << "time elapsed: " << watch.elapsed().count() << std::endl;
275  *
276  * @author: Marcelo Juchem <marcelo@fb.com>
277  */
278 template <typename Duration = folly::chrono::coarse_steady_clock::duration>
279 using coarse_stop_watch =
280     custom_stop_watch<folly::chrono::coarse_steady_clock, Duration>;
281 
282 /**
283  * A type alias for `custom_stop_watch` that uses a monotonic clock as the time
284  * source.  Refer to the documentation of `custom_stop_watch` for full
285  * documentation.
286  *
287  * Arguments:
288  *  - Duration: (optional) the duration to use when reporting elapsed time.
289  *              Defaults to the clock's duration.
290  *
291  * Example:
292  *
293  *  stop_watch<std::chrono::seconds> watch;
294  *  do_something();
295  *  std::cout << "time elapsed: " << watch.elapsed().count() << std::endl;
296  *
297  * @author: Marcelo Juchem <marcelo@fb.com>
298  */
299 template <typename Duration = std::chrono::steady_clock::duration>
300 using stop_watch = custom_stop_watch<std::chrono::steady_clock, Duration>;
301 } // namespace folly
302