blob: d1985b593366f2389be559e4f3e2fd3ca82a471b [file] [log] [blame]
brians343bc112013-02-10 01:53:46 +00001#include "aos/common/time.h"
2
Brian Silvermanf665d692013-02-17 22:11:39 -08003#include <string.h>
Brian Silvermand0575692015-02-21 16:24:02 -05004#include <inttypes.h>
5
6// We only use global_core from here, which is weak, so we don't really have a
7// dependency on it.
8#include "aos/linux_code/ipc_lib/shared_mem.h"
brians343bc112013-02-10 01:53:46 +00009
10#include "aos/common/logging/logging.h"
Austin Schuhd78ab542013-03-01 22:22:19 -080011#include "aos/common/mutex.h"
brians343bc112013-02-10 01:53:46 +000012
13namespace aos {
14namespace time {
15
Austin Schuhd78ab542013-03-01 22:22:19 -080016// State required to enable and use mock time.
17namespace {
18// True if mock time is enabled.
19// This does not need to be checked with the mutex held because setting time to
20// be enabled or disabled is atomic, and all future operations are atomic
21// anyways. If there is a race condition setting or clearing whether time is
22// enabled or not, it will still be a race condition if current_mock_time is
23// also set atomically with enabled.
24bool mock_time_enabled = false;
25// Mutex to make time reads and writes thread safe.
26Mutex time_mutex;
27// Current time when time is mocked.
28Time current_mock_time(0, 0);
29
30// TODO(aschuh): This doesn't include SleepFor and SleepUntil.
31// TODO(aschuh): Create a clock source object and change the default?
32// That would let me create a MockTime clock source.
Brian Silvermanb407c672014-04-09 11:58:37 -070033
34Time NowImpl(clockid_t clock) {
35 timespec temp;
36 if (clock_gettime(clock, &temp) != 0) {
Brian Silverman01be0002014-05-10 15:44:38 -070037 PLOG(FATAL, "clock_gettime(%jd, %p) failed",
38 static_cast<uintmax_t>(clock), &temp);
Brian Silvermanb407c672014-04-09 11:58:37 -070039 }
Brian Silvermand0575692015-02-21 16:24:02 -050040
Brian Silverman0c715e62015-10-24 16:49:46 -040041 const timespec offset = (&global_core == nullptr || global_core == nullptr ||
42 global_core->mem_struct == nullptr)
Brian Silvermand0575692015-02-21 16:24:02 -050043 ? timespec{0, 0}
44 : global_core->mem_struct->time_offset;
45 return Time(temp) + Time(offset);
brians343bc112013-02-10 01:53:46 +000046}
Austin Schuhd78ab542013-03-01 22:22:19 -080047
Brian Silvermanb407c672014-04-09 11:58:37 -070048} // namespace
49
Brian Silverman6659bc32013-10-16 10:31:32 -070050void Time::EnableMockTime(const Time &now) {
Austin Schuhd78ab542013-03-01 22:22:19 -080051 MutexLocker time_mutex_locker(&time_mutex);
Brian Silvermanb407c672014-04-09 11:58:37 -070052 mock_time_enabled = true;
Austin Schuhd78ab542013-03-01 22:22:19 -080053 current_mock_time = now;
54}
55
Brian Silvermanb407c672014-04-09 11:58:37 -070056void Time::UpdateMockTime() {
57 SetMockTime(NowImpl(kDefaultClock));
58}
59
Austin Schuhd78ab542013-03-01 22:22:19 -080060void Time::DisableMockTime() {
61 MutexLocker time_mutex_locker(&time_mutex);
62 mock_time_enabled = false;
63}
64
Brian Silverman6659bc32013-10-16 10:31:32 -070065void Time::SetMockTime(const Time &now) {
Austin Schuhd78ab542013-03-01 22:22:19 -080066 MutexLocker time_mutex_locker(&time_mutex);
Brian Silvermanb407c672014-04-09 11:58:37 -070067 if (__builtin_expect(!mock_time_enabled, 0)) {
Austin Schuhd78ab542013-03-01 22:22:19 -080068 LOG(FATAL, "Tried to set mock time and mock time is not enabled\n");
69 }
70 current_mock_time = now;
71}
72
Brian Silverman6659bc32013-10-16 10:31:32 -070073void Time::IncrementMockTime(const Time &amount) {
74 static ::aos::Mutex mutex;
75 ::aos::MutexLocker sync(&mutex);
76 SetMockTime(Now() + amount);
77}
78
Austin Schuhd78ab542013-03-01 22:22:19 -080079Time Time::Now(clockid_t clock) {
Brian Silvermanb407c672014-04-09 11:58:37 -070080 {
Austin Schuhd78ab542013-03-01 22:22:19 -080081 MutexLocker time_mutex_locker(&time_mutex);
Brian Silvermanb407c672014-04-09 11:58:37 -070082 if (mock_time_enabled) {
83 return current_mock_time;
Austin Schuhd78ab542013-03-01 22:22:19 -080084 }
Austin Schuhd78ab542013-03-01 22:22:19 -080085 }
Brian Silvermanb407c672014-04-09 11:58:37 -070086 return NowImpl(clock);
Austin Schuhd78ab542013-03-01 22:22:19 -080087}
88
Brian Silverman52aeeac2013-08-28 16:20:53 -070089void Time::CheckImpl(int32_t nsec) {
brians343bc112013-02-10 01:53:46 +000090 static_assert(aos::shm_ok<Time>::value,
91 "it should be able to go through shared memory");
Brian Silverman52aeeac2013-08-28 16:20:53 -070092 if (nsec >= kNSecInSec || nsec < 0) {
93 LOG(FATAL, "0 <= nsec(%" PRId32 ") < %" PRId32 " isn't true.\n",
94 nsec, kNSecInSec);
95 }
brians343bc112013-02-10 01:53:46 +000096}
97
98Time &Time::operator+=(const Time &rhs) {
99 sec_ += rhs.sec_;
100 nsec_ += rhs.nsec_;
101 if (nsec_ >= kNSecInSec) {
102 nsec_ -= kNSecInSec;
103 sec_ += 1;
104 }
105 return *this;
106}
107const Time Time::operator+(const Time &rhs) const {
108 return Time(*this) += rhs;
109}
110Time &Time::operator-=(const Time &rhs) {
111 sec_ -= rhs.sec_;
112 nsec_ -= rhs.nsec_;
113 if (nsec_ < 0) {
114 nsec_ += kNSecInSec;
115 sec_ -= 1;
116 }
117 return *this;
118}
119const Time Time::operator-(const Time &rhs) const {
120 return Time(*this) -= rhs;
121}
122Time &Time::operator*=(int32_t rhs) {
123 const int64_t temp = static_cast<int64_t>(nsec_) *
124 static_cast<int64_t>(rhs);
125 sec_ *= rhs; // better not overflow, or the result is just too big
126 nsec_ = temp % kNSecInSec;
127 sec_ += (temp - nsec_) / kNSecInSec;
Brian Silverman6659bc32013-10-16 10:31:32 -0700128 if (nsec_ < 0) {
129 nsec_ += kNSecInSec;
130 sec_ -= 1;
131 }
brians343bc112013-02-10 01:53:46 +0000132 return *this;
133}
134const Time Time::operator*(int32_t rhs) const {
135 return Time(*this) *= rhs;
136}
137Time &Time::operator/=(int32_t rhs) {
138 nsec_ = (sec_ % rhs) * (kNSecInSec / rhs) + nsec_ / rhs;
139 sec_ /= rhs;
Brian Silverman6659bc32013-10-16 10:31:32 -0700140 if (nsec_ < 0) {
141 nsec_ += kNSecInSec;
142 sec_ -= 1;
143 }
brians343bc112013-02-10 01:53:46 +0000144 return *this;
145}
146const Time Time::operator/(int32_t rhs) const {
147 return Time(*this) /= rhs;
148}
Brian Silverman2e0dcfd2013-03-30 22:44:40 -0700149double Time::operator/(const Time &rhs) const {
150 return ToSeconds() / rhs.ToSeconds();
151}
brians343bc112013-02-10 01:53:46 +0000152Time &Time::operator%=(int32_t rhs) {
153 nsec_ = ToNSec() % rhs;
Brian Silverman0534df62014-05-26 21:19:15 -0700154 const int wraps = nsec_ / ((rhs / kNSecInSec + 1) * kNSecInSec);
155 sec_ = wraps + rhs / kNSecInSec;
brians343bc112013-02-10 01:53:46 +0000156 nsec_ -= kNSecInSec * wraps;
Brian Silverman6659bc32013-10-16 10:31:32 -0700157 if (nsec_ < 0) {
158 nsec_ += kNSecInSec;
159 sec_ -= 1;
160 }
brians343bc112013-02-10 01:53:46 +0000161 return *this;
162}
163const Time Time::operator%(int32_t rhs) const {
164 return Time(*this) %= rhs;
165}
166
Brian Silverman0079a9d2013-10-24 15:57:35 -0700167const Time Time::operator-() const {
168 return Time(-sec_ - 1, kNSecInSec - nsec_);
169}
170
brians343bc112013-02-10 01:53:46 +0000171bool Time::operator==(const Time &rhs) const {
172 return sec_ == rhs.sec_ && nsec_ == rhs.nsec_;
173}
174bool Time::operator!=(const Time &rhs) const {
175 return !(*this == rhs);
176}
177bool Time::operator<(const Time &rhs) const {
178 return sec_ < rhs.sec_ || (sec_ == rhs.sec_ && nsec_ < rhs.nsec_);
179}
180bool Time::operator>(const Time &rhs) const {
181 return sec_ > rhs.sec_ || (sec_ == rhs.sec_ && nsec_ > rhs.nsec_);
182}
183bool Time::operator<=(const Time &rhs) const {
184 return sec_ < rhs.sec_ || (sec_ == rhs.sec_ && nsec_ <= rhs.nsec_);
185}
186bool Time::operator>=(const Time &rhs) const {
187 return sec_ > rhs.sec_ || (sec_ == rhs.sec_ && nsec_ >= rhs.nsec_);
188}
189
190bool Time::IsWithin(const Time &other, int64_t amount) const {
191 const int64_t temp = ToNSec() - other.ToNSec();
192 return temp <= amount && temp >= -amount;
193}
194
195std::ostream &operator<<(std::ostream &os, const Time& time) {
196 return os << "Time{" << time.sec_ << "s, " << time.nsec_ << "ns}";
197}
198
199void SleepFor(const Time &time, clockid_t clock) {
brians343bc112013-02-10 01:53:46 +0000200 timespec converted(time.ToTimespec()), remaining;
201 int failure = EINTR;
202 do {
203 // This checks whether the last time through the loop actually failed or got
204 // interrupted.
205 if (failure != EINTR) {
Brian Silverman01be0002014-05-10 15:44:38 -0700206 PELOG(FATAL, failure, "clock_nanosleep(%jd, 0, %p, %p) failed",
207 static_cast<intmax_t>(clock), &converted, &remaining);
brians343bc112013-02-10 01:53:46 +0000208 }
209 failure = clock_nanosleep(clock, 0, &converted, &remaining);
210 memcpy(&converted, &remaining, sizeof(converted));
211 } while (failure != 0);
brians343bc112013-02-10 01:53:46 +0000212}
213
214void SleepUntil(const Time &time, clockid_t clock) {
brians343bc112013-02-10 01:53:46 +0000215 timespec converted(time.ToTimespec());
216 int failure;
217 while ((failure = clock_nanosleep(clock, TIMER_ABSTIME,
218 &converted, NULL)) != 0) {
219 if (failure != EINTR) {
Brian Silverman01be0002014-05-10 15:44:38 -0700220 PELOG(FATAL, failure, "clock_nanosleep(%jd, TIMER_ABSTIME, %p, NULL)"
221 " failed", static_cast<intmax_t>(clock), &converted);
brians343bc112013-02-10 01:53:46 +0000222 }
223 }
brians343bc112013-02-10 01:53:46 +0000224}
225
Brian Silvermand0575692015-02-21 16:24:02 -0500226void OffsetToNow(const Time &now) {
Brian Silverman0c715e62015-10-24 16:49:46 -0400227 CHECK_NOTNULL(&global_core);
Brian Silvermand0575692015-02-21 16:24:02 -0500228 CHECK_NOTNULL(global_core);
Brian Silverman0c715e62015-10-24 16:49:46 -0400229 CHECK_NOTNULL(global_core->mem_struct);
Brian Silvermand0575692015-02-21 16:24:02 -0500230 global_core->mem_struct->time_offset.tv_nsec = 0;
231 global_core->mem_struct->time_offset.tv_sec = 0;
232 global_core->mem_struct->time_offset = (now - Time::Now()).ToTimespec();
233}
234
brians343bc112013-02-10 01:53:46 +0000235} // namespace time
236} // namespace aos