blob: 8f57916ff1a3df7050e24d7f42efa18099298e03 [file] [log] [blame]
#ifndef AOS_COMMON_TIME_H_
#define AOS_COMMON_TIME_H_
#include <stdint.h>
#include <time.h>
#include <sys/time.h>
#include <stdint.h>
#include <type_traits>
#include <chrono>
#include <thread>
#include <ostream>
#include "aos/common/type_traits.h"
#include "aos/common/macros.h"
namespace aos {
class monotonic_clock {
public:
typedef ::std::chrono::nanoseconds::rep rep;
typedef ::std::chrono::nanoseconds::period period;
typedef ::std::chrono::nanoseconds duration;
typedef ::std::chrono::time_point<monotonic_clock> time_point;
static monotonic_clock::time_point now() noexcept;
static constexpr bool is_steady = true;
// Returns the epoch (0).
static constexpr monotonic_clock::time_point epoch() {
return time_point(zero());
}
static constexpr monotonic_clock::duration zero() { return duration(0); }
static constexpr time_point min_time{
time_point(duration(::std::numeric_limits<duration::rep>::min()))};
};
namespace time {
// A nice structure for representing times.
// 0 <= nsec_ < kNSecInSec should always be true. All functions here will make
// sure that that is true if it was on all inputs (including *this).
//
// Negative times are supported so that all of the normal arithmetic identities
// work. nsec_ is still always positive.
//
// The arithmetic and comparison operators are overloaded because they make
// complete sense and are very useful. The default copy and assignment stuff is
// left because it works fine. Multiplication of Times by Times is
// not implemented because I can't think of any uses for them and there are
// multiple ways to do it. Division of Times by Times is implemented as the
// ratio of them. Multiplication, division, and modulus of Times by integers are
// implemented as interpreting the argument as nanoseconds. Modulus takes the
// sign from the first operand.
struct Time {
#ifdef SWIG
// All of the uses of constexpr here can safely be simply removed.
// NOTE: This means that relying on the fact that constexpr implicitly makes
// member functions const is not valid, so they all have to be explicitly marked
// const.
#define constexpr
#endif // SWIG
public:
static const int32_t kNSecInSec = 1000000000;
static const int32_t kNSecInMSec = 1000000;
static const int32_t kNSecInUSec = 1000;
static const int32_t kMSecInSec = 1000;
static const int32_t kUSecInSec = 1000000;
static const Time kZero;
explicit constexpr Time(int32_t sec = 0, int32_t nsec = 0)
: sec_(sec), nsec_(CheckConstexpr(nsec)) {
}
#ifndef SWIG
explicit constexpr Time(const struct timespec &value)
: sec_(value.tv_sec), nsec_(CheckConstexpr(value.tv_nsec)) {
}
struct timespec ToTimespec() const {
struct timespec ans;
ans.tv_sec = sec_;
ans.tv_nsec = nsec_;
return ans;
}
explicit constexpr Time(const struct timeval &value)
: sec_(value.tv_sec), nsec_(CheckConstexpr(value.tv_usec * kNSecInUSec)) {
}
struct timeval ToTimeval() const {
struct timeval ans;
ans.tv_sec = sec_;
ans.tv_usec = nsec_ / kNSecInUSec;
return ans;
}
#endif // SWIG
// CLOCK_MONOTONIC on linux and CLOCK_REALTIME on the cRIO because the
// cRIO doesn't have any others.
// CLOCK_REALTIME is the default realtime clock and CLOCK_MONOTONIC doesn't
// change when somebody changes the wall clock (like the ntp deamon or
// whatever). See clock_gettime(2) for details.
//
// This is the clock that code that just wants to sleep for a certain amount
// of time or measure how long something takes should use.
#ifndef __VXWORKS__
static const clockid_t kDefaultClock = CLOCK_MONOTONIC;
#else
static const clockid_t kDefaultClock = CLOCK_REALTIME;
#endif
// Creates a Time representing the current value of the specified clock or
// dies.
static Time Now(clockid_t clock = kDefaultClock);
// Constructs a Time representing seconds.
static constexpr Time InSeconds(double seconds) {
return (seconds < 0.0) ?
Time(static_cast<int32_t>(seconds) - 1,
(seconds - static_cast<int32_t>(seconds) + 1.0) * kNSecInSec) :
Time(static_cast<int32_t>(seconds),
(seconds - static_cast<int32_t>(seconds)) * kNSecInSec);
}
// Constructs a time representing microseconds.
static constexpr Time InNS(int64_t nseconds) {
return (nseconds < 0)
? Time((nseconds - 1) / static_cast<int64_t>(kNSecInSec) - 1,
(((nseconds - 1) % kNSecInSec) + 1) + kNSecInSec)
: Time(nseconds / static_cast<int64_t>(kNSecInSec),
nseconds % kNSecInSec);
}
// Constructs a time representing microseconds.
static constexpr Time InUS(int useconds) {
return (useconds < 0)
? Time((useconds + 1) / kUSecInSec - 1,
(((useconds + 1) % kUSecInSec) - 1) * kNSecInUSec +
kNSecInSec)
: Time(useconds / kUSecInSec,
(useconds % kUSecInSec) * kNSecInUSec);
}
// Constructs a time representing mseconds.
static constexpr Time InMS(int mseconds) {
return (mseconds < 0)
? Time((mseconds + 1) / kMSecInSec - 1,
(((mseconds + 1) % kMSecInSec) - 1) * kNSecInMSec +
kNSecInSec)
: Time(mseconds / kMSecInSec,
(mseconds % kMSecInSec) * kNSecInMSec);
}
// Construct a time representing the period of hertz.
static constexpr ::std::chrono::nanoseconds FromRate(int hertz) {
return ::std::chrono::duration_cast<::std::chrono::nanoseconds>(
::std::chrono::seconds(1)) /
hertz;
}
// Checks whether or not this time is within amount nanoseconds of other.
bool IsWithin(const Time &other, int64_t amount) const;
// Returns the time represented all in nanoseconds.
int64_t constexpr ToNSec() const {
return static_cast<int64_t>(sec_) * static_cast<int64_t>(kNSecInSec) +
static_cast<int64_t>(nsec_);
}
#ifdef __VXWORKS__
// Returns the time represented all in system clock ticks. The system clock
// rate is retrieved using sysClkRateGet().
int ToTicks() const {
return ToNSec() / static_cast<int64_t>(kNSecInSec / sysClkRateGet());
}
// Constructs a Time representing ticks.
static Time InTicks(int ticks) {
return Time::InSeconds(static_cast<double>(ticks) / sysClkRateGet());
}
#endif
// Returns the time represented in milliseconds.
int64_t constexpr ToMSec() const {
return static_cast<int64_t>(sec_) * static_cast<int64_t>(kMSecInSec) +
(static_cast<int64_t>(nsec_) / static_cast<int64_t>(kNSecInMSec));
}
// Returns the time represent in microseconds.
int64_t constexpr ToUSec() const {
return static_cast<int64_t>(sec_) * static_cast<int64_t>(kUSecInSec) +
(static_cast<int64_t>(nsec_) / static_cast<int64_t>(kNSecInUSec));
}
// Returns the time represented in fractional seconds.
double constexpr ToSeconds() const {
return static_cast<double>(sec_) + static_cast<double>(nsec_) / kNSecInSec;
}
#ifndef SWIG
Time &operator+=(const Time &rhs);
Time &operator-=(const Time &rhs);
Time &operator*=(int32_t rhs);
Time &operator/=(int32_t rhs);
Time &operator%=(int32_t rhs);
Time &operator%=(double rhs) = delete;
Time &operator*=(double rhs) = delete;
Time &operator/=(double rhs) = delete;
const Time operator*(double rhs) const = delete;
const Time operator/(double rhs) const = delete;
const Time operator%(double rhs) const = delete;
#endif
const Time operator+(const Time &rhs) const;
const Time operator-(const Time &rhs) const;
const Time operator*(int32_t rhs) const;
const Time operator/(int32_t rhs) const;
double operator/(const Time &rhs) const;
const Time operator%(int32_t rhs) const;
const Time operator-() const;
bool operator==(const Time &rhs) const;
bool operator!=(const Time &rhs) const;
bool operator<(const Time &rhs) const;
bool operator>(const Time &rhs) const;
bool operator<=(const Time &rhs) const;
bool operator>=(const Time &rhs) const;
#ifndef SWIG
// For gtest etc.
friend std::ostream &operator<<(std::ostream &os, const Time &time);
#endif // SWIG
int32_t constexpr sec() const { return sec_; }
void set_sec(int32_t sec) { sec_ = sec; }
int32_t constexpr nsec() const { return nsec_; }
void set_nsec(int32_t nsec) {
nsec_ = nsec;
Check();
}
// Absolute value.
Time abs() const {
if (*this > Time(0, 0)) return *this;
if (nsec_ == 0) return Time(-sec_, 0);
return Time(-sec_ - 1, kNSecInSec - nsec_);
}
// Enables returning the mock time value for Now instead of checking the
// system clock.
static void EnableMockTime(const Time &now = Now());
// Calls SetMockTime with the current actual time.
static void UpdateMockTime();
// Sets now when time is being mocked.
static void SetMockTime(const Time &now);
// Convenience function to just increment the mock time by a certain amount in
// a thread safe way.
static void IncrementMockTime(const Time &amount);
// Disables mocking time.
static void DisableMockTime();
private:
int32_t sec_, nsec_;
// LOG(FATAL)s if nsec is >= kNSecInSec or negative.
static void CheckImpl(int32_t nsec);
void Check() { CheckImpl(nsec_); }
// A constexpr version of CheckImpl that returns the given value when it
// succeeds or evaluates to non-constexpr and returns 0 when it fails.
// This will result in the usual LOG(FATAL) if this is used where it isn't
// required to be constexpr or a compile error if it is.
static constexpr int32_t CheckConstexpr(int32_t nsec) {
return (nsec >= kNSecInSec || nsec < 0) ? CheckImpl(nsec), 0 : nsec;
}
#ifdef SWIG
#undef constexpr
#endif // SWIG
};
// Sleeps for the amount of time represented by time counted by clock.
void SleepFor(const Time &time, clockid_t clock = Time::kDefaultClock);
// Sleeps until clock is at the time represented by time.
void SleepUntil(const Time &time, clockid_t clock = Time::kDefaultClock);
// Sets the global offset for all times so ::aos::time::Time::Now() will return
// now.
// There is no synchronization here, so this is only safe when only a single
// task is running.
// This is only allowed when the shared memory core infrastructure has been
// initialized in this process.
void OffsetToNow(const Time &now);
// RAII class that freezes Time::Now() (to avoid making large numbers of
// syscalls to find the real time).
class TimeFreezer {
public:
TimeFreezer() {
Time::EnableMockTime();
}
~TimeFreezer() {
Time::DisableMockTime();
}
private:
DISALLOW_COPY_AND_ASSIGN(TimeFreezer);
};
} // namespace time
} // namespace aos
namespace std {
namespace this_thread {
// Template specialization for monotonic_clock, since we can use clock_nanosleep
// with TIMER_ABSTIME and get very precise absolute time sleeps.
template <>
void sleep_until(const ::aos::monotonic_clock::time_point &end_time);
} // namespace this_thread
} // namespace std
#endif // AOS_COMMON_TIME_H_