mirror of
https://github.com/ecency/ecency-mobile.git
synced 2024-12-22 21:01:31 +03:00
318 lines
8.4 KiB
C
318 lines
8.4 KiB
C
|
/*
|
||
|
* 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>;
|
||
|
}
|