--- /dev/null
+/*
+ * Copyright 2016 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <folly/portability/Time.h>
+#include <chrono>
+#include <stdexcept>
+#include <utility>
+
+namespace folly {
+
+#ifdef CLOCK_MONOTONIC_COARSE
+struct monotonic_coarse_clock {
+ typedef std::chrono::milliseconds::rep rep;
+ typedef std::chrono::milliseconds::period period;
+ typedef std::chrono::milliseconds duration;
+ typedef std::chrono::time_point<monotonic_coarse_clock> time_point;
+ constexpr static bool is_steady = true;
+
+ static time_point now() {
+ timespec ts;
+ auto ret = clock_gettime(CLOCK_MONOTONIC_COARSE, &ts);
+ if (ret != 0) {
+ throw std::runtime_error("Error using CLOCK_MONOTONIC_COARSE.");
+ }
+ return time_point(
+ duration((ts.tv_sec * 1000) + ((ts.tv_nsec / 1000) / 1000)));
+ }
+};
+#else
+using monotonic_coarse_clock = std::chrono::steady_clock;
+#endif
+
+using monotonic_clock = std::chrono::steady_clock;
+
+/**
+ * Calculates the duration of time intervals. Prefer this over directly using
+ * monotonic clocks. It is very lightweight and provides convenient facilitles
+ * to avoid common pitfalls.
+ *
+ * There are two type aliases that should be preferred over instantiating this
+ * class directly: `coarse_stop_watch` and `stop_watch`.
+ *
+ * Arguments:
+ * - Clock: the monotonic clock to use when calculating time intervals
+ * - Duration: (optional) the duration to use when reporting elapsed time.
+ * Defaults to the clock's duration.
+ *
+ * Example 1:
+ *
+ * coarse_stop_watch<std::seconds> watch;
+ * do_something();
+ * std::cout << "time elapsed: " << watch.elapsed() << std::endl;
+ *
+ * auto const ttl = 60_s;
+ * if (watch.elapsed(ttl)) {
+ * process_expiration();
+ * }
+ *
+ * Example 2:
+ *
+ * struct run_every_n_seconds {
+ * using callback = std::function<void()>;
+ * run_every_n_seconds(std::chrono::seconds period, callback action)
+ * period_(period),
+ * action_(std::move(action))
+ * {
+ * // watch_ is correctly initialized to the current time
+ * }
+ *
+ * void run() {
+ * while (true) {
+ * if (watch_.lap(period_)) {
+ * action_();
+ * }
+ * std::this_thread::yield();
+ * }
+ * }
+ *
+ * private:
+ * stop_watch<> watch_;
+ * std::chrono::seconds period_;
+ * callback action_;
+ * };
+ *
+ * @author: Marcelo Juchem <marcelo@fb.com>
+ */
+template <typename Clock, typename Duration = typename Clock::duration>
+struct custom_stop_watch {
+ using clock_type = Clock;
+ using duration = Duration;
+ using time_point = std::chrono::time_point<clock_type, duration>;
+
+ static_assert(
+ std::ratio_less_equal<
+ typename clock_type::duration::period,
+ typename duration::period>::value,
+ "clock must be at least as precise as the requested duration");
+
+ static_assert(
+ Clock::is_steady,
+ "only monotonic clocks should be used to track time intervals");
+
+ /**
+ * Initializes the stop watch with the current time as its checkpoint.
+ *
+ * Example:
+ *
+ * stop_watch<> watch;
+ * do_something();
+ * std::cout << "time elapsed: " << watch.elapsed() << std::endl;
+ *
+ * @author: Marcelo Juchem <marcelo@fb.com>
+ */
+ custom_stop_watch() : checkpoint_(clock_type::now()) {}
+
+ /**
+ * Initializes the stop watch with the given time as its checkpoint.
+ *
+ * NOTE: this constructor should be seldomly used. It is only provided so
+ * that, in the rare occasions it is needed, one does not have to reimplement
+ * the `custom_stop_watch` class.
+ *
+ * Example:
+ *
+ * custom_stop_watch<monotonic_clock> watch(monotonic_clock::now());
+ * do_something();
+ * std::cout << "time elapsed: " << watch.elapsed() << std::endl;
+ *
+ * @author: Marcelo Juchem <marcelo@fb.com>
+ */
+ explicit custom_stop_watch(typename clock_type::time_point checkpoint)
+ : checkpoint_(std::move(checkpoint)) {}
+
+ /**
+ * Updates the stop watch checkpoint to the current time.
+ *
+ * Example:
+ *
+ * struct some_resource {
+ * // ...
+ *
+ * void on_reloaded() {
+ * time_alive.reset();
+ * }
+ *
+ * void report() {
+ * std::cout << "resource has been alive for " << time_alive.elapsed();
+ * }
+ *
+ * private:
+ * stop_watch<> time_alive;
+ * };
+ *
+ * @author: Marcelo Juchem <marcelo@fb.com>
+ */
+ void reset() {
+ checkpoint_ = clock_type::now();
+ }
+
+ /**
+ * Tells the elapsed time since the last update.
+ *
+ * The stop watch's checkpoint remains unchanged.
+ *
+ * Example:
+ *
+ * stop_watch<> watch;
+ * do_something();
+ * std::cout << "time elapsed: " << watch.elapsed() << std::endl;
+ *
+ * @author: Marcelo Juchem <marcelo@fb.com>
+ */
+ duration elapsed() const {
+ return std::chrono::duration_cast<duration>(
+ clock_type::now() - checkpoint_);
+ }
+
+ /**
+ * Tells whether the given duration has already elapsed since the last
+ * checkpoint.
+ *
+ * Example:
+ *
+ * auto const ttl = 60_s;
+ * stop_watch<> watch;
+ *
+ * do_something();
+ *
+ * std::cout << "has the TTL expired? " std::boolalpha<< watch.elapsed(ttl);
+ *
+ * @author: Marcelo Juchem <marcelo@fb.com>
+ */
+ template <typename UDuration>
+ bool elapsed(UDuration&& amount) const {
+ return clock_type::now() - checkpoint_ >= amount;
+ }
+
+ /**
+ * Tells the elapsed time since the last update, and updates the checkpoint
+ * to the current time.
+ *
+ * Example:
+ *
+ * struct some_resource {
+ * // ...
+ *
+ * void on_reloaded() {
+ * auto const alive = time_alive.lap();
+ * std::cout << "resource reloaded after being alive for " << alive;
+ * }
+ *
+ * private:
+ * stop_watch<> time_alive;
+ * };
+ *
+ * @author: Marcelo Juchem <marcelo@fb.com>
+ */
+ duration lap() {
+ auto lastCheckpoint = checkpoint_;
+
+ checkpoint_ = clock_type::now();
+
+ return std::chrono::duration_cast<duration>(checkpoint_ - lastCheckpoint);
+ }
+
+ /**
+ * Tells whether the given duration has already elapsed since the last
+ * checkpoint. If so, update the checkpoint to the current time. If not,
+ * the checkpoint remains unchanged.
+ *
+ * Example:
+ *
+ * void run_every_n_seconds(
+ * std::chrono::seconds period,
+ * std::function<void()> action
+ * ) {
+ * for (stop_watch<> watch;; ) {
+ * if (watch.lap(period)) {
+ * action();
+ * }
+ * std::this_thread::yield();
+ * }
+ * }
+ *
+ * @author: Marcelo Juchem <marcelo@fb.com>
+ */
+ template <typename UDuration>
+ bool lap(UDuration&& amount) {
+ auto now = clock_type::now();
+
+ if (now - checkpoint_ < amount) {
+ return false;
+ }
+
+ checkpoint_ = now;
+ return true;
+ }
+
+ private:
+ typename clock_type::time_point checkpoint_;
+};
+
+/**
+ * A type alias for `custom_stop_watch` that uses a coarse monotonic clock as
+ * the time source. Refer to the documentation of `custom_stop_watch` for full
+ * documentation.
+ *
+ * Arguments:
+ * - Duration: (optional) the duration to use when reporting elapsed time.
+ * Defaults to the clock's duration.
+ *
+ * Example:
+ *
+ * coarse_stop_watch<std::seconds> watch;
+ * do_something();
+ * std::cout << "time elapsed: " << watch.elapsed() << std::endl;
+ *
+ * @author: Marcelo Juchem <marcelo@fb.com>
+ */
+template <typename Duration = monotonic_coarse_clock::duration>
+using coarse_stop_watch = custom_stop_watch<monotonic_coarse_clock, Duration>;
+
+/**
+ * A type alias for `custom_stop_watch` that uses a monotonic clock as the time
+ * source. Refer to the documentation of `custom_stop_watch` for full
+ * documentation.
+ *
+ * Arguments:
+ * - Duration: (optional) the duration to use when reporting elapsed time.
+ * Defaults to the clock's duration.
+ *
+ * Example:
+ *
+ * stop_watch<std::seconds> watch;
+ * do_something();
+ * std::cout << "time elapsed: " << watch.elapsed() << std::endl;
+ *
+ * @author: Marcelo Juchem <marcelo@fb.com>
+ */
+template <typename Duration = monotonic_clock::duration>
+using stop_watch = custom_stop_watch<monotonic_clock, Duration>;
+}