blob: 125b016ca6ab023283eac388aead3b4e2bf6c011 [file] [log] [blame]
brians343bc112013-02-10 01:53:46 +00001#include "aos/common/time.h"
2
Brian Silverman0308f162016-01-02 13:32:49 -08003#include <atomic>
4
Brian Silvermanf665d692013-02-17 22:11:39 -08005#include <string.h>
Brian Silvermand0575692015-02-21 16:24:02 -05006#include <inttypes.h>
7
8// We only use global_core from here, which is weak, so we don't really have a
9// dependency on it.
10#include "aos/linux_code/ipc_lib/shared_mem.h"
brians343bc112013-02-10 01:53:46 +000011
12#include "aos/common/logging/logging.h"
Austin Schuhd78ab542013-03-01 22:22:19 -080013#include "aos/common/mutex.h"
brians343bc112013-02-10 01:53:46 +000014
Austin Schuh793d6b92016-05-01 13:28:14 -070015namespace std {
16namespace this_thread {
17template <>
18void sleep_until(const ::aos::monotonic_clock::time_point &end_time) {
19 struct timespec end_time_timespec;
20 ::std::chrono::seconds sec =
21 ::std::chrono::duration_cast<::std::chrono::seconds>(
22 end_time.time_since_epoch());
23 ::std::chrono::nanoseconds nsec =
24 ::std::chrono::duration_cast<::std::chrono::nanoseconds>(
25 end_time.time_since_epoch() - sec);
26 end_time_timespec.tv_sec = sec.count();
27 end_time_timespec.tv_nsec = nsec.count();
28 int returnval;
29 do {
30 returnval = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME,
31 &end_time_timespec, nullptr);
32 if (returnval != EINTR && returnval != 0) {
33 PLOG(FATAL, "clock_nanosleep(%jd, TIMER_ABSTIME, %p, nullptr) failed",
34 static_cast<uintmax_t>(CLOCK_MONOTONIC), &end_time_timespec);
35 }
36 } while (returnval != 0);
37}
38
39} // namespace this_thread
40} // namespace std
41
42
brians343bc112013-02-10 01:53:46 +000043namespace aos {
Austin Schuh793d6b92016-05-01 13:28:14 -070044monotonic_clock::time_point monotonic_clock::now() noexcept {
45 struct timespec current_time;
46 if (clock_gettime(CLOCK_MONOTONIC, &current_time) != 0) {
47 PLOG(FATAL, "clock_gettime(%jd, %p) failed",
48 static_cast<uintmax_t>(CLOCK_MONOTONIC), &current_time);
49 }
50 return time_point(::std::chrono::seconds(current_time.tv_sec) +
51 ::std::chrono::nanoseconds(current_time.tv_nsec));
52}
53
brians343bc112013-02-10 01:53:46 +000054namespace time {
55
Austin Schuhd78ab542013-03-01 22:22:19 -080056// State required to enable and use mock time.
57namespace {
58// True if mock time is enabled.
59// This does not need to be checked with the mutex held because setting time to
60// be enabled or disabled is atomic, and all future operations are atomic
61// anyways. If there is a race condition setting or clearing whether time is
62// enabled or not, it will still be a race condition if current_mock_time is
63// also set atomically with enabled.
Brian Silverman0308f162016-01-02 13:32:49 -080064::std::atomic<bool> mock_time_enabled{false};
Austin Schuhd78ab542013-03-01 22:22:19 -080065// Mutex to make time reads and writes thread safe.
66Mutex time_mutex;
67// Current time when time is mocked.
68Time current_mock_time(0, 0);
69
70// TODO(aschuh): This doesn't include SleepFor and SleepUntil.
71// TODO(aschuh): Create a clock source object and change the default?
72// That would let me create a MockTime clock source.
Brian Silvermanb407c672014-04-09 11:58:37 -070073
74Time NowImpl(clockid_t clock) {
75 timespec temp;
76 if (clock_gettime(clock, &temp) != 0) {
Brian Silverman01be0002014-05-10 15:44:38 -070077 PLOG(FATAL, "clock_gettime(%jd, %p) failed",
78 static_cast<uintmax_t>(clock), &temp);
Brian Silvermanb407c672014-04-09 11:58:37 -070079 }
Brian Silvermand0575692015-02-21 16:24:02 -050080
Brian Silverman0c715e62015-10-24 16:49:46 -040081 const timespec offset = (&global_core == nullptr || global_core == nullptr ||
82 global_core->mem_struct == nullptr)
Brian Silvermand0575692015-02-21 16:24:02 -050083 ? timespec{0, 0}
84 : global_core->mem_struct->time_offset;
85 return Time(temp) + Time(offset);
brians343bc112013-02-10 01:53:46 +000086}
Austin Schuhd78ab542013-03-01 22:22:19 -080087
Brian Silvermanb407c672014-04-09 11:58:37 -070088} // namespace
89
Brian Silvermandcaa3f72015-11-29 05:32:08 +000090const int32_t Time::kNSecInSec;
91const int32_t Time::kNSecInMSec;
92const int32_t Time::kNSecInUSec;
93const int32_t Time::kMSecInSec;
94const int32_t Time::kUSecInSec;
95
96const Time Time::kZero{0, 0};
97
Brian Silverman6659bc32013-10-16 10:31:32 -070098void Time::EnableMockTime(const Time &now) {
Austin Schuhd78ab542013-03-01 22:22:19 -080099 MutexLocker time_mutex_locker(&time_mutex);
Brian Silvermanb407c672014-04-09 11:58:37 -0700100 mock_time_enabled = true;
Austin Schuhd78ab542013-03-01 22:22:19 -0800101 current_mock_time = now;
102}
103
Brian Silvermanb407c672014-04-09 11:58:37 -0700104void Time::UpdateMockTime() {
105 SetMockTime(NowImpl(kDefaultClock));
106}
107
Austin Schuhd78ab542013-03-01 22:22:19 -0800108void Time::DisableMockTime() {
109 MutexLocker time_mutex_locker(&time_mutex);
110 mock_time_enabled = false;
111}
112
Brian Silverman6659bc32013-10-16 10:31:32 -0700113void Time::SetMockTime(const Time &now) {
Austin Schuhd78ab542013-03-01 22:22:19 -0800114 MutexLocker time_mutex_locker(&time_mutex);
Brian Silvermanb407c672014-04-09 11:58:37 -0700115 if (__builtin_expect(!mock_time_enabled, 0)) {
Austin Schuhd78ab542013-03-01 22:22:19 -0800116 LOG(FATAL, "Tried to set mock time and mock time is not enabled\n");
117 }
118 current_mock_time = now;
119}
120
Brian Silverman6659bc32013-10-16 10:31:32 -0700121void Time::IncrementMockTime(const Time &amount) {
122 static ::aos::Mutex mutex;
123 ::aos::MutexLocker sync(&mutex);
124 SetMockTime(Now() + amount);
125}
126
Austin Schuhd78ab542013-03-01 22:22:19 -0800127Time Time::Now(clockid_t clock) {
Brian Silvermanb407c672014-04-09 11:58:37 -0700128 {
Brian Silverman0308f162016-01-02 13:32:49 -0800129 if (mock_time_enabled.load(::std::memory_order_relaxed)) {
130 MutexLocker time_mutex_locker(&time_mutex);
Brian Silvermanb407c672014-04-09 11:58:37 -0700131 return current_mock_time;
Austin Schuhd78ab542013-03-01 22:22:19 -0800132 }
Austin Schuhd78ab542013-03-01 22:22:19 -0800133 }
Brian Silvermanb407c672014-04-09 11:58:37 -0700134 return NowImpl(clock);
Austin Schuhd78ab542013-03-01 22:22:19 -0800135}
136
Brian Silverman52aeeac2013-08-28 16:20:53 -0700137void Time::CheckImpl(int32_t nsec) {
brians343bc112013-02-10 01:53:46 +0000138 static_assert(aos::shm_ok<Time>::value,
139 "it should be able to go through shared memory");
Brian Silverman52aeeac2013-08-28 16:20:53 -0700140 if (nsec >= kNSecInSec || nsec < 0) {
141 LOG(FATAL, "0 <= nsec(%" PRId32 ") < %" PRId32 " isn't true.\n",
142 nsec, kNSecInSec);
143 }
brians343bc112013-02-10 01:53:46 +0000144}
145
146Time &Time::operator+=(const Time &rhs) {
147 sec_ += rhs.sec_;
148 nsec_ += rhs.nsec_;
149 if (nsec_ >= kNSecInSec) {
150 nsec_ -= kNSecInSec;
151 sec_ += 1;
152 }
153 return *this;
154}
155const Time Time::operator+(const Time &rhs) const {
156 return Time(*this) += rhs;
157}
158Time &Time::operator-=(const Time &rhs) {
159 sec_ -= rhs.sec_;
160 nsec_ -= rhs.nsec_;
161 if (nsec_ < 0) {
162 nsec_ += kNSecInSec;
163 sec_ -= 1;
164 }
165 return *this;
166}
167const Time Time::operator-(const Time &rhs) const {
168 return Time(*this) -= rhs;
169}
170Time &Time::operator*=(int32_t rhs) {
171 const int64_t temp = static_cast<int64_t>(nsec_) *
172 static_cast<int64_t>(rhs);
173 sec_ *= rhs; // better not overflow, or the result is just too big
174 nsec_ = temp % kNSecInSec;
175 sec_ += (temp - nsec_) / kNSecInSec;
Brian Silverman6659bc32013-10-16 10:31:32 -0700176 if (nsec_ < 0) {
177 nsec_ += kNSecInSec;
178 sec_ -= 1;
179 }
brians343bc112013-02-10 01:53:46 +0000180 return *this;
181}
182const Time Time::operator*(int32_t rhs) const {
183 return Time(*this) *= rhs;
184}
185Time &Time::operator/=(int32_t rhs) {
186 nsec_ = (sec_ % rhs) * (kNSecInSec / rhs) + nsec_ / rhs;
187 sec_ /= rhs;
Brian Silverman6659bc32013-10-16 10:31:32 -0700188 if (nsec_ < 0) {
189 nsec_ += kNSecInSec;
190 sec_ -= 1;
191 }
brians343bc112013-02-10 01:53:46 +0000192 return *this;
193}
194const Time Time::operator/(int32_t rhs) const {
195 return Time(*this) /= rhs;
196}
Brian Silverman2e0dcfd2013-03-30 22:44:40 -0700197double Time::operator/(const Time &rhs) const {
198 return ToSeconds() / rhs.ToSeconds();
199}
brians343bc112013-02-10 01:53:46 +0000200Time &Time::operator%=(int32_t rhs) {
201 nsec_ = ToNSec() % rhs;
Brian Silverman0534df62014-05-26 21:19:15 -0700202 const int wraps = nsec_ / ((rhs / kNSecInSec + 1) * kNSecInSec);
203 sec_ = wraps + rhs / kNSecInSec;
brians343bc112013-02-10 01:53:46 +0000204 nsec_ -= kNSecInSec * wraps;
Brian Silverman6659bc32013-10-16 10:31:32 -0700205 if (nsec_ < 0) {
206 nsec_ += kNSecInSec;
207 sec_ -= 1;
208 }
brians343bc112013-02-10 01:53:46 +0000209 return *this;
210}
211const Time Time::operator%(int32_t rhs) const {
212 return Time(*this) %= rhs;
213}
214
Brian Silverman0079a9d2013-10-24 15:57:35 -0700215const Time Time::operator-() const {
216 return Time(-sec_ - 1, kNSecInSec - nsec_);
217}
218
brians343bc112013-02-10 01:53:46 +0000219bool Time::operator==(const Time &rhs) const {
220 return sec_ == rhs.sec_ && nsec_ == rhs.nsec_;
221}
222bool Time::operator!=(const Time &rhs) const {
223 return !(*this == rhs);
224}
225bool Time::operator<(const Time &rhs) const {
226 return sec_ < rhs.sec_ || (sec_ == rhs.sec_ && nsec_ < rhs.nsec_);
227}
228bool Time::operator>(const Time &rhs) const {
229 return sec_ > rhs.sec_ || (sec_ == rhs.sec_ && nsec_ > rhs.nsec_);
230}
231bool Time::operator<=(const Time &rhs) const {
232 return sec_ < rhs.sec_ || (sec_ == rhs.sec_ && nsec_ <= rhs.nsec_);
233}
234bool Time::operator>=(const Time &rhs) const {
235 return sec_ > rhs.sec_ || (sec_ == rhs.sec_ && nsec_ >= rhs.nsec_);
236}
237
238bool Time::IsWithin(const Time &other, int64_t amount) const {
239 const int64_t temp = ToNSec() - other.ToNSec();
240 return temp <= amount && temp >= -amount;
241}
242
243std::ostream &operator<<(std::ostream &os, const Time& time) {
244 return os << "Time{" << time.sec_ << "s, " << time.nsec_ << "ns}";
245}
246
247void SleepFor(const Time &time, clockid_t clock) {
brians343bc112013-02-10 01:53:46 +0000248 timespec converted(time.ToTimespec()), remaining;
249 int failure = EINTR;
250 do {
251 // This checks whether the last time through the loop actually failed or got
252 // interrupted.
253 if (failure != EINTR) {
Brian Silverman01be0002014-05-10 15:44:38 -0700254 PELOG(FATAL, failure, "clock_nanosleep(%jd, 0, %p, %p) failed",
255 static_cast<intmax_t>(clock), &converted, &remaining);
brians343bc112013-02-10 01:53:46 +0000256 }
257 failure = clock_nanosleep(clock, 0, &converted, &remaining);
258 memcpy(&converted, &remaining, sizeof(converted));
259 } while (failure != 0);
brians343bc112013-02-10 01:53:46 +0000260}
261
262void SleepUntil(const Time &time, clockid_t clock) {
brians343bc112013-02-10 01:53:46 +0000263 timespec converted(time.ToTimespec());
264 int failure;
265 while ((failure = clock_nanosleep(clock, TIMER_ABSTIME,
266 &converted, NULL)) != 0) {
267 if (failure != EINTR) {
Brian Silverman01be0002014-05-10 15:44:38 -0700268 PELOG(FATAL, failure, "clock_nanosleep(%jd, TIMER_ABSTIME, %p, NULL)"
269 " failed", static_cast<intmax_t>(clock), &converted);
brians343bc112013-02-10 01:53:46 +0000270 }
271 }
brians343bc112013-02-10 01:53:46 +0000272}
273
Brian Silvermand0575692015-02-21 16:24:02 -0500274void OffsetToNow(const Time &now) {
Brian Silverman0c715e62015-10-24 16:49:46 -0400275 CHECK_NOTNULL(&global_core);
Brian Silvermand0575692015-02-21 16:24:02 -0500276 CHECK_NOTNULL(global_core);
Brian Silverman0c715e62015-10-24 16:49:46 -0400277 CHECK_NOTNULL(global_core->mem_struct);
Brian Silvermand0575692015-02-21 16:24:02 -0500278 global_core->mem_struct->time_offset.tv_nsec = 0;
279 global_core->mem_struct->time_offset.tv_sec = 0;
280 global_core->mem_struct->time_offset = (now - Time::Now()).ToTimespec();
281}
282
brians343bc112013-02-10 01:53:46 +0000283} // namespace time
284} // namespace aos