Removed Common
Change-Id: I01ea8f07220375c2ad9bc0092281d4f27c642303
diff --git a/aos/time/BUILD b/aos/time/BUILD
new file mode 100644
index 0000000..cbeeb5b
--- /dev/null
+++ b/aos/time/BUILD
@@ -0,0 +1,33 @@
+package(default_visibility = ["//visibility:public"])
+
+cc_library(
+ name = "time",
+ srcs = [
+ "time.cc",
+ ],
+ hdrs = [
+ "time.h",
+ ],
+ compatible_with = [
+ "//tools:armhf-debian",
+ ],
+ deps = [
+ "//aos:macros",
+ "//aos/mutex:mutex",
+ "//aos/logging",
+ "//aos/linux_code/ipc_lib:shared_mem",
+ ],
+)
+
+cc_test(
+ name = "time_test",
+ srcs = [
+ "time_test.cc",
+ ],
+ deps = [
+ ":time",
+ "//aos/logging",
+ "//aos/util:death_test_log_implementation",
+ "//aos/testing:googletest",
+ ],
+)
diff --git a/aos/time/time.cc b/aos/time/time.cc
new file mode 100644
index 0000000..a72b121
--- /dev/null
+++ b/aos/time/time.cc
@@ -0,0 +1,145 @@
+#include "aos/time/time.h"
+
+#include <inttypes.h>
+#include <string.h>
+
+#include <atomic>
+#include <chrono>
+
+// We only use global_core from here, which is weak, so we don't really have a
+// dependency on it.
+#include "aos/linux_code/ipc_lib/shared_mem.h"
+
+#include "aos/logging/logging.h"
+#include "aos/mutex/mutex.h"
+
+namespace chrono = ::std::chrono;
+
+namespace std {
+namespace this_thread {
+template <>
+void sleep_until(const ::aos::monotonic_clock::time_point &end_time) {
+ struct timespec end_time_timespec;
+ ::std::chrono::seconds sec =
+ ::std::chrono::duration_cast<::std::chrono::seconds>(
+ end_time.time_since_epoch());
+ ::std::chrono::nanoseconds nsec =
+ ::std::chrono::duration_cast<::std::chrono::nanoseconds>(
+ end_time.time_since_epoch() - sec);
+ end_time_timespec.tv_sec = sec.count();
+ end_time_timespec.tv_nsec = nsec.count();
+ int returnval;
+ do {
+ returnval = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME,
+ &end_time_timespec, nullptr);
+ if (returnval != EINTR && returnval != 0) {
+ PLOG(FATAL, "clock_nanosleep(%jd, TIMER_ABSTIME, %p, nullptr) failed",
+ static_cast<uintmax_t>(CLOCK_MONOTONIC), &end_time_timespec);
+ }
+ } while (returnval != 0);
+}
+
+} // namespace this_thread
+} // namespace std
+
+
+namespace aos {
+namespace time {
+
+// State required to enable and use mock time.
+namespace {
+// True if mock time is enabled.
+// This does not need to be checked with the mutex held because setting time to
+// be enabled or disabled is atomic, and all future operations are atomic
+// anyways. If there is a race condition setting or clearing whether time is
+// enabled or not, it will still be a race condition if current_mock_time is
+// also set atomically with enabled.
+::std::atomic<bool> mock_time_enabled{false};
+// Mutex to make time reads and writes thread safe.
+Mutex time_mutex;
+// Current time when time is mocked.
+monotonic_clock::time_point current_mock_time = monotonic_clock::epoch();
+
+} // namespace
+
+void EnableMockTime(monotonic_clock::time_point now) {
+ MutexLocker time_mutex_locker(&time_mutex);
+ mock_time_enabled = true;
+ current_mock_time = now;
+}
+
+void UpdateMockTime() { SetMockTime(monotonic_clock::now()); }
+
+void DisableMockTime() {
+ MutexLocker time_mutex_locker(&time_mutex);
+ mock_time_enabled = false;
+}
+
+void SetMockTime(monotonic_clock::time_point now) {
+ MutexLocker time_mutex_locker(&time_mutex);
+ if (__builtin_expect(!mock_time_enabled, 0)) {
+ LOG(FATAL, "Tried to set mock time and mock time is not enabled\n");
+ }
+ current_mock_time = now;
+}
+
+void IncrementMockTime(monotonic_clock::duration amount) {
+ static ::aos::Mutex mutex;
+ ::aos::MutexLocker sync(&mutex);
+ SetMockTime(monotonic_clock::now() + amount);
+}
+
+void OffsetToNow(monotonic_clock::time_point now) {
+ CHECK_NOTNULL(&global_core);
+ CHECK_NOTNULL(global_core);
+ CHECK_NOTNULL(global_core->mem_struct);
+ const auto offset = now - monotonic_clock::now();
+ global_core->mem_struct->time_offset =
+ chrono::duration_cast<chrono::nanoseconds>(offset).count();
+}
+
+struct timespec to_timespec(
+ const ::aos::monotonic_clock::duration duration) {
+ struct timespec time_timespec;
+ ::std::chrono::seconds sec =
+ ::std::chrono::duration_cast<::std::chrono::seconds>(duration);
+ ::std::chrono::nanoseconds nsec =
+ ::std::chrono::duration_cast<::std::chrono::nanoseconds>(duration - sec);
+ time_timespec.tv_sec = sec.count();
+ time_timespec.tv_nsec = nsec.count();
+ return time_timespec;
+}
+
+struct timespec to_timespec(
+ const ::aos::monotonic_clock::time_point time) {
+ return to_timespec(time.time_since_epoch());
+}
+} // namespace time
+
+constexpr monotonic_clock::time_point monotonic_clock::min_time;
+
+monotonic_clock::time_point monotonic_clock::now() noexcept {
+ {
+ if (time::mock_time_enabled.load(::std::memory_order_relaxed)) {
+ MutexLocker time_mutex_locker(&time::time_mutex);
+ return time::current_mock_time;
+ }
+ }
+
+ struct timespec current_time;
+ if (clock_gettime(CLOCK_MONOTONIC, ¤t_time) != 0) {
+ PLOG(FATAL, "clock_gettime(%jd, %p) failed",
+ static_cast<uintmax_t>(CLOCK_MONOTONIC), ¤t_time);
+ }
+ const chrono::nanoseconds offset =
+ (&global_core == nullptr || global_core == nullptr ||
+ global_core->mem_struct == nullptr)
+ ? chrono::nanoseconds(0)
+ : chrono::nanoseconds(global_core->mem_struct->time_offset);
+
+ return time_point(::std::chrono::seconds(current_time.tv_sec) +
+ ::std::chrono::nanoseconds(current_time.tv_nsec)) + offset;
+}
+
+
+} // namespace aos
diff --git a/aos/time/time.h b/aos/time/time.h
new file mode 100644
index 0000000..9bef741
--- /dev/null
+++ b/aos/time/time.h
@@ -0,0 +1,102 @@
+#ifndef AOS_TIME_H_
+#define AOS_TIME_H_
+
+#include <stdint.h>
+#include <time.h>
+#include <sys/time.h>
+#include <stdint.h>
+
+#include <type_traits>
+#include <chrono>
+#include <thread>
+#include <ostream>
+
+#include "aos/type_traits/type_traits.h"
+#include "aos/macros.h"
+
+namespace aos {
+
+class monotonic_clock {
+ public:
+ typedef ::std::chrono::nanoseconds::rep rep;
+ typedef ::std::chrono::nanoseconds::period period;
+ typedef ::std::chrono::nanoseconds duration;
+ typedef ::std::chrono::time_point<monotonic_clock> time_point;
+
+ static monotonic_clock::time_point now() noexcept;
+ static constexpr bool is_steady = true;
+
+ // Returns the epoch (0).
+ static constexpr monotonic_clock::time_point epoch() {
+ return time_point(zero());
+ }
+
+ static constexpr monotonic_clock::duration zero() { return duration(0); }
+
+ static constexpr time_point min_time{
+ time_point(duration(::std::numeric_limits<duration::rep>::min()))};
+};
+
+namespace time {
+
+// Enables returning the mock time value for Now instead of checking the system
+// clock.
+void EnableMockTime(monotonic_clock::time_point now);
+// Calls SetMockTime with the current actual time.
+void UpdateMockTime();
+// Sets now when time is being mocked.
+void SetMockTime(monotonic_clock::time_point now);
+// Convenience function to just increment the mock time by a certain amount in
+// a thread safe way.
+void IncrementMockTime(monotonic_clock::duration amount);
+// Disables mocking time.
+void DisableMockTime();
+
+// Sets the global offset for all times so monotonic_clock::now() will return
+// now.
+// There is no synchronization here, so this is only safe when only a single
+// task is running.
+// This is only allowed when the shared memory core infrastructure has been
+// initialized in this process.
+void OffsetToNow(const monotonic_clock::time_point now);
+
+// Construct a time representing the period of hertz.
+constexpr ::std::chrono::nanoseconds FromRate(int hertz) {
+ return ::std::chrono::duration_cast<::std::chrono::nanoseconds>(
+ ::std::chrono::seconds(1)) /
+ hertz;
+}
+
+// RAII class that freezes monotonic_clock::now() (to avoid making large numbers
+// of syscalls to find the real time).
+class TimeFreezer {
+ public:
+ TimeFreezer() { EnableMockTime(monotonic_clock::now()); }
+ ~TimeFreezer() { DisableMockTime(); }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TimeFreezer);
+};
+
+// Converts a monotonic_clock::duration into a timespec object.
+struct timespec to_timespec(::aos::monotonic_clock::duration duration);
+
+// Converts a monotonic_clock::time_point into a timespec object as time since
+// epoch.
+struct timespec to_timespec(::aos::monotonic_clock::time_point time);
+
+} // namespace time
+} // namespace aos
+
+namespace std {
+namespace this_thread {
+// Template specialization for monotonic_clock, since we can use clock_nanosleep
+// with TIMER_ABSTIME and get very precise absolute time sleeps.
+template <>
+void sleep_until(const ::aos::monotonic_clock::time_point &end_time);
+
+} // namespace this_thread
+} // namespace std
+
+
+#endif // AOS_TIME_H_
diff --git a/aos/time/time_test.cc b/aos/time/time_test.cc
new file mode 100644
index 0000000..7b6c1a5
--- /dev/null
+++ b/aos/time/time_test.cc
@@ -0,0 +1,54 @@
+#include "aos/time/time.h"
+
+#include <thread>
+
+#include "gtest/gtest.h"
+
+#include "aos/macros.h"
+#include "aos/util/death_test_log_implementation.h"
+
+namespace aos {
+namespace time {
+namespace testing {
+
+TEST(TimeTest, FromRate) {
+ EXPECT_EQ(::std::chrono::milliseconds(10), FromRate(100));
+}
+
+// Test the monotonic_clock and sleep_until functions.
+TEST(TimeTest, MonotonicClockSleepAndNow) {
+ monotonic_clock::time_point start = monotonic_clock::now();
+ const auto kSleepTime = ::std::chrono::milliseconds(500);
+ ::std::this_thread::sleep_until(start + kSleepTime);
+ monotonic_clock::time_point end = monotonic_clock::now();
+ EXPECT_GE(end - start, kSleepTime);
+ EXPECT_LT(end - start, kSleepTime + ::std::chrono::milliseconds(200));
+}
+
+// Test to_timespec for a duration.
+TEST(TimeTest, DurationToTimespec) {
+ struct timespec pos_time = to_timespec(::std::chrono::milliseconds(56262));
+ EXPECT_EQ(pos_time.tv_sec, 56);
+ EXPECT_EQ(pos_time.tv_nsec, 262000000);
+
+ struct timespec neg_time = to_timespec(::std::chrono::milliseconds(-56262));
+ EXPECT_EQ(neg_time.tv_sec, -56);
+ EXPECT_EQ(neg_time.tv_nsec, -262000000);
+}
+
+// Test to_timespec for a time_point.
+TEST(TimeTest, TimePointToTimespec) {
+ struct timespec pos_time = to_timespec(::aos::monotonic_clock::epoch() +
+ ::std::chrono::seconds(1432423));
+ EXPECT_EQ(pos_time.tv_sec, 1432423);
+ EXPECT_EQ(pos_time.tv_nsec, 0);
+
+ struct timespec neg_time = to_timespec(::aos::monotonic_clock::epoch() -
+ ::std::chrono::seconds(1432423));
+ EXPECT_EQ(neg_time.tv_sec, -1432423);
+ EXPECT_EQ(neg_time.tv_nsec, 0);
+}
+
+} // namespace testing
+} // namespace time
+} // namespace aos