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