blob: 1c8681102a70adb7e4eaa9a5947b9ec7e49831c1 [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>
brians343bc112013-02-10 01:53:46 +00004
5#include "aos/common/logging/logging.h"
Brian4a424a22014-04-02 11:52:45 -07006#include <inttypes.h>
Austin Schuhd78ab542013-03-01 22:22:19 -08007#include "aos/common/mutex.h"
brians343bc112013-02-10 01:53:46 +00008
9namespace aos {
10namespace time {
11
Austin Schuhd78ab542013-03-01 22:22:19 -080012// State required to enable and use mock time.
13namespace {
14// True if mock time is enabled.
15// This does not need to be checked with the mutex held because setting time to
16// be enabled or disabled is atomic, and all future operations are atomic
17// anyways. If there is a race condition setting or clearing whether time is
18// enabled or not, it will still be a race condition if current_mock_time is
19// also set atomically with enabled.
20bool mock_time_enabled = false;
21// Mutex to make time reads and writes thread safe.
22Mutex time_mutex;
23// Current time when time is mocked.
24Time current_mock_time(0, 0);
25
26// TODO(aschuh): This doesn't include SleepFor and SleepUntil.
27// TODO(aschuh): Create a clock source object and change the default?
28// That would let me create a MockTime clock source.
Brian Silvermanb407c672014-04-09 11:58:37 -070029
30Time NowImpl(clockid_t clock) {
31 timespec temp;
32 if (clock_gettime(clock, &temp) != 0) {
Brian Silverman01be0002014-05-10 15:44:38 -070033 PLOG(FATAL, "clock_gettime(%jd, %p) failed",
34 static_cast<uintmax_t>(clock), &temp);
Brian Silvermanb407c672014-04-09 11:58:37 -070035 }
36 return Time(temp);
brians343bc112013-02-10 01:53:46 +000037}
Austin Schuhd78ab542013-03-01 22:22:19 -080038
Brian Silvermanb407c672014-04-09 11:58:37 -070039} // namespace
40
Brian Silverman6659bc32013-10-16 10:31:32 -070041void Time::EnableMockTime(const Time &now) {
Austin Schuhd78ab542013-03-01 22:22:19 -080042 MutexLocker time_mutex_locker(&time_mutex);
Brian Silvermanb407c672014-04-09 11:58:37 -070043 mock_time_enabled = true;
Austin Schuhd78ab542013-03-01 22:22:19 -080044 current_mock_time = now;
45}
46
Brian Silvermanb407c672014-04-09 11:58:37 -070047void Time::UpdateMockTime() {
48 SetMockTime(NowImpl(kDefaultClock));
49}
50
Austin Schuhd78ab542013-03-01 22:22:19 -080051void Time::DisableMockTime() {
52 MutexLocker time_mutex_locker(&time_mutex);
53 mock_time_enabled = false;
54}
55
Brian Silverman6659bc32013-10-16 10:31:32 -070056void Time::SetMockTime(const Time &now) {
Austin Schuhd78ab542013-03-01 22:22:19 -080057 MutexLocker time_mutex_locker(&time_mutex);
Brian Silvermanb407c672014-04-09 11:58:37 -070058 if (__builtin_expect(!mock_time_enabled, 0)) {
Austin Schuhd78ab542013-03-01 22:22:19 -080059 LOG(FATAL, "Tried to set mock time and mock time is not enabled\n");
60 }
61 current_mock_time = now;
62}
63
Brian Silverman6659bc32013-10-16 10:31:32 -070064void Time::IncrementMockTime(const Time &amount) {
65 static ::aos::Mutex mutex;
66 ::aos::MutexLocker sync(&mutex);
67 SetMockTime(Now() + amount);
68}
69
Austin Schuhd78ab542013-03-01 22:22:19 -080070Time Time::Now(clockid_t clock) {
Brian Silvermanb407c672014-04-09 11:58:37 -070071 {
Austin Schuhd78ab542013-03-01 22:22:19 -080072 MutexLocker time_mutex_locker(&time_mutex);
Brian Silvermanb407c672014-04-09 11:58:37 -070073 if (mock_time_enabled) {
74 return current_mock_time;
Austin Schuhd78ab542013-03-01 22:22:19 -080075 }
Austin Schuhd78ab542013-03-01 22:22:19 -080076 }
Brian Silvermanb407c672014-04-09 11:58:37 -070077 return NowImpl(clock);
Austin Schuhd78ab542013-03-01 22:22:19 -080078}
79
Brian Silverman52aeeac2013-08-28 16:20:53 -070080void Time::CheckImpl(int32_t nsec) {
brians343bc112013-02-10 01:53:46 +000081 static_assert(aos::shm_ok<Time>::value,
82 "it should be able to go through shared memory");
Brian Silverman52aeeac2013-08-28 16:20:53 -070083 if (nsec >= kNSecInSec || nsec < 0) {
84 LOG(FATAL, "0 <= nsec(%" PRId32 ") < %" PRId32 " isn't true.\n",
85 nsec, kNSecInSec);
86 }
brians343bc112013-02-10 01:53:46 +000087}
88
89Time &Time::operator+=(const Time &rhs) {
90 sec_ += rhs.sec_;
91 nsec_ += rhs.nsec_;
92 if (nsec_ >= kNSecInSec) {
93 nsec_ -= kNSecInSec;
94 sec_ += 1;
95 }
96 return *this;
97}
98const Time Time::operator+(const Time &rhs) const {
99 return Time(*this) += rhs;
100}
101Time &Time::operator-=(const Time &rhs) {
102 sec_ -= rhs.sec_;
103 nsec_ -= rhs.nsec_;
104 if (nsec_ < 0) {
105 nsec_ += kNSecInSec;
106 sec_ -= 1;
107 }
108 return *this;
109}
110const Time Time::operator-(const Time &rhs) const {
111 return Time(*this) -= rhs;
112}
113Time &Time::operator*=(int32_t rhs) {
114 const int64_t temp = static_cast<int64_t>(nsec_) *
115 static_cast<int64_t>(rhs);
116 sec_ *= rhs; // better not overflow, or the result is just too big
117 nsec_ = temp % kNSecInSec;
118 sec_ += (temp - nsec_) / kNSecInSec;
Brian Silverman6659bc32013-10-16 10:31:32 -0700119 if (nsec_ < 0) {
120 nsec_ += kNSecInSec;
121 sec_ -= 1;
122 }
brians343bc112013-02-10 01:53:46 +0000123 return *this;
124}
125const Time Time::operator*(int32_t rhs) const {
126 return Time(*this) *= rhs;
127}
128Time &Time::operator/=(int32_t rhs) {
129 nsec_ = (sec_ % rhs) * (kNSecInSec / rhs) + nsec_ / rhs;
130 sec_ /= rhs;
Brian Silverman6659bc32013-10-16 10:31:32 -0700131 if (nsec_ < 0) {
132 nsec_ += kNSecInSec;
133 sec_ -= 1;
134 }
brians343bc112013-02-10 01:53:46 +0000135 return *this;
136}
137const Time Time::operator/(int32_t rhs) const {
138 return Time(*this) /= rhs;
139}
Brian Silverman2e0dcfd2013-03-30 22:44:40 -0700140double Time::operator/(const Time &rhs) const {
141 return ToSeconds() / rhs.ToSeconds();
142}
brians343bc112013-02-10 01:53:46 +0000143Time &Time::operator%=(int32_t rhs) {
144 nsec_ = ToNSec() % rhs;
Brian Silverman0534df62014-05-26 21:19:15 -0700145 const int wraps = nsec_ / ((rhs / kNSecInSec + 1) * kNSecInSec);
146 sec_ = wraps + rhs / kNSecInSec;
brians343bc112013-02-10 01:53:46 +0000147 nsec_ -= kNSecInSec * wraps;
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}
157
Brian Silverman0079a9d2013-10-24 15:57:35 -0700158const Time Time::operator-() const {
159 return Time(-sec_ - 1, kNSecInSec - nsec_);
160}
161
brians343bc112013-02-10 01:53:46 +0000162bool Time::operator==(const Time &rhs) const {
163 return sec_ == rhs.sec_ && nsec_ == rhs.nsec_;
164}
165bool Time::operator!=(const Time &rhs) const {
166 return !(*this == rhs);
167}
168bool Time::operator<(const Time &rhs) const {
169 return sec_ < rhs.sec_ || (sec_ == rhs.sec_ && nsec_ < rhs.nsec_);
170}
171bool Time::operator>(const Time &rhs) const {
172 return sec_ > rhs.sec_ || (sec_ == rhs.sec_ && nsec_ > rhs.nsec_);
173}
174bool Time::operator<=(const Time &rhs) const {
175 return sec_ < rhs.sec_ || (sec_ == rhs.sec_ && nsec_ <= rhs.nsec_);
176}
177bool Time::operator>=(const Time &rhs) const {
178 return sec_ > rhs.sec_ || (sec_ == rhs.sec_ && nsec_ >= rhs.nsec_);
179}
180
181bool Time::IsWithin(const Time &other, int64_t amount) const {
182 const int64_t temp = ToNSec() - other.ToNSec();
183 return temp <= amount && temp >= -amount;
184}
185
186std::ostream &operator<<(std::ostream &os, const Time& time) {
187 return os << "Time{" << time.sec_ << "s, " << time.nsec_ << "ns}";
188}
189
190void SleepFor(const Time &time, clockid_t clock) {
brians343bc112013-02-10 01:53:46 +0000191 timespec converted(time.ToTimespec()), remaining;
192 int failure = EINTR;
193 do {
194 // This checks whether the last time through the loop actually failed or got
195 // interrupted.
196 if (failure != EINTR) {
Brian Silverman01be0002014-05-10 15:44:38 -0700197 PELOG(FATAL, failure, "clock_nanosleep(%jd, 0, %p, %p) failed",
198 static_cast<intmax_t>(clock), &converted, &remaining);
brians343bc112013-02-10 01:53:46 +0000199 }
200 failure = clock_nanosleep(clock, 0, &converted, &remaining);
201 memcpy(&converted, &remaining, sizeof(converted));
202 } while (failure != 0);
brians343bc112013-02-10 01:53:46 +0000203}
204
205void SleepUntil(const Time &time, clockid_t clock) {
brians343bc112013-02-10 01:53:46 +0000206 timespec converted(time.ToTimespec());
207 int failure;
208 while ((failure = clock_nanosleep(clock, TIMER_ABSTIME,
209 &converted, NULL)) != 0) {
210 if (failure != EINTR) {
Brian Silverman01be0002014-05-10 15:44:38 -0700211 PELOG(FATAL, failure, "clock_nanosleep(%jd, TIMER_ABSTIME, %p, NULL)"
212 " failed", static_cast<intmax_t>(clock), &converted);
brians343bc112013-02-10 01:53:46 +0000213 }
214 }
brians343bc112013-02-10 01:53:46 +0000215}
216
217} // namespace time
218} // namespace aos