blob: 87c991d1b780773090133b43e10f3b9f42b68030 [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
15namespace aos {
16namespace time {
17
Austin Schuhd78ab542013-03-01 22:22:19 -080018// State required to enable and use mock time.
19namespace {
20// True if mock time is enabled.
21// This does not need to be checked with the mutex held because setting time to
22// be enabled or disabled is atomic, and all future operations are atomic
23// anyways. If there is a race condition setting or clearing whether time is
24// enabled or not, it will still be a race condition if current_mock_time is
25// also set atomically with enabled.
Brian Silverman0308f162016-01-02 13:32:49 -080026::std::atomic<bool> mock_time_enabled{false};
Austin Schuhd78ab542013-03-01 22:22:19 -080027// Mutex to make time reads and writes thread safe.
28Mutex time_mutex;
29// Current time when time is mocked.
30Time current_mock_time(0, 0);
31
32// TODO(aschuh): This doesn't include SleepFor and SleepUntil.
33// TODO(aschuh): Create a clock source object and change the default?
34// That would let me create a MockTime clock source.
Brian Silvermanb407c672014-04-09 11:58:37 -070035
36Time NowImpl(clockid_t clock) {
37 timespec temp;
38 if (clock_gettime(clock, &temp) != 0) {
Brian Silverman01be0002014-05-10 15:44:38 -070039 PLOG(FATAL, "clock_gettime(%jd, %p) failed",
40 static_cast<uintmax_t>(clock), &temp);
Brian Silvermanb407c672014-04-09 11:58:37 -070041 }
Brian Silvermand0575692015-02-21 16:24:02 -050042
Brian Silverman0c715e62015-10-24 16:49:46 -040043 const timespec offset = (&global_core == nullptr || global_core == nullptr ||
44 global_core->mem_struct == nullptr)
Brian Silvermand0575692015-02-21 16:24:02 -050045 ? timespec{0, 0}
46 : global_core->mem_struct->time_offset;
47 return Time(temp) + Time(offset);
brians343bc112013-02-10 01:53:46 +000048}
Austin Schuhd78ab542013-03-01 22:22:19 -080049
Brian Silvermanb407c672014-04-09 11:58:37 -070050} // namespace
51
Brian Silvermandcaa3f72015-11-29 05:32:08 +000052const int32_t Time::kNSecInSec;
53const int32_t Time::kNSecInMSec;
54const int32_t Time::kNSecInUSec;
55const int32_t Time::kMSecInSec;
56const int32_t Time::kUSecInSec;
57
58const Time Time::kZero{0, 0};
59
Brian Silverman6659bc32013-10-16 10:31:32 -070060void Time::EnableMockTime(const Time &now) {
Austin Schuhd78ab542013-03-01 22:22:19 -080061 MutexLocker time_mutex_locker(&time_mutex);
Brian Silvermanb407c672014-04-09 11:58:37 -070062 mock_time_enabled = true;
Austin Schuhd78ab542013-03-01 22:22:19 -080063 current_mock_time = now;
64}
65
Brian Silvermanb407c672014-04-09 11:58:37 -070066void Time::UpdateMockTime() {
67 SetMockTime(NowImpl(kDefaultClock));
68}
69
Austin Schuhd78ab542013-03-01 22:22:19 -080070void Time::DisableMockTime() {
71 MutexLocker time_mutex_locker(&time_mutex);
72 mock_time_enabled = false;
73}
74
Brian Silverman6659bc32013-10-16 10:31:32 -070075void Time::SetMockTime(const Time &now) {
Austin Schuhd78ab542013-03-01 22:22:19 -080076 MutexLocker time_mutex_locker(&time_mutex);
Brian Silvermanb407c672014-04-09 11:58:37 -070077 if (__builtin_expect(!mock_time_enabled, 0)) {
Austin Schuhd78ab542013-03-01 22:22:19 -080078 LOG(FATAL, "Tried to set mock time and mock time is not enabled\n");
79 }
80 current_mock_time = now;
81}
82
Brian Silverman6659bc32013-10-16 10:31:32 -070083void Time::IncrementMockTime(const Time &amount) {
84 static ::aos::Mutex mutex;
85 ::aos::MutexLocker sync(&mutex);
86 SetMockTime(Now() + amount);
87}
88
Austin Schuhd78ab542013-03-01 22:22:19 -080089Time Time::Now(clockid_t clock) {
Brian Silvermanb407c672014-04-09 11:58:37 -070090 {
Brian Silverman0308f162016-01-02 13:32:49 -080091 if (mock_time_enabled.load(::std::memory_order_relaxed)) {
92 MutexLocker time_mutex_locker(&time_mutex);
Brian Silvermanb407c672014-04-09 11:58:37 -070093 return current_mock_time;
Austin Schuhd78ab542013-03-01 22:22:19 -080094 }
Austin Schuhd78ab542013-03-01 22:22:19 -080095 }
Brian Silvermanb407c672014-04-09 11:58:37 -070096 return NowImpl(clock);
Austin Schuhd78ab542013-03-01 22:22:19 -080097}
98
Brian Silverman52aeeac2013-08-28 16:20:53 -070099void Time::CheckImpl(int32_t nsec) {
brians343bc112013-02-10 01:53:46 +0000100 static_assert(aos::shm_ok<Time>::value,
101 "it should be able to go through shared memory");
Brian Silverman52aeeac2013-08-28 16:20:53 -0700102 if (nsec >= kNSecInSec || nsec < 0) {
103 LOG(FATAL, "0 <= nsec(%" PRId32 ") < %" PRId32 " isn't true.\n",
104 nsec, kNSecInSec);
105 }
brians343bc112013-02-10 01:53:46 +0000106}
107
108Time &Time::operator+=(const Time &rhs) {
109 sec_ += rhs.sec_;
110 nsec_ += rhs.nsec_;
111 if (nsec_ >= kNSecInSec) {
112 nsec_ -= kNSecInSec;
113 sec_ += 1;
114 }
115 return *this;
116}
117const Time Time::operator+(const Time &rhs) const {
118 return Time(*this) += rhs;
119}
120Time &Time::operator-=(const Time &rhs) {
121 sec_ -= rhs.sec_;
122 nsec_ -= rhs.nsec_;
123 if (nsec_ < 0) {
124 nsec_ += kNSecInSec;
125 sec_ -= 1;
126 }
127 return *this;
128}
129const Time Time::operator-(const Time &rhs) const {
130 return Time(*this) -= rhs;
131}
132Time &Time::operator*=(int32_t rhs) {
133 const int64_t temp = static_cast<int64_t>(nsec_) *
134 static_cast<int64_t>(rhs);
135 sec_ *= rhs; // better not overflow, or the result is just too big
136 nsec_ = temp % kNSecInSec;
137 sec_ += (temp - nsec_) / kNSecInSec;
Brian Silverman6659bc32013-10-16 10:31:32 -0700138 if (nsec_ < 0) {
139 nsec_ += kNSecInSec;
140 sec_ -= 1;
141 }
brians343bc112013-02-10 01:53:46 +0000142 return *this;
143}
144const Time Time::operator*(int32_t rhs) const {
145 return Time(*this) *= rhs;
146}
147Time &Time::operator/=(int32_t rhs) {
148 nsec_ = (sec_ % rhs) * (kNSecInSec / rhs) + nsec_ / rhs;
149 sec_ /= rhs;
Brian Silverman6659bc32013-10-16 10:31:32 -0700150 if (nsec_ < 0) {
151 nsec_ += kNSecInSec;
152 sec_ -= 1;
153 }
brians343bc112013-02-10 01:53:46 +0000154 return *this;
155}
156const Time Time::operator/(int32_t rhs) const {
157 return Time(*this) /= rhs;
158}
Brian Silverman2e0dcfd2013-03-30 22:44:40 -0700159double Time::operator/(const Time &rhs) const {
160 return ToSeconds() / rhs.ToSeconds();
161}
brians343bc112013-02-10 01:53:46 +0000162Time &Time::operator%=(int32_t rhs) {
163 nsec_ = ToNSec() % rhs;
Brian Silverman0534df62014-05-26 21:19:15 -0700164 const int wraps = nsec_ / ((rhs / kNSecInSec + 1) * kNSecInSec);
165 sec_ = wraps + rhs / kNSecInSec;
brians343bc112013-02-10 01:53:46 +0000166 nsec_ -= kNSecInSec * wraps;
Brian Silverman6659bc32013-10-16 10:31:32 -0700167 if (nsec_ < 0) {
168 nsec_ += kNSecInSec;
169 sec_ -= 1;
170 }
brians343bc112013-02-10 01:53:46 +0000171 return *this;
172}
173const Time Time::operator%(int32_t rhs) const {
174 return Time(*this) %= rhs;
175}
176
Brian Silverman0079a9d2013-10-24 15:57:35 -0700177const Time Time::operator-() const {
178 return Time(-sec_ - 1, kNSecInSec - nsec_);
179}
180
brians343bc112013-02-10 01:53:46 +0000181bool Time::operator==(const Time &rhs) const {
182 return sec_ == rhs.sec_ && nsec_ == rhs.nsec_;
183}
184bool Time::operator!=(const Time &rhs) const {
185 return !(*this == rhs);
186}
187bool Time::operator<(const Time &rhs) const {
188 return sec_ < rhs.sec_ || (sec_ == rhs.sec_ && nsec_ < rhs.nsec_);
189}
190bool Time::operator>(const Time &rhs) const {
191 return sec_ > rhs.sec_ || (sec_ == rhs.sec_ && nsec_ > rhs.nsec_);
192}
193bool Time::operator<=(const Time &rhs) const {
194 return sec_ < rhs.sec_ || (sec_ == rhs.sec_ && nsec_ <= rhs.nsec_);
195}
196bool Time::operator>=(const Time &rhs) const {
197 return sec_ > rhs.sec_ || (sec_ == rhs.sec_ && nsec_ >= rhs.nsec_);
198}
199
200bool Time::IsWithin(const Time &other, int64_t amount) const {
201 const int64_t temp = ToNSec() - other.ToNSec();
202 return temp <= amount && temp >= -amount;
203}
204
205std::ostream &operator<<(std::ostream &os, const Time& time) {
206 return os << "Time{" << time.sec_ << "s, " << time.nsec_ << "ns}";
207}
208
209void SleepFor(const Time &time, clockid_t clock) {
brians343bc112013-02-10 01:53:46 +0000210 timespec converted(time.ToTimespec()), remaining;
211 int failure = EINTR;
212 do {
213 // This checks whether the last time through the loop actually failed or got
214 // interrupted.
215 if (failure != EINTR) {
Brian Silverman01be0002014-05-10 15:44:38 -0700216 PELOG(FATAL, failure, "clock_nanosleep(%jd, 0, %p, %p) failed",
217 static_cast<intmax_t>(clock), &converted, &remaining);
brians343bc112013-02-10 01:53:46 +0000218 }
219 failure = clock_nanosleep(clock, 0, &converted, &remaining);
220 memcpy(&converted, &remaining, sizeof(converted));
221 } while (failure != 0);
brians343bc112013-02-10 01:53:46 +0000222}
223
224void SleepUntil(const Time &time, clockid_t clock) {
brians343bc112013-02-10 01:53:46 +0000225 timespec converted(time.ToTimespec());
226 int failure;
227 while ((failure = clock_nanosleep(clock, TIMER_ABSTIME,
228 &converted, NULL)) != 0) {
229 if (failure != EINTR) {
Brian Silverman01be0002014-05-10 15:44:38 -0700230 PELOG(FATAL, failure, "clock_nanosleep(%jd, TIMER_ABSTIME, %p, NULL)"
231 " failed", static_cast<intmax_t>(clock), &converted);
brians343bc112013-02-10 01:53:46 +0000232 }
233 }
brians343bc112013-02-10 01:53:46 +0000234}
235
Brian Silvermand0575692015-02-21 16:24:02 -0500236void OffsetToNow(const Time &now) {
Brian Silverman0c715e62015-10-24 16:49:46 -0400237 CHECK_NOTNULL(&global_core);
Brian Silvermand0575692015-02-21 16:24:02 -0500238 CHECK_NOTNULL(global_core);
Brian Silverman0c715e62015-10-24 16:49:46 -0400239 CHECK_NOTNULL(global_core->mem_struct);
Brian Silvermand0575692015-02-21 16:24:02 -0500240 global_core->mem_struct->time_offset.tv_nsec = 0;
241 global_core->mem_struct->time_offset.tv_sec = 0;
242 global_core->mem_struct->time_offset = (now - Time::Now()).ToTimespec();
243}
244
brians343bc112013-02-10 01:53:46 +0000245} // namespace time
246} // namespace aos