blob: 00c936d9d6e76812c5504e329e9362a734204b69 [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"
11
12namespace aos {
13namespace time {
14
15Time Time::Now(clockid_t clock) {
16 timespec temp;
17 if (clock_gettime(clock, &temp) != 0) {
18 // TODO(aschuh): There needs to be a pluggable low level logging interface
19 // so we can break this dependency loop. This also would help during
20 // startup.
21 LOG(FATAL, "clock_gettime(%jd, %p) failed with %d: %s\n",
22 static_cast<uintmax_t>(clock), &temp, errno, strerror(errno));
23 }
24 return Time(temp);
25}
26void Time::Check() {
27 if (nsec_ >= kNSecInSec || nsec_ < 0) {
28 LOG(FATAL, "0 <= nsec_(%"PRId32") < %"PRId32" isn't true.\n",
29 nsec_, kNSecInSec);
30 }
31 static_assert(aos::shm_ok<Time>::value,
32 "it should be able to go through shared memory");
33}
34
35Time &Time::operator+=(const Time &rhs) {
36 sec_ += rhs.sec_;
37 nsec_ += rhs.nsec_;
38 if (nsec_ >= kNSecInSec) {
39 nsec_ -= kNSecInSec;
40 sec_ += 1;
41 }
42 return *this;
43}
44const Time Time::operator+(const Time &rhs) const {
45 return Time(*this) += rhs;
46}
47Time &Time::operator-=(const Time &rhs) {
48 sec_ -= rhs.sec_;
49 nsec_ -= rhs.nsec_;
50 if (nsec_ < 0) {
51 nsec_ += kNSecInSec;
52 sec_ -= 1;
53 }
54 return *this;
55}
56const Time Time::operator-(const Time &rhs) const {
57 return Time(*this) -= rhs;
58}
59Time &Time::operator*=(int32_t rhs) {
60 const int64_t temp = static_cast<int64_t>(nsec_) *
61 static_cast<int64_t>(rhs);
62 sec_ *= rhs; // better not overflow, or the result is just too big
63 nsec_ = temp % kNSecInSec;
64 sec_ += (temp - nsec_) / kNSecInSec;
65 return *this;
66}
67const Time Time::operator*(int32_t rhs) const {
68 return Time(*this) *= rhs;
69}
70Time &Time::operator/=(int32_t rhs) {
71 nsec_ = (sec_ % rhs) * (kNSecInSec / rhs) + nsec_ / rhs;
72 sec_ /= rhs;
73 return *this;
74}
75const Time Time::operator/(int32_t rhs) const {
76 return Time(*this) /= rhs;
77}
78Time &Time::operator%=(int32_t rhs) {
79 nsec_ = ToNSec() % rhs;
80 const int wraps = nsec_ / kNSecInSec;
81 sec_ = wraps;
82 nsec_ -= kNSecInSec * wraps;
83 return *this;
84}
85const Time Time::operator%(int32_t rhs) const {
86 return Time(*this) %= rhs;
87}
88
89bool Time::operator==(const Time &rhs) const {
90 return sec_ == rhs.sec_ && nsec_ == rhs.nsec_;
91}
92bool Time::operator!=(const Time &rhs) const {
93 return !(*this == rhs);
94}
95bool Time::operator<(const Time &rhs) const {
96 return sec_ < rhs.sec_ || (sec_ == rhs.sec_ && nsec_ < rhs.nsec_);
97}
98bool Time::operator>(const Time &rhs) const {
99 return sec_ > rhs.sec_ || (sec_ == rhs.sec_ && nsec_ > rhs.nsec_);
100}
101bool Time::operator<=(const Time &rhs) const {
102 return sec_ < rhs.sec_ || (sec_ == rhs.sec_ && nsec_ <= rhs.nsec_);
103}
104bool Time::operator>=(const Time &rhs) const {
105 return sec_ > rhs.sec_ || (sec_ == rhs.sec_ && nsec_ >= rhs.nsec_);
106}
107
108bool Time::IsWithin(const Time &other, int64_t amount) const {
109 const int64_t temp = ToNSec() - other.ToNSec();
110 return temp <= amount && temp >= -amount;
111}
112
113std::ostream &operator<<(std::ostream &os, const Time& time) {
114 return os << "Time{" << time.sec_ << "s, " << time.nsec_ << "ns}";
115}
116
117void SleepFor(const Time &time, clockid_t clock) {
118#ifdef __VXWORKS__
119 SleepUntil(Time::Now(clock) + time, clock);
120#else
121 timespec converted(time.ToTimespec()), remaining;
122 int failure = EINTR;
123 do {
124 // This checks whether the last time through the loop actually failed or got
125 // interrupted.
126 if (failure != EINTR) {
127 LOG(FATAL, "clock_nanosleep(%jd, 0, %p, %p) returned %d: %s\n",
128 static_cast<intmax_t>(clock), &converted, &remaining,
129 failure, strerror(failure));
130 }
131 failure = clock_nanosleep(clock, 0, &converted, &remaining);
132 memcpy(&converted, &remaining, sizeof(converted));
133 } while (failure != 0);
134#endif
135}
136
137void SleepUntil(const Time &time, clockid_t clock) {
138#ifdef __VXWORKS__
139 if (clock != CLOCK_REALTIME) {
140 LOG(FATAL, "vxworks only supports CLOCK_REALTIME\n");
141 }
142 // Vxworks nanosleep is definitely broken (fails horribly at doing remaining
143 // right), and I don't really want to know how else it's broken, so I'm using
144 // taskDelay instead because that's simpler.
145 // The +1 is because sleep functions are supposed to sleep for at least the
146 // requested amount, so we have to round up to the next clock tick.
147 while (taskDelay((time - Time::Now(clock)).ToTicks() + 1) != 0) {
148 if (errno != EINTR) {
149 LOG(FATAL, "taskDelay(some ticks) failed with %d: %s\n",
150 errno, strerror(errno));
151 }
152 }
153#else
154 timespec converted(time.ToTimespec());
155 int failure;
156 while ((failure = clock_nanosleep(clock, TIMER_ABSTIME,
157 &converted, NULL)) != 0) {
158 if (failure != EINTR) {
159 LOG(FATAL, "clock_nanosleep(%jd, TIMER_ABSTIME, %p, NULL)"
160 " returned %d: %s\n", static_cast<intmax_t>(clock), &converted,
161 failure, strerror(failure));
162 }
163 }
164#endif
165}
166
167} // namespace time
168} // namespace aos