|  | // 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/bindings/quic_libevent.h" | 
|  |  | 
|  | #include <memory> | 
|  | #include <utility> | 
|  |  | 
|  | #include "absl/time/time.h" | 
|  | #include "event2/event.h" | 
|  | #include "event2/event_struct.h" | 
|  | #include "event2/thread.h" | 
|  | #include "quiche/quic/core/io/quic_event_loop.h" | 
|  | #include "quiche/quic/core/quic_alarm.h" | 
|  | #include "quiche/quic/core/quic_clock.h" | 
|  | #include "quiche/quic/core/quic_default_clock.h" | 
|  | #include "quiche/quic/core/quic_time.h" | 
|  |  | 
|  | namespace quic { | 
|  |  | 
|  | using LibeventEventMask = short;  // NOLINT(runtime/int) | 
|  |  | 
|  | QuicSocketEventMask LibeventEventMaskToQuicEvents(int events) { | 
|  | return ((events & EV_READ) ? kSocketEventReadable : 0) | | 
|  | ((events & EV_WRITE) ? kSocketEventWritable : 0); | 
|  | } | 
|  |  | 
|  | LibeventEventMask QuicEventsToLibeventEventMask(QuicSocketEventMask events) { | 
|  | return ((events & kSocketEventReadable) ? EV_READ : 0) | | 
|  | ((events & kSocketEventWritable) ? EV_WRITE : 0); | 
|  | } | 
|  |  | 
|  | class LibeventAlarm : public QuicAlarm { | 
|  | public: | 
|  | LibeventAlarm(LibeventQuicEventLoop* loop, | 
|  | QuicArenaScopedPtr<QuicAlarm::Delegate> delegate) | 
|  | : QuicAlarm(std::move(delegate)), clock_(loop->clock()) { | 
|  | event_.reset(evtimer_new( | 
|  | loop->base(), | 
|  | [](evutil_socket_t, LibeventEventMask, void* arg) { | 
|  | LibeventAlarm* self = reinterpret_cast<LibeventAlarm*>(arg); | 
|  | self->Fire(); | 
|  | }, | 
|  | this)); | 
|  | } | 
|  |  | 
|  | protected: | 
|  | void SetImpl() override { | 
|  | absl::Duration timeout = | 
|  | absl::Microseconds((deadline() - clock_->Now()).ToMicroseconds()); | 
|  | timeval unix_time = absl::ToTimeval(timeout); | 
|  | event_add(event_.get(), &unix_time); | 
|  | } | 
|  |  | 
|  | void CancelImpl() override { event_del(event_.get()); } | 
|  |  | 
|  | private: | 
|  | std::unique_ptr<event, LibeventEventDeleter> event_; | 
|  | QuicClock* clock_; | 
|  | }; | 
|  |  | 
|  | LibeventQuicEventLoop::LibeventQuicEventLoop(event_base* base, QuicClock* clock) | 
|  | : base_(base), | 
|  | edge_triggered_(event_base_get_features(base) & EV_FEATURE_ET), | 
|  | clock_(clock), | 
|  | artifical_event_timer_(evtimer_new( | 
|  | base_, | 
|  | [](evutil_socket_t, LibeventEventMask, void* arg) { | 
|  | auto* self = reinterpret_cast<LibeventQuicEventLoop*>(arg); | 
|  | self->ActivateArtificialEvents(); | 
|  | }, | 
|  | this)) { | 
|  | QUICHE_CHECK_LE(sizeof(event), event_get_struct_event_size()) | 
|  | << "libevent ABI mismatch: sizeof(event) is bigger than the one QUICHE " | 
|  | "has been compiled with"; | 
|  | } | 
|  |  | 
|  | LibeventQuicEventLoop::~LibeventQuicEventLoop() { | 
|  | event_del(artifical_event_timer_.get()); | 
|  | } | 
|  |  | 
|  | bool LibeventQuicEventLoop::RegisterSocket(QuicUdpSocketFd fd, | 
|  | QuicSocketEventMask events, | 
|  | QuicSocketEventListener* listener) { | 
|  | auto [it, success] = | 
|  | registration_map_.try_emplace(fd, this, fd, events, listener); | 
|  | return success; | 
|  | } | 
|  |  | 
|  | bool LibeventQuicEventLoop::UnregisterSocket(QuicUdpSocketFd fd) { | 
|  | fds_with_artifical_events_.erase(fd); | 
|  | return registration_map_.erase(fd); | 
|  | } | 
|  |  | 
|  | bool LibeventQuicEventLoop::RearmSocket(QuicUdpSocketFd fd, | 
|  | QuicSocketEventMask events) { | 
|  | if (edge_triggered_) { | 
|  | QUICHE_BUG(LibeventQuicEventLoop_RearmSocket_called_on_ET) | 
|  | << "RearmSocket() called on an edge-triggered event loop"; | 
|  | return false; | 
|  | } | 
|  | auto it = registration_map_.find(fd); | 
|  | if (it == registration_map_.end()) { | 
|  | return false; | 
|  | } | 
|  | it->second.Rearm(events); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool LibeventQuicEventLoop::ArtificiallyNotifyEvent( | 
|  | QuicUdpSocketFd fd, QuicSocketEventMask events) { | 
|  | auto it = registration_map_.find(fd); | 
|  | if (it == registration_map_.end()) { | 
|  | return false; | 
|  | } | 
|  | it->second.RecordArtificalEvents(events); | 
|  | fds_with_artifical_events_.insert(fd); | 
|  | if (!evtimer_pending(artifical_event_timer_.get(), nullptr)) { | 
|  | struct timeval tv = {0, 0};  // Fire immediately in the next iteration. | 
|  | evtimer_add(artifical_event_timer_.get(), &tv); | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | void LibeventQuicEventLoop::ActivateArtificialEvents() { | 
|  | absl::flat_hash_set<QuicUdpSocketFd> fds_with_artifical_events; | 
|  | { | 
|  | using std::swap; | 
|  | swap(fds_with_artifical_events_, fds_with_artifical_events); | 
|  | } | 
|  | for (QuicUdpSocketFd fd : fds_with_artifical_events) { | 
|  | auto it = registration_map_.find(fd); | 
|  | if (it == registration_map_.end()) { | 
|  | continue; | 
|  | } | 
|  | it->second.MaybeNotifyArtificalEvents(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void LibeventQuicEventLoop::RunEventLoopOnce(QuicTime::Delta default_timeout) { | 
|  | timeval timeout = | 
|  | absl::ToTimeval(absl::Microseconds(default_timeout.ToMicroseconds())); | 
|  | event_base_loopexit(base_, &timeout); | 
|  | event_base_loop(base_, EVLOOP_ONCE); | 
|  | } | 
|  |  | 
|  | void LibeventQuicEventLoop::WakeUp() { | 
|  | timeval timeout = absl::ToTimeval(absl::ZeroDuration()); | 
|  | event_base_loopexit(base_, &timeout); | 
|  | } | 
|  |  | 
|  | LibeventQuicEventLoop::Registration::Registration( | 
|  | LibeventQuicEventLoop* loop, QuicUdpSocketFd fd, QuicSocketEventMask events, | 
|  | QuicSocketEventListener* listener) | 
|  | : loop_(loop), listener_(listener) { | 
|  | event_callback_fn callback = [](evutil_socket_t fd, LibeventEventMask events, | 
|  | void* arg) { | 
|  | auto* self = reinterpret_cast<LibeventQuicEventLoop::Registration*>(arg); | 
|  | self->listener_->OnSocketEvent(self->loop_, fd, | 
|  | LibeventEventMaskToQuicEvents(events)); | 
|  | }; | 
|  |  | 
|  | if (loop_->SupportsEdgeTriggered()) { | 
|  | LibeventEventMask mask = | 
|  | QuicEventsToLibeventEventMask(events) | EV_PERSIST | EV_ET; | 
|  | event_assign(&both_events_, loop_->base(), fd, mask, callback, this); | 
|  | event_add(&both_events_, nullptr); | 
|  | } else { | 
|  | event_assign(&read_event_, loop_->base(), fd, EV_READ, callback, this); | 
|  | event_assign(&write_event_, loop_->base(), fd, EV_WRITE, callback, this); | 
|  | Rearm(events); | 
|  | } | 
|  | } | 
|  |  | 
|  | LibeventQuicEventLoop::Registration::~Registration() { | 
|  | if (loop_->SupportsEdgeTriggered()) { | 
|  | event_del(&both_events_); | 
|  | } else { | 
|  | event_del(&read_event_); | 
|  | event_del(&write_event_); | 
|  | } | 
|  | } | 
|  |  | 
|  | void LibeventQuicEventLoop::Registration::RecordArtificalEvents( | 
|  | QuicSocketEventMask events) { | 
|  | artificial_events_ |= events; | 
|  | } | 
|  |  | 
|  | void LibeventQuicEventLoop::Registration::MaybeNotifyArtificalEvents() { | 
|  | if (artificial_events_ == 0) { | 
|  | return; | 
|  | } | 
|  | QuicSocketEventMask events = artificial_events_; | 
|  | artificial_events_ = 0; | 
|  |  | 
|  | if (loop_->SupportsEdgeTriggered()) { | 
|  | event_active(&both_events_, QuicEventsToLibeventEventMask(events), 0); | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (events & kSocketEventReadable) { | 
|  | event_active(&read_event_, EV_READ, 0); | 
|  | } | 
|  | if (events & kSocketEventWritable) { | 
|  | event_active(&write_event_, EV_WRITE, 0); | 
|  | } | 
|  | } | 
|  |  | 
|  | void LibeventQuicEventLoop::Registration::Rearm(QuicSocketEventMask events) { | 
|  | QUICHE_DCHECK(!loop_->SupportsEdgeTriggered()); | 
|  | if (events & kSocketEventReadable) { | 
|  | event_add(&read_event_, nullptr); | 
|  | } | 
|  | if (events & kSocketEventWritable) { | 
|  | event_add(&write_event_, nullptr); | 
|  | } | 
|  | } | 
|  |  | 
|  | QuicAlarm* LibeventQuicEventLoop::AlarmFactory::CreateAlarm( | 
|  | QuicAlarm::Delegate* delegate) { | 
|  | return new LibeventAlarm(loop_, | 
|  | QuicArenaScopedPtr<QuicAlarm::Delegate>(delegate)); | 
|  | } | 
|  |  | 
|  | QuicArenaScopedPtr<QuicAlarm> LibeventQuicEventLoop::AlarmFactory::CreateAlarm( | 
|  | QuicArenaScopedPtr<QuicAlarm::Delegate> delegate, | 
|  | QuicConnectionArena* arena) { | 
|  | if (arena != nullptr) { | 
|  | return arena->New<LibeventAlarm>(loop_, std::move(delegate)); | 
|  | } | 
|  | return QuicArenaScopedPtr<QuicAlarm>( | 
|  | new LibeventAlarm(loop_, std::move(delegate))); | 
|  | } | 
|  |  | 
|  | QuicLibeventEventLoopFactory::QuicLibeventEventLoopFactory( | 
|  | bool force_level_triggered) | 
|  | : force_level_triggered_(force_level_triggered) { | 
|  | std::unique_ptr<QuicEventLoop> event_loop = Create(QuicDefaultClock::Get()); | 
|  | name_ = absl::StrFormat( | 
|  | "libevent(%s)", | 
|  | event_base_get_method( | 
|  | static_cast<LibeventQuicEventLoopWithOwnership*>(event_loop.get()) | 
|  | ->base())); | 
|  | } | 
|  |  | 
|  | struct LibeventConfigDeleter { | 
|  | void operator()(event_config* config) { event_config_free(config); } | 
|  | }; | 
|  |  | 
|  | std::unique_ptr<LibeventQuicEventLoopWithOwnership> | 
|  | LibeventQuicEventLoopWithOwnership::Create(QuicClock* clock, | 
|  | bool force_level_triggered) { | 
|  | // Required for event_base_loopbreak() to actually work. | 
|  | static int threads_initialized = []() { | 
|  | #ifdef _WIN32 | 
|  | return evthread_use_windows_threads(); | 
|  | #else | 
|  | return evthread_use_pthreads(); | 
|  | #endif | 
|  | }(); | 
|  | QUICHE_DCHECK_EQ(threads_initialized, 0); | 
|  |  | 
|  | std::unique_ptr<event_config, LibeventConfigDeleter> config( | 
|  | event_config_new()); | 
|  | if (force_level_triggered) { | 
|  | // epoll and kqueue are the two only current libevent backends that support | 
|  | // edge-triggered I/O. | 
|  | event_config_avoid_method(config.get(), "epoll"); | 
|  | event_config_avoid_method(config.get(), "kqueue"); | 
|  | } | 
|  | return std::make_unique<LibeventQuicEventLoopWithOwnership>( | 
|  | event_base_new_with_config(config.get()), clock); | 
|  | } | 
|  |  | 
|  | }  // namespace quic |