blob: b2d3675441bbdd95add425607eb5a352cfd8a261 [file] [log] [blame]
#include "aos/common/time.h"
#include <atomic>
#include <string.h>
#include <inttypes.h>
// We only use global_core from here, which is weak, so we don't really have a
// dependency on it.
#include "aos/linux_code/ipc_lib/shared_mem.h"
#include "aos/common/logging/logging.h"
#include "aos/common/mutex.h"
namespace std {
namespace this_thread {
template <>
void sleep_until(const ::aos::monotonic_clock::time_point &end_time) {
struct timespec end_time_timespec;
::std::chrono::seconds sec =
::std::chrono::duration_cast<::std::chrono::seconds>(
end_time.time_since_epoch());
::std::chrono::nanoseconds nsec =
::std::chrono::duration_cast<::std::chrono::nanoseconds>(
end_time.time_since_epoch() - sec);
end_time_timespec.tv_sec = sec.count();
end_time_timespec.tv_nsec = nsec.count();
int returnval;
do {
returnval = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME,
&end_time_timespec, nullptr);
if (returnval != EINTR && returnval != 0) {
PLOG(FATAL, "clock_nanosleep(%jd, TIMER_ABSTIME, %p, nullptr) failed",
static_cast<uintmax_t>(CLOCK_MONOTONIC), &end_time_timespec);
}
} while (returnval != 0);
}
} // namespace this_thread
} // namespace std
namespace aos {
namespace time {
// State required to enable and use mock time.
namespace {
// True if mock time is enabled.
// This does not need to be checked with the mutex held because setting time to
// be enabled or disabled is atomic, and all future operations are atomic
// anyways. If there is a race condition setting or clearing whether time is
// enabled or not, it will still be a race condition if current_mock_time is
// also set atomically with enabled.
::std::atomic<bool> mock_time_enabled{false};
// Mutex to make time reads and writes thread safe.
Mutex time_mutex;
// Current time when time is mocked.
Time current_mock_time(0, 0);
// TODO(aschuh): This doesn't include SleepFor and SleepUntil.
// TODO(aschuh): Create a clock source object and change the default?
// That would let me create a MockTime clock source.
Time NowImpl(clockid_t clock) {
timespec temp;
if (clock_gettime(clock, &temp) != 0) {
PLOG(FATAL, "clock_gettime(%jd, %p) failed",
static_cast<uintmax_t>(clock), &temp);
}
const timespec offset = (&global_core == nullptr || global_core == nullptr ||
global_core->mem_struct == nullptr)
? timespec{0, 0}
: global_core->mem_struct->time_offset;
return Time(temp) + Time(offset);
}
} // namespace
const int32_t Time::kNSecInSec;
const int32_t Time::kNSecInMSec;
const int32_t Time::kNSecInUSec;
const int32_t Time::kMSecInSec;
const int32_t Time::kUSecInSec;
const Time Time::kZero{0, 0};
void Time::EnableMockTime(const Time &now) {
MutexLocker time_mutex_locker(&time_mutex);
mock_time_enabled = true;
current_mock_time = now;
}
void Time::UpdateMockTime() {
SetMockTime(NowImpl(kDefaultClock));
}
void Time::DisableMockTime() {
MutexLocker time_mutex_locker(&time_mutex);
mock_time_enabled = false;
}
void Time::SetMockTime(const Time &now) {
MutexLocker time_mutex_locker(&time_mutex);
if (__builtin_expect(!mock_time_enabled, 0)) {
LOG(FATAL, "Tried to set mock time and mock time is not enabled\n");
}
current_mock_time = now;
}
void Time::IncrementMockTime(const Time &amount) {
static ::aos::Mutex mutex;
::aos::MutexLocker sync(&mutex);
SetMockTime(Now() + amount);
}
Time Time::Now(clockid_t clock) {
{
if (mock_time_enabled.load(::std::memory_order_relaxed)) {
MutexLocker time_mutex_locker(&time_mutex);
return current_mock_time;
}
}
return NowImpl(clock);
}
void Time::CheckImpl(int32_t nsec) {
static_assert(aos::shm_ok<Time>::value,
"it should be able to go through shared memory");
if (nsec >= kNSecInSec || nsec < 0) {
LOG(FATAL, "0 <= nsec(%" PRId32 ") < %" PRId32 " isn't true.\n",
nsec, kNSecInSec);
}
}
Time &Time::operator+=(const Time &rhs) {
sec_ += rhs.sec_;
nsec_ += rhs.nsec_;
if (nsec_ >= kNSecInSec) {
nsec_ -= kNSecInSec;
sec_ += 1;
}
return *this;
}
const Time Time::operator+(const Time &rhs) const {
return Time(*this) += rhs;
}
Time &Time::operator-=(const Time &rhs) {
sec_ -= rhs.sec_;
nsec_ -= rhs.nsec_;
if (nsec_ < 0) {
nsec_ += kNSecInSec;
sec_ -= 1;
}
return *this;
}
const Time Time::operator-(const Time &rhs) const {
return Time(*this) -= rhs;
}
Time &Time::operator*=(int32_t rhs) {
const int64_t temp = static_cast<int64_t>(nsec_) *
static_cast<int64_t>(rhs);
sec_ *= rhs; // better not overflow, or the result is just too big
nsec_ = temp % kNSecInSec;
sec_ += (temp - nsec_) / kNSecInSec;
if (nsec_ < 0) {
nsec_ += kNSecInSec;
sec_ -= 1;
}
return *this;
}
const Time Time::operator*(int32_t rhs) const {
return Time(*this) *= rhs;
}
Time &Time::operator/=(int32_t rhs) {
nsec_ = (sec_ % rhs) * (kNSecInSec / rhs) + nsec_ / rhs;
sec_ /= rhs;
if (nsec_ < 0) {
nsec_ += kNSecInSec;
sec_ -= 1;
}
return *this;
}
const Time Time::operator/(int32_t rhs) const {
return Time(*this) /= rhs;
}
double Time::operator/(const Time &rhs) const {
return ToSeconds() / rhs.ToSeconds();
}
Time &Time::operator%=(int32_t rhs) {
nsec_ = ToNSec() % rhs;
const int wraps = nsec_ / ((rhs / kNSecInSec + 1) * kNSecInSec);
sec_ = wraps + rhs / kNSecInSec;
nsec_ -= kNSecInSec * wraps;
if (nsec_ < 0) {
nsec_ += kNSecInSec;
sec_ -= 1;
}
return *this;
}
const Time Time::operator%(int32_t rhs) const {
return Time(*this) %= rhs;
}
const Time Time::operator-() const {
return Time(-sec_ - 1, kNSecInSec - nsec_);
}
bool Time::operator==(const Time &rhs) const {
return sec_ == rhs.sec_ && nsec_ == rhs.nsec_;
}
bool Time::operator!=(const Time &rhs) const {
return !(*this == rhs);
}
bool Time::operator<(const Time &rhs) const {
return sec_ < rhs.sec_ || (sec_ == rhs.sec_ && nsec_ < rhs.nsec_);
}
bool Time::operator>(const Time &rhs) const {
return sec_ > rhs.sec_ || (sec_ == rhs.sec_ && nsec_ > rhs.nsec_);
}
bool Time::operator<=(const Time &rhs) const {
return sec_ < rhs.sec_ || (sec_ == rhs.sec_ && nsec_ <= rhs.nsec_);
}
bool Time::operator>=(const Time &rhs) const {
return sec_ > rhs.sec_ || (sec_ == rhs.sec_ && nsec_ >= rhs.nsec_);
}
bool Time::IsWithin(const Time &other, int64_t amount) const {
const int64_t temp = ToNSec() - other.ToNSec();
return temp <= amount && temp >= -amount;
}
std::ostream &operator<<(std::ostream &os, const Time& time) {
return os << "Time{" << time.sec_ << "s, " << time.nsec_ << "ns}";
}
void SleepFor(const Time &time, clockid_t clock) {
timespec converted(time.ToTimespec()), remaining;
int failure = EINTR;
do {
// This checks whether the last time through the loop actually failed or got
// interrupted.
if (failure != EINTR) {
PELOG(FATAL, failure, "clock_nanosleep(%jd, 0, %p, %p) failed",
static_cast<intmax_t>(clock), &converted, &remaining);
}
failure = clock_nanosleep(clock, 0, &converted, &remaining);
memcpy(&converted, &remaining, sizeof(converted));
} while (failure != 0);
}
void SleepUntil(const Time &time, clockid_t clock) {
timespec converted(time.ToTimespec());
int failure;
while ((failure = clock_nanosleep(clock, TIMER_ABSTIME,
&converted, NULL)) != 0) {
if (failure != EINTR) {
PELOG(FATAL, failure, "clock_nanosleep(%jd, TIMER_ABSTIME, %p, NULL)"
" failed", static_cast<intmax_t>(clock), &converted);
}
}
}
void OffsetToNow(const Time &now) {
CHECK_NOTNULL(&global_core);
CHECK_NOTNULL(global_core);
CHECK_NOTNULL(global_core->mem_struct);
global_core->mem_struct->time_offset.tv_nsec = 0;
global_core->mem_struct->time_offset.tv_sec = 0;
global_core->mem_struct->time_offset = (now - Time::Now()).ToTimespec();
}
} // namespace time
constexpr monotonic_clock::time_point monotonic_clock::min_time;
monotonic_clock::time_point monotonic_clock::now() noexcept {
{
if (time::mock_time_enabled.load(::std::memory_order_relaxed)) {
MutexLocker time_mutex_locker(&time::time_mutex);
return monotonic_clock::time_point(
::std::chrono::nanoseconds(time::current_mock_time.ToNSec()));
}
}
struct timespec current_time;
if (clock_gettime(CLOCK_MONOTONIC, &current_time) != 0) {
PLOG(FATAL, "clock_gettime(%jd, %p) failed",
static_cast<uintmax_t>(CLOCK_MONOTONIC), &current_time);
}
return time_point(::std::chrono::seconds(current_time.tv_sec) +
::std::chrono::nanoseconds(current_time.tv_nsec));
}
} // namespace aos