diff options
-rw-r--r-- | src/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/common/Makefile.am | 4 | ||||
-rw-r--r-- | src/common/ceph_time.cc | 112 | ||||
-rw-r--r-- | src/common/ceph_time.h | 383 | ||||
-rw-r--r-- | src/test/CMakeLists.txt | 19 | ||||
-rw-r--r-- | src/test/Makefile.am | 5 | ||||
-rw-r--r-- | src/test/common/test_time.cc | 145 |
7 files changed, 668 insertions, 1 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 41c6eec3a6c..127e5e5c569 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -217,6 +217,7 @@ set(libcommon_files common/escape.c common/io_priority.cc common/Clock.cc + common/ceph_time.cc common/Throttle.cc common/Timer.cc common/Finisher.cc diff --git a/src/common/Makefile.am b/src/common/Makefile.am index 182295f9e6d..64ce61443bd 100644 --- a/src/common/Makefile.am +++ b/src/common/Makefile.am @@ -14,6 +14,7 @@ libcommon_internal_la_SOURCES = \ common/cmdparse.cc \ common/escape.c \ common/io_priority.cc \ + common/ceph_time.cc \ common/Clock.cc \ common/Throttle.cc \ common/Timer.cc \ @@ -263,7 +264,8 @@ noinst_HEADERS += \ common/valgrind.h \ common/TracepointProvider.h \ common/event_socket.h \ - common/PluginRegistry.h + common/PluginRegistry.h \ + common/ceph_time.h if ENABLE_XIO noinst_HEADERS += \ diff --git a/src/common/ceph_time.cc b/src/common/ceph_time.cc new file mode 100644 index 00000000000..a18a31657f5 --- /dev/null +++ b/src/common/ceph_time.cc @@ -0,0 +1,112 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab +/* + * Ceph - scalable distributed file system + * + * Copyright (C) 2004-2006 Sage Weil <sage@newdream.net> + * + * This is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software + * Foundation. See file COPYING. + * + */ + +// For ceph_timespec +#include "include/types.h" + +#include "ceph_context.h" +#include "ceph_time.h" +#include "config.h" + +namespace ceph { + namespace time_detail { + real_clock::time_point real_clock::now(const CephContext* cct) noexcept { + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + // TODO: After we get the time-literal configuration patch in, + // just add the configured duration. + if (cct) + ts.tv_sec += cct->_conf->clock_offset; + return from_timespec(ts); + } + + void real_clock::to_ceph_timespec(const time_point& t, + struct ceph_timespec& ts) { + ts.tv_sec = to_time_t(t); + ts.tv_nsec = (t.time_since_epoch() % seconds(1)).count(); + } + struct ceph_timespec real_clock::to_ceph_timespec(const time_point& t) { + struct ceph_timespec ts; + to_ceph_timespec(t, ts); + return ts; + } + real_clock::time_point real_clock::from_ceph_timespec( + const struct ceph_timespec& ts) { + return time_point(seconds(ts.tv_sec) + nanoseconds(ts.tv_nsec)); + } + + coarse_real_clock::time_point coarse_real_clock::now( + const CephContext* cct) noexcept { + struct timespec ts; + clock_gettime(CLOCK_REALTIME_COARSE, &ts); + // TODO: After we get the time-literal configuration patch in, + // just add the configured duration. + if (cct) + ts.tv_sec += cct->_conf->clock_offset; + return from_timespec(ts); + } + + void coarse_real_clock::to_ceph_timespec(const time_point& t, + struct ceph_timespec& ts) { + ts.tv_sec = to_time_t(t); + ts.tv_nsec = (t.time_since_epoch() % seconds(1)).count(); + } + struct ceph_timespec coarse_real_clock::to_ceph_timespec( + const time_point& t) { + struct ceph_timespec ts; + to_ceph_timespec(t, ts); + return ts; + } + real_clock::time_point coarse_real_clock::from_ceph_timespec( + const struct ceph_timespec& ts) { + return time_point(seconds(ts.tv_sec) + nanoseconds(ts.tv_nsec)); + } + }; + + using std::chrono::duration_cast; + using std::chrono::seconds; + using std::chrono::microseconds; + + std::ostream& operator<<(std::ostream& m, const mono_time& t) { + return m << std::chrono::duration<double>(t.time_since_epoch()).count() + << "s"; + } + + std::ostream& operator<<(std::ostream& m, const timespan& t) { + return m << std::chrono::duration<double>(t).count() << "s"; + } + + std::ostream& operator<<(std::ostream& m, const real_time& t) { + m.setf(std::ios::right); + char oldfill = m.fill(); + m.fill('0'); + // localtime. this looks like an absolute time. + // aim for http://en.wikipedia.org/wiki/ISO_8601 + struct tm bdt; + time_t tt = ceph::real_clock::to_time_t(t); + localtime_r(&tt, &bdt); + m << std::setw(4) << (bdt.tm_year+1900) // 2007 -> '07' + << '-' << std::setw(2) << (bdt.tm_mon+1) + << '-' << std::setw(2) << bdt.tm_mday + << ' ' + << std::setw(2) << bdt.tm_hour + << ':' << std::setw(2) << bdt.tm_min + << ':' << std::setw(2) << bdt.tm_sec + << "." << std::setw(6) << duration_cast<microseconds>( + t.time_since_epoch() % seconds(1)); + m.fill(oldfill); + m.unsetf(std::ios::right); + return m; + } +}; diff --git a/src/common/ceph_time.h b/src/common/ceph_time.h new file mode 100644 index 00000000000..a634e7fec55 --- /dev/null +++ b/src/common/ceph_time.h @@ -0,0 +1,383 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab +/* + * Ceph - scalable distributed file system + * + * Copyright (C) 2004-2006 Sage Weil <sage@newdream.net> + * + * This is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software + * Foundation. See file COPYING. + * + */ + +#ifndef COMMON_CEPH_TIME_H +#define COMMON_CEPH_TIME_H + +#include <chrono> +#include <ctime> + +#include "include/encoding.h" + +class CephContext; +struct ceph_timespec; + +namespace ceph { + namespace time_detail { + using std::chrono::duration_cast; + using std::chrono::seconds; + using std::chrono::microseconds; + using std::chrono::nanoseconds; + // Currently we use a 64-bit count of nanoseconds. + + // We could, if we wished, use a struct holding a uint64_t count + // of seconds and a uint32_t count of nanoseconds. + + // At least this way we can change it to something else if we + // want. + typedef uint64_t rep; + + // A concrete duration, unsigned. The timespan Ceph thinks in. + typedef std::chrono::duration<rep, std::nano> timespan; + + + // Like the above but signed. + typedef int64_t signed_rep; + + typedef std::chrono::duration<signed_rep, std::nano> signedspan; + + // We define our own clocks so we can have our choice of all time + // sources supported by the operating system. With the standard + // library the resolution and cost are unspecified. (For example, + // the libc++ system_clock class gives only microsecond + // resolution.) + + // One potential issue is that we should accept system_clock + // timepoints in user-facing APIs alongside (or instead of) + // ceph::real_clock times. + class real_clock { + public: + typedef timespan duration; + typedef duration::rep rep; + typedef duration::period period; + // The second template parameter defaults to the clock's duration + // type. + typedef std::chrono::time_point<real_clock> time_point; + static constexpr const bool is_steady = false; + + static time_point now() noexcept { + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + return from_timespec(ts); + } + // We need a version of 'now' that can take a CephContext for + // introducing configurable clock skew. + static time_point now(const CephContext* cct) noexcept; + + // Allow conversion to/from any clock with the same interface as + // std::chrono::system_clock) + template<typename Clock, typename Duration> + static time_point to_system_time_point( + const std::chrono::time_point<Clock, Duration>& t) { + return time_point(seconds(Clock::to_time_t(t)) + + duration_cast<duration>(t.time_since_epoch() % + seconds(1))); + } + template<typename Clock, typename Duration> + static std::chrono::time_point<Clock, Duration> to_system_time_point( + const time_point& t) { + return (Clock::from_time_t(to_time_t(t)) + + duration_cast<Duration>(t.time_since_epoch() % seconds(1))); + } + + static time_t to_time_t(const time_point& t) noexcept { + return duration_cast<seconds>(t.time_since_epoch()).count(); + } + static time_point from_time_t(const time_t& t) noexcept { + return time_point(seconds(t)); + } + + static void to_timespec(const time_point& t, struct timespec& ts) { + ts.tv_sec = to_time_t(t); + ts.tv_nsec = (t.time_since_epoch() % seconds(1)).count(); + } + static struct timespec to_timespec(const time_point& t) { + struct timespec ts; + to_timespec(t, ts); + return ts; + } + static time_point from_timespec(const struct timespec& ts) { + return time_point(seconds(ts.tv_sec) + nanoseconds(ts.tv_nsec)); + } + + static void to_ceph_timespec(const time_point& t, + struct ceph_timespec& ts); + static struct ceph_timespec to_ceph_timespec(const time_point& t); + static time_point from_ceph_timespec(const struct ceph_timespec& ts); + + static void to_timeval(const time_point& t, struct timeval& tv) { + tv.tv_sec = to_time_t(t); + tv.tv_usec = duration_cast<microseconds>(t.time_since_epoch() % + seconds(1)).count(); + } + static struct timeval to_timeval(const time_point& t) { + struct timeval tv; + to_timeval(t, tv); + return tv; + } + static time_point from_timeval(const struct timeval& tv) { + return time_point(seconds(tv.tv_sec) + microseconds(tv.tv_usec)); + } + + static double to_double(const time_point& t) { + return std::chrono::duration<double>(t.time_since_epoch()).count(); + } + static time_point from_double(const double d) { + return time_point(duration_cast<duration>( + std::chrono::duration<double>(d))); + } + }; + + class coarse_real_clock { + public: + typedef timespan duration; + typedef duration::rep rep; + typedef duration::period period; + // The second template parameter defaults to the clock's duration + // type. + typedef std::chrono::time_point<real_clock> time_point; + static constexpr const bool is_steady = false; + + static time_point now() noexcept { + struct timespec ts; + clock_gettime(CLOCK_REALTIME_COARSE, &ts); + return from_timespec(ts); + } + static time_point now(const CephContext* cct) noexcept; + + static time_t to_time_t(const time_point& t) noexcept { + return duration_cast<seconds>(t.time_since_epoch()).count(); + } + static time_point from_time_t(const time_t t) noexcept { + return time_point(seconds(t)); + } + + static void to_timespec(const time_point& t, struct timespec& ts) { + ts.tv_sec = to_time_t(t); + ts.tv_nsec = (t.time_since_epoch() % seconds(1)).count(); + } + static struct timespec to_timespec(const time_point& t) { + struct timespec ts; + to_timespec(t, ts); + return ts; + } + static time_point from_timespec(const struct timespec& ts) { + return time_point(seconds(ts.tv_sec) + nanoseconds(ts.tv_nsec)); + } + + static void to_ceph_timespec(const time_point& t, + struct ceph_timespec& ts); + static struct ceph_timespec to_ceph_timespec(const time_point& t); + static time_point from_ceph_timespec(const struct ceph_timespec& ts); + + static void to_timeval(const time_point& t, struct timeval& tv) { + tv.tv_sec = to_time_t(t); + tv.tv_usec = duration_cast<microseconds>(t.time_since_epoch() % + seconds(1)).count(); + } + static struct timeval to_timeval(const time_point& t) { + struct timeval tv; + to_timeval(t, tv); + return tv; + } + static time_point from_timeval(const struct timeval& tv) { + return time_point(seconds(tv.tv_sec) + microseconds(tv.tv_usec)); + } + + static double to_double(const time_point& t) { + return std::chrono::duration<double>(t.time_since_epoch()).count(); + } + static time_point from_double(const double d) { + return time_point(duration_cast<duration>( + std::chrono::duration<double>(d))); + } + }; + + class mono_clock { + public: + typedef timespan duration; + typedef duration::rep rep; + typedef duration::period period; + typedef std::chrono::time_point<mono_clock> time_point; + static constexpr const bool is_steady = true; + + static time_point now() noexcept { + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return time_point(seconds(ts.tv_sec) + nanoseconds(ts.tv_nsec)); + } + + // A monotonic clock's timepoints are only meaningful to the + // computer on which they were generated. Thus having an + // optional skew is meaningless. + }; + + class coarse_mono_clock { + public: + typedef timespan duration; + typedef duration::rep rep; + typedef duration::period period; + typedef std::chrono::time_point<mono_clock> time_point; + static constexpr const bool is_steady = true; + + static time_point now() noexcept { + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC_COARSE, &ts); + return time_point(seconds(ts.tv_sec) + nanoseconds(ts.tv_nsec)); + } + }; + } // namespace time_detail + + // duration is the concrete time representation for our code in the + // case that we are only interested in durations between now and the + // future. Using it means we don't have to have EVERY function that + // deals with a duration be a template. We can do so for user-facing + // APIs, however. + using time_detail::timespan; + + // Similar to the above but for durations that can specify + // differences between now and a time point in the past. + using time_detail::signedspan; + + // High-resolution real-time clock + using time_detail::real_clock; + + // Low-resolution but preusmably faster real-time clock + using time_detail::coarse_real_clock; + + + // High-resolution monotonic clock + using time_detail::mono_clock; + + // Low-resolution but, I would hope or there's no point, faster + // monotonic clock + using time_detail::coarse_mono_clock; + + // Please note that the coarse clocks are disjoint. You cannot + // subtract a real_clock timepoint from a coarse_real_clock + // timepoint as, from C++'s perspective, they are disjoint types. + + // This is not necessarily bad. If I sample a mono_clock and then a + // coarse_mono_clock, the coarse_mono_clock's time could potentially + // be previous to the mono_clock's time (just due to differing + // resolution) which would be Incorrect. + + // This is not horrible, though, since you can use an idiom like + // mono_clock::timepoint(coarsepoint.time_since_epoch()) to unwrap + // and rewrap if you know what you're doing. + + + // Actual wall-clock times + typedef real_clock::time_point real_time; + + // Monotonic times should never be serialized or communicated + // between machines, since they are incomparable. Thus we also don't + // make any provision for converting between + // std::chrono::steady_clock time and ceph::mono_clock time. + typedef mono_clock::time_point mono_time; + + template<typename Rep1, typename Ratio1, typename Rep2, typename Ratio2> + auto floor(const std::chrono::duration<Rep1, Ratio1>& duration, + const std::chrono::duration<Rep2, Ratio2>& precision) -> + typename std::common_type<std::chrono::duration<Rep1, Ratio1>, + std::chrono::duration<Rep2, Ratio2> >::type { + return duration - (duration % precision); + } + + template<typename Rep1, typename Ratio1, typename Rep2, typename Ratio2> + auto ceil(const std::chrono::duration<Rep1, Ratio1>& duration, + const std::chrono::duration<Rep2, Ratio2>& precision) -> + typename std::common_type<std::chrono::duration<Rep1, Ratio1>, + std::chrono::duration<Rep2, Ratio2> >::type { + auto tmod = duration % precision; + return duration - tmod + (tmod > tmod.zero() ? 1 : 0) * precision; + } + + template<typename Clock, typename Duration, typename Rep, typename Ratio> + auto floor(const std::chrono::time_point<Clock, Duration>& timepoint, + const std::chrono::duration<Rep, Ratio>& precision) -> + std::chrono::time_point<Clock, + typename std::common_type< + Duration, std::chrono::duration<Rep, Ratio> + >::type> { + return std::chrono::time_point< + Clock, typename std::common_type< + Duration, std::chrono::duration<Rep, Ratio> >::type>( + floor(timepoint.time_since_epoch(), precision)); + } + template<typename Clock, typename Duration, typename Rep, typename Ratio> + auto ceil(const std::chrono::time_point<Clock, Duration>& timepoint, + const std::chrono::duration<Rep, Ratio>& precision) -> + std::chrono::time_point<Clock, + typename std::common_type< + Duration, + std::chrono::duration<Rep, Ratio> >::type> { + return std::chrono::time_point< + Clock, typename std::common_type< + Duration, std::chrono::duration<Rep, Ratio> >::type>( + ceil(timepoint.time_since_epoch(), precision)); + } + + static inline timespan make_timespan(const double d) { + return std::chrono::duration_cast<timespan>( + std::chrono::duration<double>(d)); + } + + std::ostream& operator<<(std::ostream& m, const timespan& t); + std::ostream& operator<<(std::ostream& m, const real_time& t); + std::ostream& operator<<(std::ostream& m, const mono_time& t); +} // namespace ceph + +// We need these definitions to be able to hande ::encode/::decode on +// time points. + +template<typename Clock, typename Duration> +void encode(const std::chrono::time_point<Clock, Duration>& t, + ceph::bufferlist &bl) { + struct timespec ts = Clock::to_timespec(); + // A 32 bit count of seconds causes me vast unhappiness. + ::encode((uint32_t) ts.tv_sec, bl); + ::encode((uint32_t) ts.tv_nsec, bl); +} + +template<typename Clock, typename Duration> +void decode(std::chrono::time_point<Clock, Duration>& t, + bufferlist::iterator& p) { + struct timespec ts; + ::decode((uint32_t&) ts.tv_sec, p); + ::decode((uint32_t&) ts.tv_nsec, p); + + t = Clock::from_timespec(ts); +} + +// C++ Overload Resolution requires that our encode/decode functions +// be defined in the same namespace as the type. So we need this +// to handle things like ::encode(std::vector<ceph::real_time // > >) + +namespace std { + namespace chrono { + template<typename Clock, typename Duration> + void encode(const time_point<Clock, Duration>& t, + ceph::bufferlist &bl) { + ::encode(t, bl); + } + + template<typename Clock, typename Duration> + void decode(time_point<Clock, Duration>& t, bufferlist::iterator &p) { + ::decode(t, p); + } + } // namespace chrono +} // namespace std + +#endif // COMMON_CEPH_TIME_H diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index 82377732e4a..04b136765b0 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -536,6 +536,25 @@ target_link_libraries(unittest_sloppy_crc_map global set_target_properties(unittest_sloppy_crc_map PROPERTIES COMPILE_FLAGS ${UNITTEST_CXX_FLAGS}) +# unittest_time +set(unittest_time_srcs + common/test_time.cc + ${CMAKE_SOURCE_DIR}/src/common/ceph_time.cc + ) +add_executable(unittest_time + ${unittest_time_srcs} + $<TARGET_OBJECTS:heap_profiler_objs> + ) +target_link_libraries(unittest_time + global + ${BLKID_LIBRARIES} + ${CMAKE_DL_LIBS} + ${TCMALLOC_LIBS} + ${UNITTEST_LIBS} + ) +set_target_properties(unittest_time + PROPERTIES COMPILE_FLAGS ${UNITTEST_CXX_FLAGS}) + # unittest_util add_executable(unittest_util EXCLUDE_FROM_ALL common/test_util.cc diff --git a/src/test/Makefile.am b/src/test/Makefile.am index 86ff39180ea..29c1d61db90 100644 --- a/src/test/Makefile.am +++ b/src/test/Makefile.am @@ -190,6 +190,11 @@ unittest_sloppy_crc_map_CXXFLAGS = $(UNITTEST_CXXFLAGS) unittest_sloppy_crc_map_LDADD = $(UNITTEST_LDADD) $(CEPH_GLOBAL) check_TESTPROGRAMS += unittest_sloppy_crc_map +unittest_time_SOURCES = test/common/test_time.cc +unittest_time_CXXFLAGS = $(UNITTEST_CXXFLAGS) +unittest_time_LDADD = $(LIBCOMMON) -lm $(UNITTEST_LDADD) $(CRYPTO_LIBS) $(EXTRALIBS) +check_TESTPROGRAMS += unittest_time + unittest_util_SOURCES = test/common/test_util.cc unittest_util_CXXFLAGS = $(UNITTEST_CXXFLAGS) unittest_util_LDADD = $(LIBCOMMON) -lm $(UNITTEST_LDADD) $(CRYPTO_LIBS) $(EXTRALIBS) diff --git a/src/test/common/test_time.cc b/src/test/common/test_time.cc new file mode 100644 index 00000000000..e363085da33 --- /dev/null +++ b/src/test/common/test_time.cc @@ -0,0 +1,145 @@ + +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab +/* + * Ceph - scalable distributed file system + * + * Copyright (C) 2004-2006 Sage Weil <sage@newdream.net> + * + * This is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software + * Foundation. See file COPYING. + * + */ + +#include <ctime> + +#include "common/ceph_time.h" +#include "include/rados.h" +#include "gtest/gtest.h" + + +using ceph::real_clock; +using ceph::real_time; + +using ceph::real_clock; +using ceph::real_time; + +using ceph::coarse_real_clock; +using ceph::coarse_mono_clock; + +using ceph::timespan; +using ceph::signedspan; + +using std::chrono::seconds; +using std::chrono::microseconds; +using std::chrono::nanoseconds; + +static_assert(!real_clock::is_steady, "ceph::real_clock must not be steady."); +static_assert(!coarse_real_clock::is_steady, + "ceph::coarse_real_clock must not be steady."); + +static_assert(mono_clock::is_steady, "ceph::mono_clock must be steady."); +static_assert(coarse_mono_clock::is_steady, + "ceph::coarse_mono_clock must be steady."); + +// Before this file was written. +static constexpr uint32_t bs = 1440701569; +static constexpr uint32_t bns = 123456789; +static constexpr uint32_t bus = 123456; +static constexpr time_t btt = bs; +static constexpr struct timespec bts = { bs, bns }; +static constexpr struct ceph_timespec bcts = { bs, bns }; +static constexpr struct timeval btv = { bs, bus }; +static constexpr double bd = bs + ((double)bns / 1000000000.); + +template<typename Clock> +static void system_clock_sanity() { + static constexpr typename Clock::time_point brt(seconds(bs) + + nanoseconds(bns)); + const typename Clock::time_point now(Clock::now()); + + ASSERT_GT(now, brt); + + ASSERT_GT(Clock::to_time_t(now), btt); + + ASSERT_GT(Clock::to_timespec(now).tv_sec, bts.tv_sec); + ASSERT_LT(Clock::to_timespec(now).tv_nsec, 1000000000L); + + ASSERT_GT(Clock::to_ceph_timespec(now).tv_sec, bcts.tv_sec); + ASSERT_LT(Clock::to_ceph_timespec(now).tv_nsec, 1000000000UL); + + ASSERT_GT(Clock::to_timeval(now).tv_sec, btv.tv_sec); + ASSERT_LT(Clock::to_timeval(now).tv_usec, 1000000L); +} + +template<typename Clock> +static void system_clock_conversions() { + static constexpr typename Clock::time_point brt(seconds(bs) + + nanoseconds(bns)); + + ASSERT_EQ(Clock::to_time_t(brt), btt); + ASSERT_EQ(Clock::from_time_t(btt) + nanoseconds(bns), brt); + + { + const struct timespec tts = Clock::to_timespec(brt); + ASSERT_EQ(tts.tv_sec, bts.tv_sec); + ASSERT_EQ(tts.tv_nsec, bts.tv_nsec); + } + ASSERT_EQ(Clock::from_timespec(bts), brt); + { + struct timespec tts; + Clock::to_timespec(brt, tts); + ASSERT_EQ(tts.tv_sec, bts.tv_sec); + ASSERT_EQ(tts.tv_nsec, bts.tv_nsec); + } + + { + const struct ceph_timespec tcts = Clock::to_ceph_timespec(brt); + ASSERT_EQ(tcts.tv_sec, bcts.tv_sec); + ASSERT_EQ(tcts.tv_nsec, bcts.tv_nsec); + } + ASSERT_EQ(Clock::from_ceph_timespec(bcts), brt); + { + struct ceph_timespec tcts; + Clock::to_ceph_timespec(brt, tcts); + ASSERT_EQ(tcts.tv_sec, bcts.tv_sec); + ASSERT_EQ(tcts.tv_nsec, bcts.tv_nsec); + } + + { + const struct timeval ttv = Clock::to_timeval(brt); + ASSERT_EQ(ttv.tv_sec, btv.tv_sec); + ASSERT_EQ(ttv.tv_usec, btv.tv_usec); + } + ASSERT_EQ(Clock::from_timeval(btv), brt - nanoseconds(bns - bus * 1000)); + { + struct timeval ttv; + Clock::to_timeval(brt, ttv); + ASSERT_EQ(ttv.tv_sec, btv.tv_sec); + ASSERT_EQ(ttv.tv_usec, btv.tv_usec); + } + + ASSERT_EQ(Clock::to_double(brt), bd); + // Fudge factor + ASSERT_LT(abs((Clock::from_double(bd) - brt).count()), 30); +} + +TEST(RealClock, Sanity) { + system_clock_sanity<real_clock>(); +} + + +TEST(RealClock, Conversions) { + system_clock_conversions<real_clock>(); +} + +TEST(CoarseRealClock, Sanity) { + system_clock_sanity<coarse_real_clock>(); +} + + +TEST(CoarseRealClock, Conversions) { + system_clock_conversions<coarse_real_clock>(); +} |