text: /*
   * Copyright (c) Facebook, Inc. and its affiliates.
   *
   * 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 <chrono>
  #include <stdexcept>
  #include <utility>
  
  #include <folly/Chrono.h>
  #include <folly/portability/Time.h>
  
  namespace folly {
  
  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::chrono::seconds> watch;
   *  do_something();
   *  std::cout << "time elapsed: " << watch.elapsed().count() << 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;
    }
  
    /**
     * Returns the current checkpoint
     */
    typename clock_type::time_point getCheckpoint() const {
      return checkpoint_;
    }
  
   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::chrono::seconds> watch;
   *  do_something();
   *  std::cout << "time elapsed: " << watch.elapsed().count() << std::endl;
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  template <typename Duration = folly::chrono::coarse_steady_clock::duration>
  using coarse_stop_watch =
      custom_stop_watch<folly::chrono::coarse_steady_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::chrono::seconds> watch;
   *  do_something();
   *  std::cout << "time elapsed: " << watch.elapsed().count() << std::endl;
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  template <typename Duration = std::chrono::steady_clock::duration>
  using stop_watch = custom_stop_watch<std::chrono::steady_clock, Duration>;
  } // namespace folly
  