| // Licensed to the Apache Software Foundation (ASF) under one |
| // or more contributor license agreements. See the NOTICE file |
| // distributed with this work for additional information |
| // regarding copyright ownership. The ASF licenses this file |
| // to you under the Apache License, Version 2.0 (the |
| // "License"); you may not use this file except in compliance |
| // with the License. You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, |
| // software distributed under the License is distributed on an |
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| // KIND, either express or implied. See the License for the |
| // specific language governing permissions and limitations |
| // under the License. |
| |
| #include "kudu/util/monotime.h" |
| |
| #include <sys/time.h> |
| #include <unistd.h> |
| |
| #include <cstdint> |
| #include <ctime> |
| #include <ostream> |
| |
| #include <glog/logging.h> |
| #include <gtest/gtest.h> |
| |
| #include "kudu/util/test_util.h" |
| |
| namespace kudu { |
| |
| TEST(TestMonoTime, TestMonotonicity) { |
| alarm(360); |
| MonoTime prev(MonoTime::Now()); |
| MonoTime next; |
| |
| do { |
| next = MonoTime::Now(); |
| //LOG(INFO) << " next = " << next.ToString(); |
| } while (!prev.ComesBefore(next)); |
| ASSERT_FALSE(next.ComesBefore(prev)); |
| alarm(0); |
| } |
| |
| TEST(TestMonoTime, TestComparison) { |
| MonoTime now(MonoTime::Now()); |
| MonoTime future(now); |
| future.AddDelta(MonoDelta::FromNanoseconds(1L)); |
| |
| ASSERT_GT((future - now).ToNanoseconds(), 0); |
| ASSERT_LT((now - future).ToNanoseconds(), 0); |
| ASSERT_EQ((now - now).ToNanoseconds(), 0); |
| |
| MonoDelta nano(MonoDelta::FromNanoseconds(1L)); |
| MonoDelta mil(MonoDelta::FromMilliseconds(1L)); |
| MonoDelta sec(MonoDelta::FromSeconds(1.0)); |
| |
| ASSERT_TRUE(nano.LessThan(mil)); |
| ASSERT_TRUE(mil.LessThan(sec)); |
| ASSERT_TRUE(mil.MoreThan(nano)); |
| ASSERT_TRUE(sec.MoreThan(mil)); |
| } |
| |
| TEST(TestMonoTime, TestTimeVal) { |
| struct timeval tv; |
| tv.tv_sec = 0; |
| tv.tv_usec = 0; |
| |
| // Normal conversion case. |
| MonoDelta one_sec_one_micro(MonoDelta::FromNanoseconds(1000001000L)); |
| one_sec_one_micro.ToTimeVal(&tv); |
| ASSERT_EQ(1, tv.tv_sec); |
| ASSERT_EQ(1, tv.tv_usec); |
| |
| // Case where we are still positive but sub-micro. |
| // Round up to nearest microsecond. This is to avoid infinite timeouts |
| // in APIs that take a struct timeval. |
| MonoDelta zero_sec_one_nano(MonoDelta::FromNanoseconds(1L)); |
| zero_sec_one_nano.ToTimeVal(&tv); |
| ASSERT_EQ(0, tv.tv_sec); |
| ASSERT_EQ(1, tv.tv_usec); // Special case: 1ns rounds up to |
| |
| // Negative conversion case. Ensure the timeval is normalized. |
| // That means sec is negative and usec is positive. |
| MonoDelta neg_micro(MonoDelta::FromMicroseconds(-1L)); |
| ASSERT_EQ(-1000, neg_micro.ToNanoseconds()); |
| neg_micro.ToTimeVal(&tv); |
| ASSERT_EQ(-1, tv.tv_sec); |
| ASSERT_EQ(999999, tv.tv_usec); |
| |
| // Case where we are still negative but sub-micro. |
| // Round up to nearest microsecond. This is to avoid infinite timeouts |
| // in APIs that take a struct timeval and for consistency. |
| MonoDelta zero_sec_neg_one_nano(MonoDelta::FromNanoseconds(-1L)); |
| zero_sec_neg_one_nano.ToTimeVal(&tv); |
| ASSERT_EQ(-1, tv.tv_sec); |
| ASSERT_EQ(999999, tv.tv_usec); |
| } |
| |
| TEST(TestMonoTime, TestTimeSpec) { |
| MonoTime one_sec_one_nano_expected(1000000001L); |
| struct timespec ts; |
| ts.tv_sec = 1; |
| ts.tv_nsec = 1; |
| MonoTime one_sec_one_nano_actual(ts); |
| ASSERT_EQ(0, (one_sec_one_nano_expected - one_sec_one_nano_actual).ToNanoseconds()); |
| |
| MonoDelta zero_sec_two_nanos(MonoDelta::FromNanoseconds(2L)); |
| zero_sec_two_nanos.ToTimeSpec(&ts); |
| ASSERT_EQ(0, ts.tv_sec); |
| ASSERT_EQ(2, ts.tv_nsec); |
| |
| // Negative conversion case. Ensure the timespec is normalized. |
| // That means sec is negative and nsec is positive. |
| MonoDelta neg_nano(MonoDelta::FromNanoseconds(-1L)); |
| ASSERT_EQ(-1, neg_nano.ToNanoseconds()); |
| neg_nano.ToTimeSpec(&ts); |
| ASSERT_EQ(-1, ts.tv_sec); |
| ASSERT_EQ(999999999, ts.tv_nsec); |
| |
| } |
| |
| TEST(TestMonoTime, TestDeltas) { |
| alarm(360); |
| const MonoDelta max_delta(MonoDelta::FromSeconds(0.1)); |
| MonoTime prev(MonoTime::Now()); |
| MonoDelta cur_delta; |
| do { |
| cur_delta = MonoTime::Now() - prev; |
| } while (cur_delta.LessThan(max_delta)); |
| alarm(0); |
| } |
| |
| TEST(TestMonoTime, TestDeltaConversions) { |
| // TODO: Reliably test MonoDelta::FromSeconds() considering floating-point rounding errors |
| |
| MonoDelta mil(MonoDelta::FromMilliseconds(500)); |
| ASSERT_EQ(500 * MonoTime::kNanosecondsPerMillisecond, mil.nano_delta_); |
| |
| MonoDelta micro(MonoDelta::FromMicroseconds(500)); |
| ASSERT_EQ(500 * MonoTime::kNanosecondsPerMicrosecond, micro.nano_delta_); |
| |
| MonoDelta nano(MonoDelta::FromNanoseconds(500)); |
| ASSERT_EQ(500, nano.nano_delta_); |
| } |
| |
| static void DoTestMonoTimePerf() { |
| const MonoDelta max_delta(MonoDelta::FromMilliseconds(500)); |
| uint64_t num_calls = 0; |
| MonoTime prev(MonoTime::Now()); |
| MonoDelta cur_delta; |
| do { |
| cur_delta = MonoTime::Now() - prev; |
| num_calls++; |
| } while (cur_delta.LessThan(max_delta)); |
| LOG(INFO) << "DoTestMonoTimePerf():" |
| << num_calls << " in " |
| << max_delta.ToString() << " seconds."; |
| } |
| |
| TEST(TestMonoTime, TestSleepFor) { |
| MonoTime start = MonoTime::Now(); |
| MonoDelta sleep = MonoDelta::FromMilliseconds(100); |
| SleepFor(sleep); |
| MonoTime end = MonoTime::Now(); |
| MonoDelta actualSleep = end - start; |
| ASSERT_GE(actualSleep.ToNanoseconds(), sleep.ToNanoseconds()); |
| } |
| |
| TEST(TestMonoTime, TestSleepForOverflow) { |
| SKIP_IF_SLOW_NOT_ALLOWED(); |
| |
| // This quantity (~4s sleep) overflows a 32-bit integer such that |
| // the value becomes 0. |
| MonoTime start = MonoTime::Now(); |
| MonoDelta sleep = MonoDelta::FromNanoseconds(1L << 32); |
| SleepFor(sleep); |
| MonoTime end = MonoTime::Now(); |
| MonoDelta actualSleep = end - start; |
| ASSERT_GE(actualSleep.ToNanoseconds(), sleep.ToNanoseconds()); |
| } |
| |
| // Test functionality of the handy operators for MonoTime/MonoDelta objects. |
| // The test assumes that the core functionality provided by the |
| // MonoTime/MonoDelta objects are in place, and it tests that the operators |
| // have the expected behavior expressed in terms of already existing, |
| // semantically equivalent methods. |
| TEST(TestMonoTime, TestOperators) { |
| // MonoTime& MonoTime::operator+=(const MonoDelta& delta); |
| { |
| MonoTime tmp = MonoTime::Now(); |
| MonoTime start = tmp; |
| MonoDelta delta = MonoDelta::FromMilliseconds(100); |
| MonoTime o_end = start; |
| o_end += delta; |
| tmp.AddDelta(delta); |
| MonoTime m_end = tmp; |
| EXPECT_TRUE(m_end.Equals(o_end)); |
| } |
| |
| // MonoTime& MonoTime::operator-=(const MonoDelta& delta); |
| { |
| MonoTime tmp = MonoTime::Now(); |
| MonoTime start = tmp; |
| MonoDelta delta = MonoDelta::FromMilliseconds(100); |
| MonoTime o_end = start; |
| o_end -= delta; |
| tmp.AddDelta(MonoDelta::FromNanoseconds(-delta.ToNanoseconds())); |
| MonoTime m_end = tmp; |
| EXPECT_TRUE(m_end.Equals(o_end)); |
| } |
| |
| // MonoDelta& MonoDelta::operator+=(const MonoDelta& delta); |
| { |
| MonoDelta d0 = MonoDelta::FromNanoseconds(0); |
| MonoDelta d1 = MonoDelta::FromNanoseconds(1); |
| MonoDelta d1_prev = d1; |
| d1 += d0; |
| ASSERT_EQ(d1_prev, d1); |
| ASSERT_EQ(1, d1.ToNanoseconds()); |
| d0 += d1; |
| ASSERT_EQ(d1, d0); |
| ASSERT_EQ(1, d0.ToNanoseconds()); |
| d1 += d1_prev; |
| ASSERT_EQ(2, d1.ToNanoseconds()); |
| |
| MonoDelta d2 = MonoDelta::FromNanoseconds(-1); |
| d1 += d2; |
| ASSERT_EQ(1, d1.ToNanoseconds()); |
| d2 += d1; |
| ASSERT_EQ(0, d2.ToNanoseconds()); |
| } |
| |
| // MonoDelta& MonoDelta::operator-=(const MonoDelta& delta); |
| { |
| MonoDelta d0 = MonoDelta::FromNanoseconds(0); |
| MonoDelta d1 = MonoDelta::FromNanoseconds(1); |
| MonoDelta d1_prev = d1; |
| d1 -= d0; |
| ASSERT_EQ(d1_prev, d1); |
| ASSERT_EQ(1, d1.ToNanoseconds()); |
| d0 -= d1; |
| ASSERT_EQ(-1, d0.ToNanoseconds()); |
| |
| MonoDelta d2 = MonoDelta::FromNanoseconds(-2); |
| d0 -= d2; |
| ASSERT_EQ(1, d0.ToNanoseconds()); |
| d2 -= d0; |
| ASSERT_EQ(-3, d2.ToNanoseconds()); |
| } |
| |
| // bool operator==(const MonoDelta& lhs, const MonoDelta& rhs); |
| { |
| MonoDelta dn = MonoDelta::FromNanoseconds(0); |
| MonoDelta dm = MonoDelta::FromMicroseconds(0); |
| ASSERT_TRUE(dn.Equals(dm)); |
| EXPECT_TRUE(dn == dm); |
| EXPECT_TRUE(dm == dn); |
| } |
| |
| // bool operator!=(const MonoDelta& lhs, const MonoDelta& rhs); |
| { |
| MonoDelta dn = MonoDelta::FromNanoseconds(1); |
| MonoDelta dm = MonoDelta::FromMicroseconds(1); |
| ASSERT_FALSE(dn.Equals(dm)); |
| EXPECT_TRUE(dn != dm); |
| EXPECT_TRUE(dm != dn); |
| } |
| |
| // bool operator<(const MonoDelta& lhs, const MonoDelta& rhs); |
| { |
| MonoDelta d0 = MonoDelta::FromNanoseconds(0); |
| MonoDelta d1 = MonoDelta::FromNanoseconds(1); |
| ASSERT_TRUE(d0.LessThan(d1)); |
| EXPECT_TRUE(d0 < d1); |
| } |
| |
| // bool operator<=(const MonoDelta& lhs, const MonoDelta& rhs); |
| { |
| MonoDelta d0 = MonoDelta::FromNanoseconds(0); |
| MonoDelta d1 = MonoDelta::FromNanoseconds(1); |
| ASSERT_TRUE(d0.LessThan(d1)); |
| EXPECT_TRUE(d0 <= d1); |
| |
| MonoDelta d20 = MonoDelta::FromNanoseconds(2); |
| MonoDelta d21 = MonoDelta::FromNanoseconds(2); |
| ASSERT_TRUE(d20.Equals(d21)); |
| EXPECT_TRUE(d20 <= d21); |
| } |
| |
| // bool operator>(const MonoDelta& lhs, const MonoDelta& rhs); |
| { |
| MonoDelta d0 = MonoDelta::FromNanoseconds(0); |
| MonoDelta d1 = MonoDelta::FromNanoseconds(1); |
| ASSERT_TRUE(d1.MoreThan(d0)); |
| EXPECT_TRUE(d1 > d0); |
| } |
| |
| // bool operator>=(const MonoDelta& lhs, const MonoDelta& rhs); |
| { |
| MonoDelta d0 = MonoDelta::FromNanoseconds(0); |
| MonoDelta d1 = MonoDelta::FromNanoseconds(1); |
| ASSERT_TRUE(d1.MoreThan(d0)); |
| EXPECT_TRUE(d1 >= d1); |
| |
| MonoDelta d20 = MonoDelta::FromNanoseconds(2); |
| MonoDelta d21 = MonoDelta::FromNanoseconds(2); |
| ASSERT_TRUE(d20.Equals(d21)); |
| EXPECT_TRUE(d21 >= d20); |
| } |
| |
| // MonoDelta operator-(const MonoDelta& lhs, const MonoDelta& rhs); |
| { |
| { |
| MonoDelta d0 = MonoDelta::FromNanoseconds(0); |
| MonoDelta d1 = MonoDelta::FromNanoseconds(1); |
| MonoDelta d01 = d0 - d1; |
| MonoDelta d10 = d1 - d0; |
| EXPECT_EQ(d1, d10); |
| EXPECT_EQ(MonoDelta::FromNanoseconds(-1), d01); |
| EXPECT_EQ(MonoDelta::FromNanoseconds(1), d10); |
| EXPECT_GT(d0, d01); |
| EXPECT_GT(d10, d0); |
| EXPECT_EQ(d1, d10); |
| } |
| |
| { |
| MonoDelta d0 = MonoDelta::FromNanoseconds(2); |
| MonoDelta d1 = MonoDelta::FromNanoseconds(2); |
| MonoDelta d01 = d0 - d1; |
| MonoDelta d10 = d1 - d0; |
| EXPECT_EQ(d01, d10); |
| EXPECT_EQ(MonoDelta::FromNanoseconds(0), d01); |
| } |
| |
| { |
| MonoDelta d0 = MonoDelta::FromNanoseconds(3); |
| MonoDelta d1 = MonoDelta::FromNanoseconds(-3); |
| MonoDelta d01 = d0 - d1; |
| MonoDelta d10 = d1 - d0; |
| EXPECT_EQ(MonoDelta::FromNanoseconds(6), d01); |
| EXPECT_EQ(MonoDelta::FromNanoseconds(-6), d10); |
| } |
| } |
| |
| // MonoDelta operator+(const MonoDelta& lhs, const MonoDelta& rhs); |
| { |
| { |
| MonoDelta d0 = MonoDelta::FromNanoseconds(0); |
| MonoDelta d1 = MonoDelta::FromNanoseconds(1); |
| MonoDelta d01 = d0 + d1; |
| MonoDelta d10 = d1 + d0; |
| EXPECT_EQ(d01, d10); |
| EXPECT_EQ(MonoDelta::FromNanoseconds(1), d01); |
| } |
| |
| { |
| MonoDelta d0 = MonoDelta::FromNanoseconds(3); |
| MonoDelta d1 = MonoDelta::FromNanoseconds(-3); |
| MonoDelta d01 = d0 + d1; |
| MonoDelta d10 = d1 + d0; |
| EXPECT_EQ(d01, d10); |
| EXPECT_EQ(MonoDelta::FromNanoseconds(0), d01); |
| } |
| } |
| |
| // bool operator==(const MonoTime& lhs, const MonoTime& rhs); |
| { |
| MonoTime t0 = MonoTime::Now(); |
| MonoTime t1(t0); |
| ASSERT_TRUE(t0.Equals(t1)); |
| ASSERT_TRUE(t1.Equals(t0)); |
| EXPECT_TRUE(t0 == t1); |
| EXPECT_TRUE(t1 == t0); |
| } |
| |
| // bool operator!=(const MonoTime& lhs, const MonoTime& rhs); |
| { |
| MonoTime t0 = MonoTime::Now(); |
| MonoTime t1(t0 + MonoDelta::FromMilliseconds(100)); |
| ASSERT_TRUE(!t0.Equals(t1)); |
| ASSERT_TRUE(!t1.Equals(t0)); |
| EXPECT_TRUE(t0 != t1); |
| EXPECT_TRUE(t1 != t0); |
| } |
| |
| // bool operator<(const MonoTime& lhs, const MonoTime& rhs); |
| { |
| MonoTime t0 = MonoTime::Now(); |
| MonoTime t1(t0 + MonoDelta::FromMilliseconds(100)); |
| ASSERT_TRUE(t0.ComesBefore(t1)); |
| ASSERT_FALSE(t1.ComesBefore(t0)); |
| EXPECT_TRUE(t0 < t1); |
| EXPECT_FALSE(t1 < t0); |
| } |
| |
| // bool operator<=(const MonoTime& lhs, const MonoTime& rhs); |
| { |
| MonoTime t00 = MonoTime::Now(); |
| MonoTime t01(t00); |
| ASSERT_TRUE(t00.Equals(t00)); |
| ASSERT_TRUE(t00.Equals(t01)); |
| ASSERT_TRUE(t01.Equals(t00)); |
| ASSERT_TRUE(t01.Equals(t01)); |
| EXPECT_TRUE(t00 <= t00); |
| EXPECT_TRUE(t00 <= t01); |
| EXPECT_TRUE(t01 <= t00); |
| EXPECT_TRUE(t01 <= t01); |
| |
| MonoTime t1(t00 + MonoDelta::FromMilliseconds(100)); |
| ASSERT_TRUE(t00.ComesBefore(t1)); |
| ASSERT_TRUE(t01.ComesBefore(t1)); |
| ASSERT_FALSE(t1.ComesBefore(t00)); |
| ASSERT_FALSE(t1.ComesBefore(t01)); |
| EXPECT_TRUE(t00 <= t1); |
| EXPECT_TRUE(t01 <= t1); |
| EXPECT_FALSE(t1 <= t00); |
| EXPECT_FALSE(t1 <= t01); |
| } |
| |
| // bool operator>(const MonoTime& lhs, const MonoTime& rhs); |
| { |
| MonoTime t0 = MonoTime::Now(); |
| MonoTime t1(t0 + MonoDelta::FromMilliseconds(100)); |
| ASSERT_TRUE(t0.ComesBefore(t1)); |
| ASSERT_FALSE(t1.ComesBefore(t0)); |
| EXPECT_TRUE(t0 < t1); |
| EXPECT_FALSE(t1 < t0); |
| } |
| |
| // bool operator>=(const MonoTime& lhs, const MonoTime& rhs); |
| { |
| MonoTime t00 = MonoTime::Now(); |
| MonoTime t01(t00); |
| ASSERT_TRUE(t00.Equals(t00)); |
| ASSERT_TRUE(t00.Equals(t01)); |
| ASSERT_TRUE(t01.Equals(t00)); |
| ASSERT_TRUE(t01.Equals(t01)); |
| EXPECT_TRUE(t00 >= t00); |
| EXPECT_TRUE(t00 >= t01); |
| EXPECT_TRUE(t01 >= t00); |
| EXPECT_TRUE(t01 >= t01); |
| |
| MonoTime t1(t00 + MonoDelta::FromMilliseconds(100)); |
| ASSERT_TRUE(t00.ComesBefore(t1)); |
| ASSERT_TRUE(t01.ComesBefore(t1)); |
| ASSERT_FALSE(t1.ComesBefore(t00)); |
| ASSERT_FALSE(t1.ComesBefore(t01)); |
| EXPECT_FALSE(t00 >= t1); |
| EXPECT_FALSE(t01 >= t1); |
| EXPECT_TRUE(t1 >= t00); |
| EXPECT_TRUE(t1 >= t01); |
| } |
| |
| // MonoDelta operator-(const MonoTime& t0, const MonoTime& t1); |
| { |
| const int64_t deltas[] = { 100, -100 }; |
| |
| MonoTime tmp = MonoTime::Now(); |
| for (auto d : deltas) { |
| MonoDelta delta = MonoDelta::FromMilliseconds(d); |
| |
| MonoTime start = tmp; |
| tmp.AddDelta(delta); |
| MonoTime end = tmp; |
| MonoDelta delta_o = end - start; |
| EXPECT_TRUE(delta.Equals(delta_o)); |
| } |
| } |
| |
| // MonoTime operator+(const MonoTime& t, const MonoDelta& delta); |
| { |
| MonoTime start = MonoTime::Now(); |
| |
| MonoDelta delta_0 = MonoDelta::FromMilliseconds(0); |
| MonoTime end_0 = start + delta_0; |
| EXPECT_TRUE(end_0.Equals(start)); |
| |
| MonoDelta delta_1 = MonoDelta::FromMilliseconds(1); |
| MonoTime end_1 = start + delta_1; |
| EXPECT_TRUE(end_1 > end_0); |
| end_0.AddDelta(delta_1); |
| EXPECT_TRUE(end_0.Equals(end_1)); |
| } |
| |
| // MonoTime operator-(const MonoTime& t, const MonoDelta& delta); |
| { |
| MonoTime start = MonoTime::Now(); |
| |
| MonoDelta delta_0 = MonoDelta::FromMilliseconds(0); |
| MonoTime end_0 = start - delta_0; |
| EXPECT_TRUE(end_0.Equals(start)); |
| |
| MonoDelta delta_1 = MonoDelta::FromMilliseconds(1); |
| MonoTime end_1 = start - delta_1; |
| EXPECT_TRUE(end_1 < end_0); |
| end_1.AddDelta(delta_1); |
| EXPECT_TRUE(end_1.Equals(end_0)); |
| } |
| } |
| |
| TEST(TestMonoTimePerf, TestMonoTimePerf) { |
| alarm(360); |
| DoTestMonoTimePerf(); |
| alarm(0); |
| } |
| |
| } // namespace kudu |