1 // Copyright 2022 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef QUICHE_QUIC_CORE_IO_QUIC_POLL_EVENT_LOOP_H_ 6 #define QUICHE_QUIC_CORE_IO_QUIC_POLL_EVENT_LOOP_H_ 7 8 #include <poll.h> 9 10 #include <memory> 11 12 #include "absl/container/btree_map.h" 13 #include "absl/types/span.h" 14 #include "quiche/quic/core/io/quic_event_loop.h" 15 #include "quiche/quic/core/quic_alarm.h" 16 #include "quiche/quic/core/quic_alarm_factory.h" 17 #include "quiche/quic/core/quic_clock.h" 18 #include "quiche/quic/core/quic_udp_socket.h" 19 #include "quiche/common/quiche_linked_hash_map.h" 20 21 namespace quic { 22 23 // A simple and portable implementation of QuicEventLoop using poll(2). Works 24 // on all POSIX platforms (and can be potentially made to support Windows using 25 // WSAPoll). 26 // 27 // For most operations, this implementation has a typical runtime of 28 // O(N + log M), where N is the number of file descriptors, and M is the number 29 // of pending alarms. 30 // 31 // This API has to deal with the situations where callbacks are modified from 32 // the callbacks themselves. To address this, we use the following two 33 // approaches: 34 // 1. The code does not execute any callbacks until the very end of the 35 // processing, when all of the state for the event loop is consistent. 36 // 2. The callbacks are stored as weak pointers, since other callbacks can 37 // cause them to be unregistered. 38 class QUICHE_NO_EXPORT QuicPollEventLoop : public QuicEventLoop { 39 public: 40 QuicPollEventLoop(QuicClock* clock); 41 42 // QuicEventLoop implementation. SupportsEdgeTriggered()43 bool SupportsEdgeTriggered() const override { return false; } 44 ABSL_MUST_USE_RESULT bool RegisterSocket( 45 QuicUdpSocketFd fd, QuicSocketEventMask events, 46 QuicSocketEventListener* listener) override; 47 ABSL_MUST_USE_RESULT bool UnregisterSocket(QuicUdpSocketFd fd) override; 48 ABSL_MUST_USE_RESULT bool RearmSocket(QuicUdpSocketFd fd, 49 QuicSocketEventMask events) override; 50 ABSL_MUST_USE_RESULT bool ArtificiallyNotifyEvent( 51 QuicUdpSocketFd fd, QuicSocketEventMask events) override; 52 void RunEventLoopOnce(QuicTime::Delta default_timeout) override; 53 std::unique_ptr<QuicAlarmFactory> CreateAlarmFactory() override; GetClock()54 const QuicClock* GetClock() override { return clock_; } 55 56 protected: 57 // Allows poll(2) calls to be mocked out in unit tests. PollSyscall(pollfd * fds,nfds_t nfds,int timeout)58 virtual int PollSyscall(pollfd* fds, nfds_t nfds, int timeout) { 59 return ::poll(fds, nfds, timeout); 60 } 61 62 private: 63 friend class QuicPollEventLoopPeer; 64 65 struct Registration { 66 QuicSocketEventMask events = 0; 67 QuicSocketEventListener* listener; 68 69 QuicSocketEventMask artificially_notify_at_next_iteration = 0; 70 }; 71 72 class Alarm : public QuicAlarm { 73 public: 74 Alarm(QuicPollEventLoop* loop, 75 QuicArenaScopedPtr<QuicAlarm::Delegate> delegate); 76 77 void SetImpl() override; 78 void CancelImpl() override; 79 DoFire()80 void DoFire() { 81 current_schedule_handle_.reset(); 82 Fire(); 83 } 84 85 private: 86 QuicPollEventLoop* loop_; 87 // Deleted when the alarm is cancelled, causing the corresponding weak_ptr 88 // in the alarm list to not be executed. 89 std::shared_ptr<Alarm*> current_schedule_handle_; 90 }; 91 92 class AlarmFactory : public QuicAlarmFactory { 93 public: AlarmFactory(QuicPollEventLoop * loop)94 AlarmFactory(QuicPollEventLoop* loop) : loop_(loop) {} 95 96 // QuicAlarmFactory implementation. 97 QuicAlarm* CreateAlarm(QuicAlarm::Delegate* delegate) override; 98 QuicArenaScopedPtr<QuicAlarm> CreateAlarm( 99 QuicArenaScopedPtr<QuicAlarm::Delegate> delegate, 100 QuicConnectionArena* arena) override; 101 102 private: 103 QuicPollEventLoop* loop_; 104 }; 105 106 // Used for deferred execution of I/O callbacks. 107 struct ReadyListEntry { 108 QuicUdpSocketFd fd; 109 std::weak_ptr<Registration> registration; 110 QuicSocketEventMask events; 111 }; 112 113 // We're using a linked hash map here to ensure the events are called in the 114 // registration order. This isn't strictly speaking necessary, but makes 115 // testing things easier. 116 using RegistrationMap = 117 quiche::QuicheLinkedHashMap<QuicUdpSocketFd, 118 std::shared_ptr<Registration>>; 119 // Alarms are stored as weak pointers, since the alarm can be cancelled and 120 // disappear while in the queue. 121 using AlarmList = absl::btree_multimap<QuicTime, std::weak_ptr<Alarm*>>; 122 123 // Returns the timeout for the next poll(2) call. It is typically the time at 124 // which the next alarm is supposed to activate. 125 QuicTime::Delta ComputePollTimeout(QuicTime now, 126 QuicTime::Delta default_timeout) const; 127 // Calls poll(2) with the provided timeout and dispatches the callbacks 128 // accordingly. 129 void ProcessIoEvents(QuicTime start_time, QuicTime::Delta timeout); 130 // Calls all of the alarm callbacks that are scheduled before or at |time|. 131 void ProcessAlarmsUpTo(QuicTime time); 132 133 // Adds the I/O callbacks for |fd| to the |ready_lits| as appopriate. 134 void DispatchIoEvent(std::vector<ReadyListEntry>& ready_list, 135 QuicUdpSocketFd fd, short mask); // NOLINT(runtime/int) 136 // Runs all of the callbacks on the ready list. 137 void RunReadyCallbacks(std::vector<ReadyListEntry>& ready_list); 138 139 // Calls poll() while handling EINTR. Returns the return value of poll(2) 140 // system call. 141 int PollWithRetries(absl::Span<pollfd> fds, QuicTime start_time, 142 QuicTime::Delta timeout); 143 144 const QuicClock* clock_; 145 RegistrationMap registrations_; 146 AlarmList alarms_; 147 bool has_artificial_events_pending_ = false; 148 }; 149 150 class QUICHE_NO_EXPORT QuicPollEventLoopFactory : public QuicEventLoopFactory { 151 public: Get()152 static QuicPollEventLoopFactory* Get() { 153 static QuicPollEventLoopFactory* factory = new QuicPollEventLoopFactory(); 154 return factory; 155 } 156 Create(QuicClock * clock)157 std::unique_ptr<QuicEventLoop> Create(QuicClock* clock) override { 158 return std::make_unique<QuicPollEventLoop>(clock); 159 } 160 GetName()161 std::string GetName() const override { return "poll(2)"; } 162 }; 163 164 } // namespace quic 165 166 #endif // QUICHE_QUIC_CORE_IO_QUIC_POLL_EVENT_LOOP_H_ 167