blob: 7318afb5f906a7a2e7f72b21a8ffe78448dadc8b [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
41 const timespec offset = (global_core == nullptr)
42 ? timespec{0, 0}
43 : global_core->mem_struct->time_offset;
44 return Time(temp) + Time(offset);
brians343bc112013-02-10 01:53:46 +000045}
Austin Schuhd78ab542013-03-01 22:22:19 -080046
Brian Silvermanb407c672014-04-09 11:58:37 -070047} // namespace
48
Brian Silverman6659bc32013-10-16 10:31:32 -070049void Time::EnableMockTime(const Time &now) {
Austin Schuhd78ab542013-03-01 22:22:19 -080050 MutexLocker time_mutex_locker(&time_mutex);
Brian Silvermanb407c672014-04-09 11:58:37 -070051 mock_time_enabled = true;
Austin Schuhd78ab542013-03-01 22:22:19 -080052 current_mock_time = now;
53}
54
Brian Silvermanb407c672014-04-09 11:58:37 -070055void Time::UpdateMockTime() {
56 SetMockTime(NowImpl(kDefaultClock));
57}
58
Austin Schuhd78ab542013-03-01 22:22:19 -080059void Time::DisableMockTime() {
60 MutexLocker time_mutex_locker(&time_mutex);
61 mock_time_enabled = false;
62}
63
Brian Silverman6659bc32013-10-16 10:31:32 -070064void Time::SetMockTime(const Time &now) {
Austin Schuhd78ab542013-03-01 22:22:19 -080065 MutexLocker time_mutex_locker(&time_mutex);
Brian Silvermanb407c672014-04-09 11:58:37 -070066 if (__builtin_expect(!mock_time_enabled, 0)) {
Austin Schuhd78ab542013-03-01 22:22:19 -080067 LOG(FATAL, "Tried to set mock time and mock time is not enabled\n");
68 }
69 current_mock_time = now;
70}
71
Brian Silverman6659bc32013-10-16 10:31:32 -070072void Time::IncrementMockTime(const Time &amount) {
73 static ::aos::Mutex mutex;
74 ::aos::MutexLocker sync(&mutex);
75 SetMockTime(Now() + amount);
76}
77
Austin Schuhd78ab542013-03-01 22:22:19 -080078Time Time::Now(clockid_t clock) {
Brian Silvermanb407c672014-04-09 11:58:37 -070079 {
Austin Schuhd78ab542013-03-01 22:22:19 -080080 MutexLocker time_mutex_locker(&time_mutex);
Brian Silvermanb407c672014-04-09 11:58:37 -070081 if (mock_time_enabled) {
82 return current_mock_time;
Austin Schuhd78ab542013-03-01 22:22:19 -080083 }
Austin Schuhd78ab542013-03-01 22:22:19 -080084 }
Brian Silvermanb407c672014-04-09 11:58:37 -070085 return NowImpl(clock);
Austin Schuhd78ab542013-03-01 22:22:19 -080086}
87
Brian Silverman52aeeac2013-08-28 16:20:53 -070088void Time::CheckImpl(int32_t nsec) {
brians343bc112013-02-10 01:53:46 +000089 static_assert(aos::shm_ok<Time>::value,
90 "it should be able to go through shared memory");
Brian Silverman52aeeac2013-08-28 16:20:53 -070091 if (nsec >= kNSecInSec || nsec < 0) {
92 LOG(FATAL, "0 <= nsec(%" PRId32 ") < %" PRId32 " isn't true.\n",
93 nsec, kNSecInSec);
94 }
brians343bc112013-02-10 01:53:46 +000095}
96
97Time &Time::operator+=(const Time &rhs) {
98 sec_ += rhs.sec_;
99 nsec_ += rhs.nsec_;
100 if (nsec_ >= kNSecInSec) {
101 nsec_ -= kNSecInSec;
102 sec_ += 1;
103 }
104 return *this;
105}
106const Time Time::operator+(const Time &rhs) const {
107 return Time(*this) += rhs;
108}
109Time &Time::operator-=(const Time &rhs) {
110 sec_ -= rhs.sec_;
111 nsec_ -= rhs.nsec_;
112 if (nsec_ < 0) {
113 nsec_ += kNSecInSec;
114 sec_ -= 1;
115 }
116 return *this;
117}
118const Time Time::operator-(const Time &rhs) const {
119 return Time(*this) -= rhs;
120}
121Time &Time::operator*=(int32_t rhs) {
122 const int64_t temp = static_cast<int64_t>(nsec_) *
123 static_cast<int64_t>(rhs);
124 sec_ *= rhs; // better not overflow, or the result is just too big
125 nsec_ = temp % kNSecInSec;
126 sec_ += (temp - nsec_) / kNSecInSec;
Brian Silverman6659bc32013-10-16 10:31:32 -0700127 if (nsec_ < 0) {
128 nsec_ += kNSecInSec;
129 sec_ -= 1;
130 }
brians343bc112013-02-10 01:53:46 +0000131 return *this;
132}
133const Time Time::operator*(int32_t rhs) const {
134 return Time(*this) *= rhs;
135}
136Time &Time::operator/=(int32_t rhs) {
137 nsec_ = (sec_ % rhs) * (kNSecInSec / rhs) + nsec_ / rhs;
138 sec_ /= rhs;
Brian Silverman6659bc32013-10-16 10:31:32 -0700139 if (nsec_ < 0) {
140 nsec_ += kNSecInSec;
141 sec_ -= 1;
142 }
brians343bc112013-02-10 01:53:46 +0000143 return *this;
144}
145const Time Time::operator/(int32_t rhs) const {
146 return Time(*this) /= rhs;
147}
Brian Silverman2e0dcfd2013-03-30 22:44:40 -0700148double Time::operator/(const Time &rhs) const {
149 return ToSeconds() / rhs.ToSeconds();
150}
brians343bc112013-02-10 01:53:46 +0000151Time &Time::operator%=(int32_t rhs) {
152 nsec_ = ToNSec() % rhs;
Brian Silverman0534df62014-05-26 21:19:15 -0700153 const int wraps = nsec_ / ((rhs / kNSecInSec + 1) * kNSecInSec);
154 sec_ = wraps + rhs / kNSecInSec;
brians343bc112013-02-10 01:53:46 +0000155 nsec_ -= kNSecInSec * wraps;
Brian Silverman6659bc32013-10-16 10:31:32 -0700156 if (nsec_ < 0) {
157 nsec_ += kNSecInSec;
158 sec_ -= 1;
159 }
brians343bc112013-02-10 01:53:46 +0000160 return *this;
161}
162const Time Time::operator%(int32_t rhs) const {
163 return Time(*this) %= rhs;
164}
165
Brian Silverman0079a9d2013-10-24 15:57:35 -0700166const Time Time::operator-() const {
167 return Time(-sec_ - 1, kNSecInSec - nsec_);
168}
169
brians343bc112013-02-10 01:53:46 +0000170bool Time::operator==(const Time &rhs) const {
171 return sec_ == rhs.sec_ && nsec_ == rhs.nsec_;
172}
173bool Time::operator!=(const Time &rhs) const {
174 return !(*this == rhs);
175}
176bool Time::operator<(const Time &rhs) const {
177 return sec_ < rhs.sec_ || (sec_ == rhs.sec_ && nsec_ < rhs.nsec_);
178}
179bool Time::operator>(const Time &rhs) const {
180 return sec_ > rhs.sec_ || (sec_ == rhs.sec_ && nsec_ > rhs.nsec_);
181}
182bool Time::operator<=(const Time &rhs) const {
183 return sec_ < rhs.sec_ || (sec_ == rhs.sec_ && nsec_ <= rhs.nsec_);
184}
185bool Time::operator>=(const Time &rhs) const {
186 return sec_ > rhs.sec_ || (sec_ == rhs.sec_ && nsec_ >= rhs.nsec_);
187}
188
189bool Time::IsWithin(const Time &other, int64_t amount) const {
190 const int64_t temp = ToNSec() - other.ToNSec();
191 return temp <= amount && temp >= -amount;
192}
193
194std::ostream &operator<<(std::ostream &os, const Time& time) {
195 return os << "Time{" << time.sec_ << "s, " << time.nsec_ << "ns}";
196}
197
198void SleepFor(const Time &time, clockid_t clock) {
brians343bc112013-02-10 01:53:46 +0000199 timespec converted(time.ToTimespec()), remaining;
200 int failure = EINTR;
201 do {
202 // This checks whether the last time through the loop actually failed or got
203 // interrupted.
204 if (failure != EINTR) {
Brian Silverman01be0002014-05-10 15:44:38 -0700205 PELOG(FATAL, failure, "clock_nanosleep(%jd, 0, %p, %p) failed",
206 static_cast<intmax_t>(clock), &converted, &remaining);
brians343bc112013-02-10 01:53:46 +0000207 }
208 failure = clock_nanosleep(clock, 0, &converted, &remaining);
209 memcpy(&converted, &remaining, sizeof(converted));
210 } while (failure != 0);
brians343bc112013-02-10 01:53:46 +0000211}
212
213void SleepUntil(const Time &time, clockid_t clock) {
brians343bc112013-02-10 01:53:46 +0000214 timespec converted(time.ToTimespec());
215 int failure;
216 while ((failure = clock_nanosleep(clock, TIMER_ABSTIME,
217 &converted, NULL)) != 0) {
218 if (failure != EINTR) {
Brian Silverman01be0002014-05-10 15:44:38 -0700219 PELOG(FATAL, failure, "clock_nanosleep(%jd, TIMER_ABSTIME, %p, NULL)"
220 " failed", static_cast<intmax_t>(clock), &converted);
brians343bc112013-02-10 01:53:46 +0000221 }
222 }
brians343bc112013-02-10 01:53:46 +0000223}
224
Brian Silvermand0575692015-02-21 16:24:02 -0500225void OffsetToNow(const Time &now) {
226 CHECK_NOTNULL(global_core);
227 global_core->mem_struct->time_offset.tv_nsec = 0;
228 global_core->mem_struct->time_offset.tv_sec = 0;
229 global_core->mem_struct->time_offset = (now - Time::Now()).ToTimespec();
230}
231
brians343bc112013-02-10 01:53:46 +0000232} // namespace time
233} // namespace aos