xref: /aosp_15_r20/external/cronet/net/third_party/quiche/src/quiche/quic/bindings/quic_libevent.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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 #include "quiche/quic/bindings/quic_libevent.h"
6 
7 #include <memory>
8 
9 #include "absl/time/time.h"
10 #include "event2/event.h"
11 #include "event2/event_struct.h"
12 #include "event2/thread.h"
13 #include "quiche/quic/core/io/quic_event_loop.h"
14 #include "quiche/quic/core/quic_alarm.h"
15 #include "quiche/quic/core/quic_clock.h"
16 #include "quiche/quic/core/quic_default_clock.h"
17 #include "quiche/quic/core/quic_time.h"
18 
19 namespace quic {
20 
21 using LibeventEventMask = short;  // NOLINT(runtime/int)
22 
LibeventEventMaskToQuicEvents(int events)23 QuicSocketEventMask LibeventEventMaskToQuicEvents(int events) {
24   return ((events & EV_READ) ? kSocketEventReadable : 0) |
25          ((events & EV_WRITE) ? kSocketEventWritable : 0);
26 }
27 
QuicEventsToLibeventEventMask(QuicSocketEventMask events)28 LibeventEventMask QuicEventsToLibeventEventMask(QuicSocketEventMask events) {
29   return ((events & kSocketEventReadable) ? EV_READ : 0) |
30          ((events & kSocketEventWritable) ? EV_WRITE : 0);
31 }
32 
33 class LibeventAlarm : public QuicAlarm {
34  public:
LibeventAlarm(LibeventQuicEventLoop * loop,QuicArenaScopedPtr<QuicAlarm::Delegate> delegate)35   LibeventAlarm(LibeventQuicEventLoop* loop,
36                 QuicArenaScopedPtr<QuicAlarm::Delegate> delegate)
37       : QuicAlarm(std::move(delegate)), clock_(loop->clock()) {
38     event_.reset(evtimer_new(
39         loop->base(),
40         [](evutil_socket_t, LibeventEventMask, void* arg) {
41           LibeventAlarm* self = reinterpret_cast<LibeventAlarm*>(arg);
42           self->Fire();
43         },
44         this));
45   }
46 
47  protected:
SetImpl()48   void SetImpl() override {
49     absl::Duration timeout =
50         absl::Microseconds((deadline() - clock_->Now()).ToMicroseconds());
51     timeval unix_time = absl::ToTimeval(timeout);
52     event_add(event_.get(), &unix_time);
53   }
54 
CancelImpl()55   void CancelImpl() override { event_del(event_.get()); }
56 
57  private:
58   // While we inline `struct event` elsewhere, it is actually quite large, so
59   // doing that for the libevent-based QuicAlarm would cause it to not fit into
60   // the QuicConnectionArena.
61   struct EventDeleter {
operator ()quic::LibeventAlarm::EventDeleter62     void operator()(event* ev) { event_free(ev); }
63   };
64   std::unique_ptr<event, EventDeleter> event_;
65   QuicClock* clock_;
66 };
67 
LibeventQuicEventLoop(event_base * base,QuicClock * clock)68 LibeventQuicEventLoop::LibeventQuicEventLoop(event_base* base, QuicClock* clock)
69     : base_(base),
70       edge_triggered_(event_base_get_features(base) & EV_FEATURE_ET),
71       clock_(clock) {
72   QUICHE_CHECK_LE(sizeof(event), event_get_struct_event_size())
73       << "libevent ABI mismatch: sizeof(event) is bigger than the one QUICHE "
74          "has been compiled with";
75 }
76 
RegisterSocket(QuicUdpSocketFd fd,QuicSocketEventMask events,QuicSocketEventListener * listener)77 bool LibeventQuicEventLoop::RegisterSocket(QuicUdpSocketFd fd,
78                                            QuicSocketEventMask events,
79                                            QuicSocketEventListener* listener) {
80   auto [it, success] =
81       registration_map_.try_emplace(fd, this, fd, events, listener);
82   return success;
83 }
84 
UnregisterSocket(QuicUdpSocketFd fd)85 bool LibeventQuicEventLoop::UnregisterSocket(QuicUdpSocketFd fd) {
86   return registration_map_.erase(fd);
87 }
88 
RearmSocket(QuicUdpSocketFd fd,QuicSocketEventMask events)89 bool LibeventQuicEventLoop::RearmSocket(QuicUdpSocketFd fd,
90                                         QuicSocketEventMask events) {
91   if (edge_triggered_) {
92     QUICHE_BUG(LibeventQuicEventLoop_RearmSocket_called_on_ET)
93         << "RearmSocket() called on an edge-triggered event loop";
94     return false;
95   }
96   auto it = registration_map_.find(fd);
97   if (it == registration_map_.end()) {
98     return false;
99   }
100   it->second.Rearm(events);
101   return true;
102 }
103 
ArtificiallyNotifyEvent(QuicUdpSocketFd fd,QuicSocketEventMask events)104 bool LibeventQuicEventLoop::ArtificiallyNotifyEvent(
105     QuicUdpSocketFd fd, QuicSocketEventMask events) {
106   auto it = registration_map_.find(fd);
107   if (it == registration_map_.end()) {
108     return false;
109   }
110   it->second.ArtificiallyNotify(events);
111   return true;
112 }
113 
RunEventLoopOnce(QuicTime::Delta default_timeout)114 void LibeventQuicEventLoop::RunEventLoopOnce(QuicTime::Delta default_timeout) {
115   timeval timeout =
116       absl::ToTimeval(absl::Microseconds(default_timeout.ToMicroseconds()));
117   event_base_loopexit(base_, &timeout);
118   event_base_loop(base_, EVLOOP_ONCE);
119 }
120 
WakeUp()121 void LibeventQuicEventLoop::WakeUp() {
122   timeval timeout = absl::ToTimeval(absl::ZeroDuration());
123   event_base_loopexit(base_, &timeout);
124 }
125 
Registration(LibeventQuicEventLoop * loop,QuicUdpSocketFd fd,QuicSocketEventMask events,QuicSocketEventListener * listener)126 LibeventQuicEventLoop::Registration::Registration(
127     LibeventQuicEventLoop* loop, QuicUdpSocketFd fd, QuicSocketEventMask events,
128     QuicSocketEventListener* listener)
129     : loop_(loop), listener_(listener) {
130   event_callback_fn callback = [](evutil_socket_t fd, LibeventEventMask events,
131                                   void* arg) {
132     auto* self = reinterpret_cast<LibeventQuicEventLoop::Registration*>(arg);
133     self->listener_->OnSocketEvent(self->loop_, fd,
134                                    LibeventEventMaskToQuicEvents(events));
135   };
136 
137   if (loop_->SupportsEdgeTriggered()) {
138     LibeventEventMask mask =
139         QuicEventsToLibeventEventMask(events) | EV_PERSIST | EV_ET;
140     event_assign(&both_events_, loop_->base(), fd, mask, callback, this);
141     event_add(&both_events_, nullptr);
142   } else {
143     event_assign(&read_event_, loop_->base(), fd, EV_READ, callback, this);
144     event_assign(&write_event_, loop_->base(), fd, EV_WRITE, callback, this);
145     Rearm(events);
146   }
147 }
148 
~Registration()149 LibeventQuicEventLoop::Registration::~Registration() {
150   if (loop_->SupportsEdgeTriggered()) {
151     event_del(&both_events_);
152   } else {
153     event_del(&read_event_);
154     event_del(&write_event_);
155   }
156 }
157 
ArtificiallyNotify(QuicSocketEventMask events)158 void LibeventQuicEventLoop::Registration::ArtificiallyNotify(
159     QuicSocketEventMask events) {
160   if (loop_->SupportsEdgeTriggered()) {
161     event_active(&both_events_, QuicEventsToLibeventEventMask(events), 0);
162     return;
163   }
164 
165   if (events & kSocketEventReadable) {
166     event_active(&read_event_, EV_READ, 0);
167   }
168   if (events & kSocketEventWritable) {
169     event_active(&write_event_, EV_WRITE, 0);
170   }
171 }
172 
Rearm(QuicSocketEventMask events)173 void LibeventQuicEventLoop::Registration::Rearm(QuicSocketEventMask events) {
174   QUICHE_DCHECK(!loop_->SupportsEdgeTriggered());
175   if (events & kSocketEventReadable) {
176     event_add(&read_event_, nullptr);
177   }
178   if (events & kSocketEventWritable) {
179     event_add(&write_event_, nullptr);
180   }
181 }
182 
CreateAlarm(QuicAlarm::Delegate * delegate)183 QuicAlarm* LibeventQuicEventLoop::AlarmFactory::CreateAlarm(
184     QuicAlarm::Delegate* delegate) {
185   return new LibeventAlarm(loop_,
186                            QuicArenaScopedPtr<QuicAlarm::Delegate>(delegate));
187 }
188 
CreateAlarm(QuicArenaScopedPtr<QuicAlarm::Delegate> delegate,QuicConnectionArena * arena)189 QuicArenaScopedPtr<QuicAlarm> LibeventQuicEventLoop::AlarmFactory::CreateAlarm(
190     QuicArenaScopedPtr<QuicAlarm::Delegate> delegate,
191     QuicConnectionArena* arena) {
192   if (arena != nullptr) {
193     return arena->New<LibeventAlarm>(loop_, std::move(delegate));
194   }
195   return QuicArenaScopedPtr<QuicAlarm>(
196       new LibeventAlarm(loop_, std::move(delegate)));
197 }
198 
QuicLibeventEventLoopFactory(bool force_level_triggered)199 QuicLibeventEventLoopFactory::QuicLibeventEventLoopFactory(
200     bool force_level_triggered)
201     : force_level_triggered_(force_level_triggered) {
202   std::unique_ptr<QuicEventLoop> event_loop = Create(QuicDefaultClock::Get());
203   name_ = absl::StrFormat(
204       "libevent(%s)",
205       event_base_get_method(
206           static_cast<LibeventQuicEventLoopWithOwnership*>(event_loop.get())
207               ->base()));
208 }
209 
210 struct LibeventConfigDeleter {
operator ()quic::LibeventConfigDeleter211   void operator()(event_config* config) { event_config_free(config); }
212 };
213 
214 std::unique_ptr<LibeventQuicEventLoopWithOwnership>
Create(QuicClock * clock,bool force_level_triggered)215 LibeventQuicEventLoopWithOwnership::Create(QuicClock* clock,
216                                            bool force_level_triggered) {
217   // Required for event_base_loopbreak() to actually work.
218   static int threads_initialized = []() {
219 #ifdef _WIN32
220     return evthread_use_windows_threads();
221 #else
222     return evthread_use_pthreads();
223 #endif
224   }();
225   QUICHE_DCHECK_EQ(threads_initialized, 0);
226 
227   std::unique_ptr<event_config, LibeventConfigDeleter> config(
228       event_config_new());
229   if (force_level_triggered) {
230     // epoll and kqueue are the two only current libevent backends that support
231     // edge-triggered I/O.
232     event_config_avoid_method(config.get(), "epoll");
233     event_config_avoid_method(config.get(), "kqueue");
234   }
235   return std::make_unique<LibeventQuicEventLoopWithOwnership>(
236       event_base_new_with_config(config.get()), clock);
237 }
238 
239 }  // namespace quic
240