| // 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 "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: | 
 |   // While we inline `struct event` elsewhere, it is actually quite large, so | 
 |   // doing that for the libevent-based QuicAlarm would cause it to not fit into | 
 |   // the QuicConnectionArena. | 
 |   struct EventDeleter { | 
 |     void operator()(event* ev) { event_free(ev); } | 
 |   }; | 
 |   std::unique_ptr<event, EventDeleter> event_; | 
 |   QuicClock* clock_; | 
 | }; | 
 |  | 
 | LibeventQuicEventLoop::LibeventQuicEventLoop(event_base* base, QuicClock* clock) | 
 |     : base_(base), | 
 |       edge_triggered_(event_base_get_features(base) & EV_FEATURE_ET), | 
 |       clock_(clock) { | 
 |   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"; | 
 | } | 
 |  | 
 | 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) { | 
 |   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.ArtificiallyNotify(events); | 
 |   return true; | 
 | } | 
 |  | 
 | 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() { event_base_loopbreak(base_); } | 
 |  | 
 | 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::ArtificiallyNotify( | 
 |     QuicSocketEventMask events) { | 
 |   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 |