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