blob: 28e5cae4d917b8fbde8c43cc56030ef5444c20b8 [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"
10#include "aos/common/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.
brians343bc112013-02-10 01:53:46 +000033}
Austin Schuhd78ab542013-03-01 22:22:19 -080034
Brian Silverman6659bc32013-10-16 10:31:32 -070035void Time::EnableMockTime(const Time &now) {
Austin Schuhd78ab542013-03-01 22:22:19 -080036 mock_time_enabled = true;
37 MutexLocker time_mutex_locker(&time_mutex);
38 current_mock_time = now;
39}
40
41void Time::DisableMockTime() {
42 MutexLocker time_mutex_locker(&time_mutex);
43 mock_time_enabled = false;
44}
45
Brian Silverman6659bc32013-10-16 10:31:32 -070046void Time::SetMockTime(const Time &now) {
Austin Schuhd78ab542013-03-01 22:22:19 -080047 MutexLocker time_mutex_locker(&time_mutex);
48 if (!mock_time_enabled) {
49 LOG(FATAL, "Tried to set mock time and mock time is not enabled\n");
50 }
51 current_mock_time = now;
52}
53
Brian Silverman6659bc32013-10-16 10:31:32 -070054void Time::IncrementMockTime(const Time &amount) {
55 static ::aos::Mutex mutex;
56 ::aos::MutexLocker sync(&mutex);
57 SetMockTime(Now() + amount);
58}
59
Austin Schuhd78ab542013-03-01 22:22:19 -080060Time Time::Now(clockid_t clock) {
61 if (mock_time_enabled) {
62 MutexLocker time_mutex_locker(&time_mutex);
63 return current_mock_time;
64 } else {
65 timespec temp;
66 if (clock_gettime(clock, &temp) != 0) {
Austin Schuhd78ab542013-03-01 22:22:19 -080067 LOG(FATAL, "clock_gettime(%jd, %p) failed with %d: %s\n",
68 static_cast<uintmax_t>(clock), &temp, errno, strerror(errno));
69 }
70 return Time(temp);
71 }
72}
73
Brian Silverman52aeeac2013-08-28 16:20:53 -070074void Time::CheckImpl(int32_t nsec) {
brians343bc112013-02-10 01:53:46 +000075 static_assert(aos::shm_ok<Time>::value,
76 "it should be able to go through shared memory");
Brian Silverman52aeeac2013-08-28 16:20:53 -070077 if (nsec >= kNSecInSec || nsec < 0) {
78 LOG(FATAL, "0 <= nsec(%" PRId32 ") < %" PRId32 " isn't true.\n",
79 nsec, kNSecInSec);
80 }
brians343bc112013-02-10 01:53:46 +000081}
82
83Time &Time::operator+=(const Time &rhs) {
84 sec_ += rhs.sec_;
85 nsec_ += rhs.nsec_;
86 if (nsec_ >= kNSecInSec) {
87 nsec_ -= kNSecInSec;
88 sec_ += 1;
89 }
90 return *this;
91}
92const Time Time::operator+(const Time &rhs) const {
93 return Time(*this) += rhs;
94}
95Time &Time::operator-=(const Time &rhs) {
96 sec_ -= rhs.sec_;
97 nsec_ -= rhs.nsec_;
98 if (nsec_ < 0) {
99 nsec_ += kNSecInSec;
100 sec_ -= 1;
101 }
102 return *this;
103}
104const Time Time::operator-(const Time &rhs) const {
105 return Time(*this) -= rhs;
106}
107Time &Time::operator*=(int32_t rhs) {
108 const int64_t temp = static_cast<int64_t>(nsec_) *
109 static_cast<int64_t>(rhs);
110 sec_ *= rhs; // better not overflow, or the result is just too big
111 nsec_ = temp % kNSecInSec;
112 sec_ += (temp - nsec_) / kNSecInSec;
Brian Silverman6659bc32013-10-16 10:31:32 -0700113 if (nsec_ < 0) {
114 nsec_ += kNSecInSec;
115 sec_ -= 1;
116 }
brians343bc112013-02-10 01:53:46 +0000117 return *this;
118}
119const Time Time::operator*(int32_t rhs) const {
120 return Time(*this) *= rhs;
121}
122Time &Time::operator/=(int32_t rhs) {
123 nsec_ = (sec_ % rhs) * (kNSecInSec / rhs) + nsec_ / rhs;
124 sec_ /= rhs;
Brian Silverman6659bc32013-10-16 10:31:32 -0700125 if (nsec_ < 0) {
126 nsec_ += kNSecInSec;
127 sec_ -= 1;
128 }
brians343bc112013-02-10 01:53:46 +0000129 return *this;
130}
131const Time Time::operator/(int32_t rhs) const {
132 return Time(*this) /= rhs;
133}
Brian Silverman2e0dcfd2013-03-30 22:44:40 -0700134double Time::operator/(const Time &rhs) const {
135 return ToSeconds() / rhs.ToSeconds();
136}
brians343bc112013-02-10 01:53:46 +0000137Time &Time::operator%=(int32_t rhs) {
138 nsec_ = ToNSec() % rhs;
139 const int wraps = nsec_ / kNSecInSec;
140 sec_ = wraps;
141 nsec_ -= kNSecInSec * wraps;
Brian Silverman6659bc32013-10-16 10:31:32 -0700142 if (nsec_ < 0) {
143 nsec_ += kNSecInSec;
144 sec_ -= 1;
145 }
brians343bc112013-02-10 01:53:46 +0000146 return *this;
147}
148const Time Time::operator%(int32_t rhs) const {
149 return Time(*this) %= rhs;
150}
151
152bool Time::operator==(const Time &rhs) const {
153 return sec_ == rhs.sec_ && nsec_ == rhs.nsec_;
154}
155bool Time::operator!=(const Time &rhs) const {
156 return !(*this == rhs);
157}
158bool Time::operator<(const Time &rhs) const {
159 return sec_ < rhs.sec_ || (sec_ == rhs.sec_ && nsec_ < rhs.nsec_);
160}
161bool Time::operator>(const Time &rhs) const {
162 return sec_ > rhs.sec_ || (sec_ == rhs.sec_ && nsec_ > rhs.nsec_);
163}
164bool Time::operator<=(const Time &rhs) const {
165 return sec_ < rhs.sec_ || (sec_ == rhs.sec_ && nsec_ <= rhs.nsec_);
166}
167bool Time::operator>=(const Time &rhs) const {
168 return sec_ > rhs.sec_ || (sec_ == rhs.sec_ && nsec_ >= rhs.nsec_);
169}
170
171bool Time::IsWithin(const Time &other, int64_t amount) const {
172 const int64_t temp = ToNSec() - other.ToNSec();
173 return temp <= amount && temp >= -amount;
174}
175
176std::ostream &operator<<(std::ostream &os, const Time& time) {
177 return os << "Time{" << time.sec_ << "s, " << time.nsec_ << "ns}";
178}
179
180void SleepFor(const Time &time, clockid_t clock) {
181#ifdef __VXWORKS__
182 SleepUntil(Time::Now(clock) + time, clock);
183#else
184 timespec converted(time.ToTimespec()), remaining;
185 int failure = EINTR;
186 do {
187 // This checks whether the last time through the loop actually failed or got
188 // interrupted.
189 if (failure != EINTR) {
190 LOG(FATAL, "clock_nanosleep(%jd, 0, %p, %p) returned %d: %s\n",
191 static_cast<intmax_t>(clock), &converted, &remaining,
192 failure, strerror(failure));
193 }
194 failure = clock_nanosleep(clock, 0, &converted, &remaining);
195 memcpy(&converted, &remaining, sizeof(converted));
196 } while (failure != 0);
197#endif
198}
199
200void SleepUntil(const Time &time, clockid_t clock) {
201#ifdef __VXWORKS__
202 if (clock != CLOCK_REALTIME) {
203 LOG(FATAL, "vxworks only supports CLOCK_REALTIME\n");
204 }
205 // Vxworks nanosleep is definitely broken (fails horribly at doing remaining
206 // right), and I don't really want to know how else it's broken, so I'm using
207 // taskDelay instead because that's simpler.
208 // The +1 is because sleep functions are supposed to sleep for at least the
209 // requested amount, so we have to round up to the next clock tick.
210 while (taskDelay((time - Time::Now(clock)).ToTicks() + 1) != 0) {
211 if (errno != EINTR) {
212 LOG(FATAL, "taskDelay(some ticks) failed with %d: %s\n",
213 errno, strerror(errno));
214 }
215 }
216#else
217 timespec converted(time.ToTimespec());
218 int failure;
219 while ((failure = clock_nanosleep(clock, TIMER_ABSTIME,
220 &converted, NULL)) != 0) {
221 if (failure != EINTR) {
222 LOG(FATAL, "clock_nanosleep(%jd, TIMER_ABSTIME, %p, NULL)"
223 " returned %d: %s\n", static_cast<intmax_t>(clock), &converted,
224 failure, strerror(failure));
225 }
226 }
227#endif
228}
229
230} // namespace time
231} // namespace aos