blob: 88802e34de3457759cd4371ca9de379e4f2b9b2e [file] [log] [blame]
brians343bc112013-02-10 01:53:46 +00001#include "aos/common/time.h"
2
3#ifdef __VXWORKS__
4#include <taskLib.h>
5#endif
Brian Silvermanf665d692013-02-17 22:11:39 -08006#include <errno.h>
7#include <string.h>
brians343bc112013-02-10 01:53:46 +00008
9#include "aos/common/logging/logging.h"
Brian4a424a22014-04-02 11:52:45 -070010#include <inttypes.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) {
37 LOG(FATAL, "clock_gettime(%jd, %p) failed with %d: %s\n",
38 static_cast<uintmax_t>(clock), &temp, errno, strerror(errno));
39 }
40 return Time(temp);
brians343bc112013-02-10 01:53:46 +000041}
Austin Schuhd78ab542013-03-01 22:22:19 -080042
Brian Silvermanb407c672014-04-09 11:58:37 -070043} // namespace
44
Brian Silverman6659bc32013-10-16 10:31:32 -070045void Time::EnableMockTime(const Time &now) {
Austin Schuhd78ab542013-03-01 22:22:19 -080046 MutexLocker time_mutex_locker(&time_mutex);
Brian Silvermanb407c672014-04-09 11:58:37 -070047 mock_time_enabled = true;
Austin Schuhd78ab542013-03-01 22:22:19 -080048 current_mock_time = now;
49}
50
Brian Silvermanb407c672014-04-09 11:58:37 -070051void Time::UpdateMockTime() {
52 SetMockTime(NowImpl(kDefaultClock));
53}
54
Austin Schuhd78ab542013-03-01 22:22:19 -080055void Time::DisableMockTime() {
56 MutexLocker time_mutex_locker(&time_mutex);
57 mock_time_enabled = false;
58}
59
Brian Silverman6659bc32013-10-16 10:31:32 -070060void Time::SetMockTime(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 if (__builtin_expect(!mock_time_enabled, 0)) {
Austin Schuhd78ab542013-03-01 22:22:19 -080063 LOG(FATAL, "Tried to set mock time and mock time is not enabled\n");
64 }
65 current_mock_time = now;
66}
67
Brian Silverman6659bc32013-10-16 10:31:32 -070068void Time::IncrementMockTime(const Time &amount) {
69 static ::aos::Mutex mutex;
70 ::aos::MutexLocker sync(&mutex);
71 SetMockTime(Now() + amount);
72}
73
Austin Schuhd78ab542013-03-01 22:22:19 -080074Time Time::Now(clockid_t clock) {
Brian Silvermanb407c672014-04-09 11:58:37 -070075 {
Austin Schuhd78ab542013-03-01 22:22:19 -080076 MutexLocker time_mutex_locker(&time_mutex);
Brian Silvermanb407c672014-04-09 11:58:37 -070077 if (mock_time_enabled) {
78 return current_mock_time;
Austin Schuhd78ab542013-03-01 22:22:19 -080079 }
Austin Schuhd78ab542013-03-01 22:22:19 -080080 }
Brian Silvermanb407c672014-04-09 11:58:37 -070081 return NowImpl(clock);
Austin Schuhd78ab542013-03-01 22:22:19 -080082}
83
Brian Silverman52aeeac2013-08-28 16:20:53 -070084void Time::CheckImpl(int32_t nsec) {
brians343bc112013-02-10 01:53:46 +000085 static_assert(aos::shm_ok<Time>::value,
86 "it should be able to go through shared memory");
Brian Silverman52aeeac2013-08-28 16:20:53 -070087 if (nsec >= kNSecInSec || nsec < 0) {
88 LOG(FATAL, "0 <= nsec(%" PRId32 ") < %" PRId32 " isn't true.\n",
89 nsec, kNSecInSec);
90 }
brians343bc112013-02-10 01:53:46 +000091}
92
93Time &Time::operator+=(const Time &rhs) {
94 sec_ += rhs.sec_;
95 nsec_ += rhs.nsec_;
96 if (nsec_ >= kNSecInSec) {
97 nsec_ -= kNSecInSec;
98 sec_ += 1;
99 }
100 return *this;
101}
102const Time Time::operator+(const Time &rhs) const {
103 return Time(*this) += rhs;
104}
105Time &Time::operator-=(const Time &rhs) {
106 sec_ -= rhs.sec_;
107 nsec_ -= rhs.nsec_;
108 if (nsec_ < 0) {
109 nsec_ += kNSecInSec;
110 sec_ -= 1;
111 }
112 return *this;
113}
114const Time Time::operator-(const Time &rhs) const {
115 return Time(*this) -= rhs;
116}
117Time &Time::operator*=(int32_t rhs) {
118 const int64_t temp = static_cast<int64_t>(nsec_) *
119 static_cast<int64_t>(rhs);
120 sec_ *= rhs; // better not overflow, or the result is just too big
121 nsec_ = temp % kNSecInSec;
122 sec_ += (temp - nsec_) / kNSecInSec;
Brian Silverman6659bc32013-10-16 10:31:32 -0700123 if (nsec_ < 0) {
124 nsec_ += kNSecInSec;
125 sec_ -= 1;
126 }
brians343bc112013-02-10 01:53:46 +0000127 return *this;
128}
129const Time Time::operator*(int32_t rhs) const {
130 return Time(*this) *= rhs;
131}
132Time &Time::operator/=(int32_t rhs) {
133 nsec_ = (sec_ % rhs) * (kNSecInSec / rhs) + nsec_ / rhs;
134 sec_ /= rhs;
Brian Silverman6659bc32013-10-16 10:31:32 -0700135 if (nsec_ < 0) {
136 nsec_ += kNSecInSec;
137 sec_ -= 1;
138 }
brians343bc112013-02-10 01:53:46 +0000139 return *this;
140}
141const Time Time::operator/(int32_t rhs) const {
142 return Time(*this) /= rhs;
143}
Brian Silverman2e0dcfd2013-03-30 22:44:40 -0700144double Time::operator/(const Time &rhs) const {
145 return ToSeconds() / rhs.ToSeconds();
146}
brians343bc112013-02-10 01:53:46 +0000147Time &Time::operator%=(int32_t rhs) {
148 nsec_ = ToNSec() % rhs;
149 const int wraps = nsec_ / kNSecInSec;
150 sec_ = wraps;
151 nsec_ -= kNSecInSec * wraps;
Brian Silverman6659bc32013-10-16 10:31:32 -0700152 if (nsec_ < 0) {
153 nsec_ += kNSecInSec;
154 sec_ -= 1;
155 }
brians343bc112013-02-10 01:53:46 +0000156 return *this;
157}
158const Time Time::operator%(int32_t rhs) const {
159 return Time(*this) %= rhs;
160}
161
Brian Silverman0079a9d2013-10-24 15:57:35 -0700162const Time Time::operator-() const {
163 return Time(-sec_ - 1, kNSecInSec - nsec_);
164}
165
brians343bc112013-02-10 01:53:46 +0000166bool Time::operator==(const Time &rhs) const {
167 return sec_ == rhs.sec_ && nsec_ == rhs.nsec_;
168}
169bool Time::operator!=(const Time &rhs) const {
170 return !(*this == rhs);
171}
172bool Time::operator<(const Time &rhs) const {
173 return sec_ < rhs.sec_ || (sec_ == rhs.sec_ && nsec_ < rhs.nsec_);
174}
175bool Time::operator>(const Time &rhs) const {
176 return sec_ > rhs.sec_ || (sec_ == rhs.sec_ && nsec_ > rhs.nsec_);
177}
178bool Time::operator<=(const Time &rhs) const {
179 return sec_ < rhs.sec_ || (sec_ == rhs.sec_ && nsec_ <= rhs.nsec_);
180}
181bool Time::operator>=(const Time &rhs) const {
182 return sec_ > rhs.sec_ || (sec_ == rhs.sec_ && nsec_ >= rhs.nsec_);
183}
184
185bool Time::IsWithin(const Time &other, int64_t amount) const {
186 const int64_t temp = ToNSec() - other.ToNSec();
187 return temp <= amount && temp >= -amount;
188}
189
190std::ostream &operator<<(std::ostream &os, const Time& time) {
191 return os << "Time{" << time.sec_ << "s, " << time.nsec_ << "ns}";
192}
193
194void SleepFor(const Time &time, clockid_t clock) {
195#ifdef __VXWORKS__
196 SleepUntil(Time::Now(clock) + time, clock);
197#else
198 timespec converted(time.ToTimespec()), remaining;
199 int failure = EINTR;
200 do {
201 // This checks whether the last time through the loop actually failed or got
202 // interrupted.
203 if (failure != EINTR) {
204 LOG(FATAL, "clock_nanosleep(%jd, 0, %p, %p) returned %d: %s\n",
205 static_cast<intmax_t>(clock), &converted, &remaining,
206 failure, strerror(failure));
207 }
208 failure = clock_nanosleep(clock, 0, &converted, &remaining);
209 memcpy(&converted, &remaining, sizeof(converted));
210 } while (failure != 0);
211#endif
212}
213
214void SleepUntil(const Time &time, clockid_t clock) {
215#ifdef __VXWORKS__
216 if (clock != CLOCK_REALTIME) {
217 LOG(FATAL, "vxworks only supports CLOCK_REALTIME\n");
218 }
219 // Vxworks nanosleep is definitely broken (fails horribly at doing remaining
220 // right), and I don't really want to know how else it's broken, so I'm using
221 // taskDelay instead because that's simpler.
222 // The +1 is because sleep functions are supposed to sleep for at least the
223 // requested amount, so we have to round up to the next clock tick.
224 while (taskDelay((time - Time::Now(clock)).ToTicks() + 1) != 0) {
225 if (errno != EINTR) {
226 LOG(FATAL, "taskDelay(some ticks) failed with %d: %s\n",
227 errno, strerror(errno));
228 }
229 }
230#else
231 timespec converted(time.ToTimespec());
232 int failure;
233 while ((failure = clock_nanosleep(clock, TIMER_ABSTIME,
234 &converted, NULL)) != 0) {
235 if (failure != EINTR) {
236 LOG(FATAL, "clock_nanosleep(%jd, TIMER_ABSTIME, %p, NULL)"
237 " returned %d: %s\n", static_cast<intmax_t>(clock), &converted,
238 failure, strerror(failure));
239 }
240 }
241#endif
242}
243
244} // namespace time
245} // namespace aos