| // Copyright 2022 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "quiche/quic/core/io/quic_poll_event_loop.h" |
| |
| #include <fcntl.h> |
| #include <unistd.h> |
| |
| #include <cerrno> |
| #include <memory> |
| #include <vector> |
| |
| #include "absl/memory/memory.h" |
| #include "quiche/quic/core/io/quic_event_loop.h" |
| #include "quiche/quic/core/quic_alarm.h" |
| #include "quiche/quic/core/quic_alarm_factory.h" |
| #include "quiche/quic/core/quic_time.h" |
| #include "quiche/quic/platform/api/quic_test.h" |
| #include "quiche/quic/test_tools/mock_clock.h" |
| |
| namespace quic { |
| |
| class QuicPollEventLoopPeer { |
| public: |
| static QuicTime::Delta ComputePollTimeout(const QuicPollEventLoop& loop, |
| QuicTime now, |
| QuicTime::Delta default_timeout) { |
| return loop.ComputePollTimeout(now, default_timeout); |
| } |
| }; |
| |
| } // namespace quic |
| |
| namespace quic::test { |
| namespace { |
| |
| using testing::_; |
| using testing::AtMost; |
| using testing::ElementsAre; |
| |
| constexpr QuicSocketEventMask kAllEvents = |
| kSocketEventReadable | kSocketEventWritable | kSocketEventError; |
| constexpr QuicTime::Delta kDefaultTimeout = QuicTime::Delta::FromSeconds(100); |
| |
| class MockQuicSocketEventListener : public QuicSocketEventListener { |
| public: |
| MOCK_METHOD(void, OnSocketEvent, |
| (QuicEventLoop* /*event_loop*/, QuicUdpSocketFd /*fd*/, |
| QuicSocketEventMask /*events*/), |
| (override)); |
| }; |
| |
| class MockDelegate : public QuicAlarm::Delegate { |
| public: |
| QuicConnectionContext* GetConnectionContext() override { return nullptr; } |
| MOCK_METHOD(void, OnAlarm, (), (override)); |
| }; |
| |
| class QuicPollEventLoopForTest : public QuicPollEventLoop { |
| public: |
| QuicPollEventLoopForTest(MockClock* clock) |
| : QuicPollEventLoop(clock), clock_(clock) {} |
| |
| int PollSyscall(pollfd* fds, nfds_t nfds, int timeout) override { |
| timeouts_.push_back(timeout); |
| if (eintr_after_ != QuicTime::Delta::Infinite()) { |
| errno = EINTR; |
| clock_->AdvanceTime(eintr_after_); |
| eintr_after_ = QuicTime::Delta::Infinite(); |
| return -1; |
| } |
| clock_->AdvanceTime(QuicTime::Delta::FromMilliseconds(timeout)); |
| return QuicPollEventLoop::PollSyscall(fds, nfds, timeout); |
| } |
| |
| void TriggerEintrAfter(QuicTime::Delta time) { eintr_after_ = time; } |
| |
| const std::vector<int>& timeouts() const { return timeouts_; } |
| |
| private: |
| MockClock* clock_; |
| QuicTime::Delta eintr_after_ = QuicTime::Delta::Infinite(); |
| std::vector<int> timeouts_; |
| }; |
| |
| class QuicPollEventLoopTest : public QuicTest { |
| public: |
| QuicPollEventLoopTest() |
| : loop_(&clock_), factory_(loop_.CreateAlarmFactory()) { |
| int fds[2]; |
| int result = ::pipe(fds); |
| QUICHE_CHECK(result >= 0) << "Failed to create a pipe, errno: " << errno; |
| read_fd_ = fds[0]; |
| write_fd_ = fds[1]; |
| |
| QUICHE_CHECK(::fcntl(read_fd_, F_SETFL, |
| ::fcntl(read_fd_, F_GETFL) | O_NONBLOCK) == 0) |
| << "Failed to mark pipe FD non-blocking, errno: " << errno; |
| QUICHE_CHECK(::fcntl(write_fd_, F_SETFL, |
| ::fcntl(write_fd_, F_GETFL) | O_NONBLOCK) == 0) |
| << "Failed to mark pipe FD non-blocking, errno: " << errno; |
| |
| clock_.AdvanceTime(10 * kDefaultTimeout); |
| } |
| |
| ~QuicPollEventLoopTest() { |
| close(read_fd_); |
| close(write_fd_); |
| } |
| |
| QuicTime::Delta ComputePollTimeout() { |
| return QuicPollEventLoopPeer::ComputePollTimeout(loop_, clock_.Now(), |
| kDefaultTimeout); |
| } |
| |
| std::pair<std::unique_ptr<QuicAlarm>, MockDelegate*> CreateAlarm() { |
| auto delegate = std::make_unique<testing::StrictMock<MockDelegate>>(); |
| MockDelegate* delegate_unowned = delegate.get(); |
| auto alarm = absl::WrapUnique(factory_->CreateAlarm(delegate.release())); |
| return std::make_pair(std::move(alarm), delegate_unowned); |
| } |
| |
| protected: |
| MockClock clock_; |
| QuicPollEventLoopForTest loop_; |
| std::unique_ptr<QuicAlarmFactory> factory_; |
| int read_fd_; |
| int write_fd_; |
| }; |
| |
| TEST_F(QuicPollEventLoopTest, NothingHappens) { |
| testing::StrictMock<MockQuicSocketEventListener> listener; |
| ASSERT_TRUE(loop_.RegisterSocket(read_fd_, kAllEvents, &listener)); |
| ASSERT_TRUE(loop_.RegisterSocket(write_fd_, kAllEvents, &listener)); |
| |
| // Attempt double-registration. |
| EXPECT_FALSE(loop_.RegisterSocket(write_fd_, kAllEvents, &listener)); |
| |
| EXPECT_EQ(ComputePollTimeout(), kDefaultTimeout); |
| |
| EXPECT_CALL(listener, OnSocketEvent(_, write_fd_, kSocketEventWritable)); |
| loop_.RunEventLoopOnce(QuicTime::Delta::FromMilliseconds(4)); |
| // Expect no further calls. |
| loop_.RunEventLoopOnce(QuicTime::Delta::FromMilliseconds(5)); |
| EXPECT_THAT(loop_.timeouts(), ElementsAre(4, 5)); |
| } |
| |
| TEST_F(QuicPollEventLoopTest, RearmWriter) { |
| testing::StrictMock<MockQuicSocketEventListener> listener; |
| ASSERT_TRUE(loop_.RegisterSocket(write_fd_, kAllEvents, &listener)); |
| |
| EXPECT_CALL(listener, OnSocketEvent(_, write_fd_, kSocketEventWritable)) |
| .Times(2); |
| loop_.RunEventLoopOnce(QuicTime::Delta::FromMilliseconds(1)); |
| ASSERT_TRUE(loop_.RearmSocket(write_fd_, kSocketEventWritable)); |
| loop_.RunEventLoopOnce(QuicTime::Delta::FromMilliseconds(1)); |
| } |
| |
| TEST_F(QuicPollEventLoopTest, Readable) { |
| testing::StrictMock<MockQuicSocketEventListener> listener; |
| ASSERT_TRUE(loop_.RegisterSocket(read_fd_, kAllEvents, &listener)); |
| |
| ASSERT_EQ(4, write(write_fd_, "test", 4)); |
| EXPECT_CALL(listener, OnSocketEvent(_, read_fd_, kSocketEventReadable)); |
| loop_.RunEventLoopOnce(QuicTime::Delta::FromMilliseconds(1)); |
| // Expect no further calls. |
| loop_.RunEventLoopOnce(QuicTime::Delta::FromMilliseconds(1)); |
| } |
| |
| TEST_F(QuicPollEventLoopTest, RearmReader) { |
| testing::StrictMock<MockQuicSocketEventListener> listener; |
| ASSERT_TRUE(loop_.RegisterSocket(read_fd_, kAllEvents, &listener)); |
| |
| ASSERT_EQ(4, write(write_fd_, "test", 4)); |
| EXPECT_CALL(listener, OnSocketEvent(_, read_fd_, kSocketEventReadable)); |
| loop_.RunEventLoopOnce(QuicTime::Delta::FromMilliseconds(1)); |
| // Expect no further calls. |
| loop_.RunEventLoopOnce(QuicTime::Delta::FromMilliseconds(1)); |
| } |
| |
| TEST_F(QuicPollEventLoopTest, WriterUnblocked) { |
| testing::StrictMock<MockQuicSocketEventListener> listener; |
| ASSERT_TRUE(loop_.RegisterSocket(write_fd_, kAllEvents, &listener)); |
| |
| EXPECT_CALL(listener, OnSocketEvent(_, write_fd_, kSocketEventWritable)); |
| loop_.RunEventLoopOnce(QuicTime::Delta::FromMilliseconds(1)); |
| loop_.RunEventLoopOnce(QuicTime::Delta::FromMilliseconds(1)); |
| |
| int io_result; |
| std::string data(2048, 'a'); |
| do { |
| io_result = write(write_fd_, data.data(), data.size()); |
| } while (io_result > 0); |
| ASSERT_EQ(errno, EAGAIN); |
| |
| // Rearm and expect no immediate calls. |
| ASSERT_TRUE(loop_.RearmSocket(write_fd_, kSocketEventWritable)); |
| loop_.RunEventLoopOnce(QuicTime::Delta::FromMilliseconds(1)); |
| |
| EXPECT_CALL(listener, OnSocketEvent(_, write_fd_, kSocketEventWritable)); |
| do { |
| io_result = read(read_fd_, data.data(), data.size()); |
| } while (io_result > 0); |
| ASSERT_EQ(errno, EAGAIN); |
| loop_.RunEventLoopOnce(QuicTime::Delta::FromMilliseconds(1)); |
| } |
| |
| TEST_F(QuicPollEventLoopTest, ArtificialEvent) { |
| testing::StrictMock<MockQuicSocketEventListener> listener; |
| ASSERT_TRUE(loop_.RegisterSocket(read_fd_, kAllEvents, &listener)); |
| ASSERT_TRUE(loop_.RegisterSocket(write_fd_, kAllEvents, &listener)); |
| |
| EXPECT_EQ(ComputePollTimeout(), kDefaultTimeout); |
| ASSERT_TRUE(loop_.ArtificiallyNotifyEvent(read_fd_, kSocketEventReadable)); |
| EXPECT_EQ(ComputePollTimeout(), QuicTime::Delta::Zero()); |
| |
| { |
| testing::InSequence s; |
| EXPECT_CALL(listener, OnSocketEvent(_, read_fd_, kSocketEventReadable)); |
| EXPECT_CALL(listener, OnSocketEvent(_, write_fd_, kSocketEventWritable)); |
| } |
| loop_.RunEventLoopOnce(QuicTime::Delta::FromMilliseconds(1)); |
| EXPECT_EQ(ComputePollTimeout(), kDefaultTimeout); |
| } |
| |
| TEST_F(QuicPollEventLoopTest, Unregister) { |
| testing::StrictMock<MockQuicSocketEventListener> listener; |
| ASSERT_TRUE(loop_.RegisterSocket(write_fd_, kAllEvents, &listener)); |
| ASSERT_TRUE(loop_.UnregisterSocket(write_fd_)); |
| |
| // Expect nothing to happen. |
| loop_.RunEventLoopOnce(QuicTime::Delta::FromMilliseconds(1)); |
| |
| EXPECT_FALSE(loop_.UnregisterSocket(write_fd_)); |
| EXPECT_FALSE(loop_.RearmSocket(write_fd_, kSocketEventWritable)); |
| EXPECT_FALSE(loop_.ArtificiallyNotifyEvent(write_fd_, kSocketEventWritable)); |
| } |
| |
| TEST_F(QuicPollEventLoopTest, UnregisterInsideEventHandler) { |
| testing::StrictMock<MockQuicSocketEventListener> listener; |
| ASSERT_TRUE(loop_.RegisterSocket(read_fd_, kAllEvents, &listener)); |
| ASSERT_TRUE(loop_.RegisterSocket(write_fd_, kAllEvents, &listener)); |
| |
| EXPECT_CALL(listener, OnSocketEvent(_, read_fd_, kSocketEventReadable)) |
| .WillOnce([this]() { ASSERT_TRUE(loop_.UnregisterSocket(write_fd_)); }); |
| EXPECT_CALL(listener, OnSocketEvent(_, write_fd_, kSocketEventWritable)) |
| .Times(0); |
| ASSERT_TRUE(loop_.ArtificiallyNotifyEvent(read_fd_, kSocketEventReadable)); |
| loop_.RunEventLoopOnce(QuicTime::Delta::FromMilliseconds(1)); |
| } |
| |
| TEST_F(QuicPollEventLoopTest, EintrHandler) { |
| testing::StrictMock<MockQuicSocketEventListener> listener; |
| ASSERT_TRUE(loop_.RegisterSocket(read_fd_, kAllEvents, &listener)); |
| |
| loop_.TriggerEintrAfter(QuicTime::Delta::FromMilliseconds(25)); |
| loop_.RunEventLoopOnce(QuicTime::Delta::FromMilliseconds(100)); |
| EXPECT_THAT(loop_.timeouts(), ElementsAre(100, 75)); |
| } |
| |
| TEST_F(QuicPollEventLoopTest, AlarmInFuture) { |
| EXPECT_EQ(ComputePollTimeout(), kDefaultTimeout); |
| |
| constexpr auto kAlarmTimeout = QuicTime::Delta::FromMilliseconds(5); |
| auto [alarm, delegate] = CreateAlarm(); |
| EXPECT_EQ(ComputePollTimeout(), kDefaultTimeout); |
| |
| alarm->Set(clock_.Now() + kAlarmTimeout); |
| EXPECT_EQ(ComputePollTimeout(), kAlarmTimeout); |
| |
| EXPECT_CALL(*delegate, OnAlarm()); |
| loop_.RunEventLoopOnce(QuicTime::Delta::FromMilliseconds(100)); |
| EXPECT_EQ(ComputePollTimeout(), kDefaultTimeout); |
| } |
| |
| TEST_F(QuicPollEventLoopTest, AlarmsInPast) { |
| EXPECT_EQ(ComputePollTimeout(), kDefaultTimeout); |
| |
| constexpr auto kAlarmTimeout = QuicTime::Delta::FromMilliseconds(5); |
| auto [alarm1, delegate1] = CreateAlarm(); |
| auto [alarm2, delegate2] = CreateAlarm(); |
| |
| alarm1->Set(clock_.Now() - 2 * kAlarmTimeout); |
| alarm2->Set(clock_.Now() - kAlarmTimeout); |
| |
| { |
| testing::InSequence s; |
| EXPECT_CALL(*delegate1, OnAlarm()); |
| EXPECT_CALL(*delegate2, OnAlarm()); |
| } |
| loop_.RunEventLoopOnce(QuicTime::Delta::FromMilliseconds(100)); |
| } |
| |
| TEST_F(QuicPollEventLoopTest, AlarmCancelled) { |
| EXPECT_EQ(ComputePollTimeout(), kDefaultTimeout); |
| |
| constexpr auto kAlarmTimeout = QuicTime::Delta::FromMilliseconds(5); |
| auto [alarm, delegate] = CreateAlarm(); |
| EXPECT_EQ(ComputePollTimeout(), kDefaultTimeout); |
| |
| alarm->Set(clock_.Now() + kAlarmTimeout); |
| alarm->Cancel(); |
| alarm->Set(clock_.Now() + 2 * kAlarmTimeout); |
| EXPECT_EQ(ComputePollTimeout(), kAlarmTimeout); |
| |
| EXPECT_CALL(*delegate, OnAlarm()); |
| loop_.RunEventLoopOnce(QuicTime::Delta::FromMilliseconds(100)); |
| EXPECT_THAT(loop_.timeouts(), ElementsAre(10)); |
| EXPECT_EQ(ComputePollTimeout(), kDefaultTimeout); |
| } |
| |
| TEST_F(QuicPollEventLoopTest, AlarmCancelsAnotherAlarm) { |
| EXPECT_EQ(ComputePollTimeout(), kDefaultTimeout); |
| |
| constexpr auto kAlarmTimeout = QuicTime::Delta::FromMilliseconds(5); |
| auto [alarm1_ptr, delegate1] = CreateAlarm(); |
| auto [alarm2_ptr, delegate2] = CreateAlarm(); |
| |
| QuicAlarm& alarm1 = *alarm1_ptr; |
| QuicAlarm& alarm2 = *alarm2_ptr; |
| alarm1.Set(clock_.Now() - kAlarmTimeout); |
| alarm2.Set(clock_.Now() - kAlarmTimeout); |
| |
| int alarms_called = 0; |
| // Since the order in which alarms are cancelled is not well-determined, make |
| // each one cancel another. |
| EXPECT_CALL(*delegate1, OnAlarm()).Times(AtMost(1)).WillOnce([&]() { |
| alarm2.Cancel(); |
| ++alarms_called; |
| }); |
| EXPECT_CALL(*delegate2, OnAlarm()).Times(AtMost(1)).WillOnce([&]() { |
| alarm1.Cancel(); |
| ++alarms_called; |
| }); |
| loop_.RunEventLoopOnce(QuicTime::Delta::FromMilliseconds(100)); |
| EXPECT_EQ(alarms_called, 1); |
| EXPECT_EQ(ComputePollTimeout(), kDefaultTimeout); |
| } |
| |
| } // namespace |
| } // namespace quic::test |