blob: 019cb52130bbd8d04c2088cf2dd2c43f4d6129ae [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 Silvermandcaa3f72015-11-29 05:32:08 +000050const int32_t Time::kNSecInSec;
51const int32_t Time::kNSecInMSec;
52const int32_t Time::kNSecInUSec;
53const int32_t Time::kMSecInSec;
54const int32_t Time::kUSecInSec;
55
56const Time Time::kZero{0, 0};
57
Brian Silverman6659bc32013-10-16 10:31:32 -070058void Time::EnableMockTime(const Time &now) {
Austin Schuhd78ab542013-03-01 22:22:19 -080059 MutexLocker time_mutex_locker(&time_mutex);
Brian Silvermanb407c672014-04-09 11:58:37 -070060 mock_time_enabled = true;
Austin Schuhd78ab542013-03-01 22:22:19 -080061 current_mock_time = now;
62}
63
Brian Silvermanb407c672014-04-09 11:58:37 -070064void Time::UpdateMockTime() {
65 SetMockTime(NowImpl(kDefaultClock));
66}
67
Austin Schuhd78ab542013-03-01 22:22:19 -080068void Time::DisableMockTime() {
69 MutexLocker time_mutex_locker(&time_mutex);
70 mock_time_enabled = false;
71}
72
Brian Silverman6659bc32013-10-16 10:31:32 -070073void Time::SetMockTime(const Time &now) {
Austin Schuhd78ab542013-03-01 22:22:19 -080074 MutexLocker time_mutex_locker(&time_mutex);
Brian Silvermanb407c672014-04-09 11:58:37 -070075 if (__builtin_expect(!mock_time_enabled, 0)) {
Austin Schuhd78ab542013-03-01 22:22:19 -080076 LOG(FATAL, "Tried to set mock time and mock time is not enabled\n");
77 }
78 current_mock_time = now;
79}
80
Brian Silverman6659bc32013-10-16 10:31:32 -070081void Time::IncrementMockTime(const Time &amount) {
82 static ::aos::Mutex mutex;
83 ::aos::MutexLocker sync(&mutex);
84 SetMockTime(Now() + amount);
85}
86
Austin Schuhd78ab542013-03-01 22:22:19 -080087Time Time::Now(clockid_t clock) {
Brian Silvermanb407c672014-04-09 11:58:37 -070088 {
Austin Schuhd78ab542013-03-01 22:22:19 -080089 MutexLocker time_mutex_locker(&time_mutex);
Brian Silvermanb407c672014-04-09 11:58:37 -070090 if (mock_time_enabled) {
91 return current_mock_time;
Austin Schuhd78ab542013-03-01 22:22:19 -080092 }
Austin Schuhd78ab542013-03-01 22:22:19 -080093 }
Brian Silvermanb407c672014-04-09 11:58:37 -070094 return NowImpl(clock);
Austin Schuhd78ab542013-03-01 22:22:19 -080095}
96
Brian Silverman52aeeac2013-08-28 16:20:53 -070097void Time::CheckImpl(int32_t nsec) {
brians343bc112013-02-10 01:53:46 +000098 static_assert(aos::shm_ok<Time>::value,
99 "it should be able to go through shared memory");
Brian Silverman52aeeac2013-08-28 16:20:53 -0700100 if (nsec >= kNSecInSec || nsec < 0) {
101 LOG(FATAL, "0 <= nsec(%" PRId32 ") < %" PRId32 " isn't true.\n",
102 nsec, kNSecInSec);
103 }
brians343bc112013-02-10 01:53:46 +0000104}
105
106Time &Time::operator+=(const Time &rhs) {
107 sec_ += rhs.sec_;
108 nsec_ += rhs.nsec_;
109 if (nsec_ >= kNSecInSec) {
110 nsec_ -= kNSecInSec;
111 sec_ += 1;
112 }
113 return *this;
114}
115const Time Time::operator+(const Time &rhs) const {
116 return Time(*this) += rhs;
117}
118Time &Time::operator-=(const Time &rhs) {
119 sec_ -= rhs.sec_;
120 nsec_ -= rhs.nsec_;
121 if (nsec_ < 0) {
122 nsec_ += kNSecInSec;
123 sec_ -= 1;
124 }
125 return *this;
126}
127const Time Time::operator-(const Time &rhs) const {
128 return Time(*this) -= rhs;
129}
130Time &Time::operator*=(int32_t rhs) {
131 const int64_t temp = static_cast<int64_t>(nsec_) *
132 static_cast<int64_t>(rhs);
133 sec_ *= rhs; // better not overflow, or the result is just too big
134 nsec_ = temp % kNSecInSec;
135 sec_ += (temp - nsec_) / kNSecInSec;
Brian Silverman6659bc32013-10-16 10:31:32 -0700136 if (nsec_ < 0) {
137 nsec_ += kNSecInSec;
138 sec_ -= 1;
139 }
brians343bc112013-02-10 01:53:46 +0000140 return *this;
141}
142const Time Time::operator*(int32_t rhs) const {
143 return Time(*this) *= rhs;
144}
145Time &Time::operator/=(int32_t rhs) {
146 nsec_ = (sec_ % rhs) * (kNSecInSec / rhs) + nsec_ / rhs;
147 sec_ /= rhs;
Brian Silverman6659bc32013-10-16 10:31:32 -0700148 if (nsec_ < 0) {
149 nsec_ += kNSecInSec;
150 sec_ -= 1;
151 }
brians343bc112013-02-10 01:53:46 +0000152 return *this;
153}
154const Time Time::operator/(int32_t rhs) const {
155 return Time(*this) /= rhs;
156}
Brian Silverman2e0dcfd2013-03-30 22:44:40 -0700157double Time::operator/(const Time &rhs) const {
158 return ToSeconds() / rhs.ToSeconds();
159}
brians343bc112013-02-10 01:53:46 +0000160Time &Time::operator%=(int32_t rhs) {
161 nsec_ = ToNSec() % rhs;
Brian Silverman0534df62014-05-26 21:19:15 -0700162 const int wraps = nsec_ / ((rhs / kNSecInSec + 1) * kNSecInSec);
163 sec_ = wraps + rhs / kNSecInSec;
brians343bc112013-02-10 01:53:46 +0000164 nsec_ -= kNSecInSec * wraps;
Brian Silverman6659bc32013-10-16 10:31:32 -0700165 if (nsec_ < 0) {
166 nsec_ += kNSecInSec;
167 sec_ -= 1;
168 }
brians343bc112013-02-10 01:53:46 +0000169 return *this;
170}
171const Time Time::operator%(int32_t rhs) const {
172 return Time(*this) %= rhs;
173}
174
Brian Silverman0079a9d2013-10-24 15:57:35 -0700175const Time Time::operator-() const {
176 return Time(-sec_ - 1, kNSecInSec - nsec_);
177}
178
brians343bc112013-02-10 01:53:46 +0000179bool Time::operator==(const Time &rhs) const {
180 return sec_ == rhs.sec_ && nsec_ == rhs.nsec_;
181}
182bool Time::operator!=(const Time &rhs) const {
183 return !(*this == rhs);
184}
185bool Time::operator<(const Time &rhs) const {
186 return sec_ < rhs.sec_ || (sec_ == rhs.sec_ && nsec_ < rhs.nsec_);
187}
188bool Time::operator>(const Time &rhs) const {
189 return sec_ > rhs.sec_ || (sec_ == rhs.sec_ && nsec_ > rhs.nsec_);
190}
191bool Time::operator<=(const Time &rhs) const {
192 return sec_ < rhs.sec_ || (sec_ == rhs.sec_ && nsec_ <= rhs.nsec_);
193}
194bool Time::operator>=(const Time &rhs) const {
195 return sec_ > rhs.sec_ || (sec_ == rhs.sec_ && nsec_ >= rhs.nsec_);
196}
197
198bool Time::IsWithin(const Time &other, int64_t amount) const {
199 const int64_t temp = ToNSec() - other.ToNSec();
200 return temp <= amount && temp >= -amount;
201}
202
203std::ostream &operator<<(std::ostream &os, const Time& time) {
204 return os << "Time{" << time.sec_ << "s, " << time.nsec_ << "ns}";
205}
206
207void SleepFor(const Time &time, clockid_t clock) {
brians343bc112013-02-10 01:53:46 +0000208 timespec converted(time.ToTimespec()), remaining;
209 int failure = EINTR;
210 do {
211 // This checks whether the last time through the loop actually failed or got
212 // interrupted.
213 if (failure != EINTR) {
Brian Silverman01be0002014-05-10 15:44:38 -0700214 PELOG(FATAL, failure, "clock_nanosleep(%jd, 0, %p, %p) failed",
215 static_cast<intmax_t>(clock), &converted, &remaining);
brians343bc112013-02-10 01:53:46 +0000216 }
217 failure = clock_nanosleep(clock, 0, &converted, &remaining);
218 memcpy(&converted, &remaining, sizeof(converted));
219 } while (failure != 0);
brians343bc112013-02-10 01:53:46 +0000220}
221
222void SleepUntil(const Time &time, clockid_t clock) {
brians343bc112013-02-10 01:53:46 +0000223 timespec converted(time.ToTimespec());
224 int failure;
225 while ((failure = clock_nanosleep(clock, TIMER_ABSTIME,
226 &converted, NULL)) != 0) {
227 if (failure != EINTR) {
Brian Silverman01be0002014-05-10 15:44:38 -0700228 PELOG(FATAL, failure, "clock_nanosleep(%jd, TIMER_ABSTIME, %p, NULL)"
229 " failed", static_cast<intmax_t>(clock), &converted);
brians343bc112013-02-10 01:53:46 +0000230 }
231 }
brians343bc112013-02-10 01:53:46 +0000232}
233
Brian Silvermand0575692015-02-21 16:24:02 -0500234void OffsetToNow(const Time &now) {
Brian Silverman0c715e62015-10-24 16:49:46 -0400235 CHECK_NOTNULL(&global_core);
Brian Silvermand0575692015-02-21 16:24:02 -0500236 CHECK_NOTNULL(global_core);
Brian Silverman0c715e62015-10-24 16:49:46 -0400237 CHECK_NOTNULL(global_core->mem_struct);
Brian Silvermand0575692015-02-21 16:24:02 -0500238 global_core->mem_struct->time_offset.tv_nsec = 0;
239 global_core->mem_struct->time_offset.tv_sec = 0;
240 global_core->mem_struct->time_offset = (now - Time::Now()).ToTimespec();
241}
242
brians343bc112013-02-10 01:53:46 +0000243} // namespace time
244} // namespace aos