diff --git a/source/extensions/quic_listeners/quiche/platform/BUILD b/source/extensions/quic_listeners/quiche/platform/BUILD index e347bac4cfbf1..8bad70cce43c8 100644 --- a/source/extensions/quic_listeners/quiche/platform/BUILD +++ b/source/extensions/quic_listeners/quiche/platform/BUILD @@ -169,6 +169,17 @@ envoy_cc_library( ], ) +envoy_cc_library( + name = "envoy_quic_clock_lib", + srcs = ["envoy_quic_clock.cc"], + hdrs = ["envoy_quic_clock.h"], + visibility = ["//visibility:public"], + deps = [ + "//include/envoy/event:timer_interface", + "@com_googlesource_quiche//:quic_platform", + ], +) + envoy_cc_library( name = "quiche_common_platform_impl_lib", hdrs = [ diff --git a/source/extensions/quic_listeners/quiche/platform/envoy_quic_clock.cc b/source/extensions/quic_listeners/quiche/platform/envoy_quic_clock.cc new file mode 100644 index 0000000000000..b582956751da4 --- /dev/null +++ b/source/extensions/quic_listeners/quiche/platform/envoy_quic_clock.cc @@ -0,0 +1,22 @@ +#include "extensions/quic_listeners/quiche/platform/envoy_quic_clock.h" + +namespace Envoy { +namespace Quic { + +quic::QuicTime EnvoyQuicClock::ApproximateNow() const { + // This might be expensive as Dispatcher doesn't store approximate time_point. + return Now(); +} + +quic::QuicTime EnvoyQuicClock::Now() const { + return quic::QuicTime::Zero() + quic::QuicTime::Delta::FromMicroseconds( + microsecondsSinceEpoch(time_system_.monotonicTime())); +} + +quic::QuicWallTime EnvoyQuicClock::WallNow() const { + return quic::QuicWallTime::FromUNIXMicroseconds( + microsecondsSinceEpoch(time_system_.systemTime())); +} + +} // namespace Quic +} // namespace Envoy diff --git a/source/extensions/quic_listeners/quiche/platform/envoy_quic_clock.h b/source/extensions/quic_listeners/quiche/platform/envoy_quic_clock.h new file mode 100644 index 0000000000000..eb90a1db1b94f --- /dev/null +++ b/source/extensions/quic_listeners/quiche/platform/envoy_quic_clock.h @@ -0,0 +1,30 @@ +#pragma once + +#include + +#include "envoy/event/timer.h" + +#include "quiche/quic/platform/api/quic_clock.h" + +namespace Envoy { +namespace Quic { + +class EnvoyQuicClock : public quic::QuicClock { +public: + EnvoyQuicClock(Event::TimeSystem& time_system) : time_system_(time_system) {} + + // quic::QuicClock + quic::QuicTime ApproximateNow() const override; + quic::QuicTime Now() const override; + quic::QuicWallTime WallNow() const override; + +private: + template int64_t microsecondsSinceEpoch(std::chrono::time_point time) const { + return std::chrono::duration_cast(time.time_since_epoch()).count(); + } + + Event::TimeSystem& time_system_; +}; + +} // namespace Quic +} // namespace Envoy diff --git a/test/extensions/quic_listeners/quiche/platform/BUILD b/test/extensions/quic_listeners/quiche/platform/BUILD index ba5156ac0bd37..d15ec6a49bd21 100644 --- a/test/extensions/quic_listeners/quiche/platform/BUILD +++ b/test/extensions/quic_listeners/quiche/platform/BUILD @@ -187,3 +187,13 @@ envoy_cc_test_library( ":quic_platform_expect_bug_impl_lib", ], ) + +envoy_cc_test( + name = "envoy_quic_clock_test", + srcs = ["envoy_quic_clock_test.cc"], + deps = [ + "//source/extensions/quic_listeners/quiche/platform:envoy_quic_clock_lib", + "//test/test_common:simulated_time_system_lib", + "//test/test_common:test_time_lib", + ], +) diff --git a/test/extensions/quic_listeners/quiche/platform/envoy_quic_clock_test.cc b/test/extensions/quic_listeners/quiche/platform/envoy_quic_clock_test.cc new file mode 100644 index 0000000000000..8bdf8a9875124 --- /dev/null +++ b/test/extensions/quic_listeners/quiche/platform/envoy_quic_clock_test.cc @@ -0,0 +1,54 @@ +#include + +#include "extensions/quic_listeners/quiche/platform/envoy_quic_clock.h" + +#include "test/test_common/simulated_time_system.h" +#include "test/test_common/test_time.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +namespace Envoy { +namespace Quic { + +TEST(EnvoyQuicClockTest, TestNow) { + Event::SimulatedTimeSystemHelper time_system; + EnvoyQuicClock clock(time_system); + uint64_t mono_time = std::chrono::duration_cast( + time_system.monotonicTime().time_since_epoch()) + .count(); + uint64_t sys_time = std::chrono::duration_cast( + time_system.systemTime().time_since_epoch()) + .count(); + // Advance time by 1000000us. + time_system.sleep(std::chrono::microseconds(1000000)); + EXPECT_EQ(mono_time + 1000000, (clock.Now() - quic::QuicTime::Zero()).ToMicroseconds()); + EXPECT_EQ(sys_time + 1000000, clock.WallNow().ToUNIXMicroseconds()); + + // Advance time by 10us. + time_system.sleep(std::chrono::microseconds(10)); + EXPECT_EQ(mono_time + 1000000 + 10, (clock.Now() - quic::QuicTime::Zero()).ToMicroseconds()); + EXPECT_EQ(sys_time + 1000000 + 10, clock.WallNow().ToUNIXMicroseconds()); + EXPECT_EQ(clock.ApproximateNow(), clock.Now()); + + // Advance time by 2ms. + time_system.sleep(std::chrono::milliseconds(2)); + EXPECT_EQ(mono_time + 1000000 + 10 + 2 * 1000, + (clock.Now() - quic::QuicTime::Zero()).ToMicroseconds()); + EXPECT_EQ(sys_time + 1000000 + 10 + 2 * 1000, clock.WallNow().ToUNIXMicroseconds()); +} + +// Tests that Now() should never go back. +TEST(EnvoyQuicClockTest, TestMonotonicityWithReadTimeSystem) { + Event::TestRealTimeSystem time_system; + EnvoyQuicClock clock(time_system); + quic::QuicTime last_now = clock.Now(); + for (int i = 0; i < 1000; ++i) { + quic::QuicTime now = clock.Now(); + ASSERT_LE(last_now, now); + last_now = now; + } +} + +} // namespace Quic +} // namespace Envoy