Switch QuicServer to use QuicEventLoop instead of QuicEpollServer

This allows QuicServer to work on all platforms supported by QuicEventLoop, instead of being Linux-only.

PiperOrigin-RevId: 460286930
diff --git a/quiche/quic/bindings/quic_libevent.cc b/quiche/quic/bindings/quic_libevent.cc
index 668f441..996d70b 100644
--- a/quiche/quic/bindings/quic_libevent.cc
+++ b/quiche/quic/bindings/quic_libevent.cc
@@ -35,30 +35,33 @@
   LibeventAlarm(LibeventQuicEventLoop* loop,
                 QuicArenaScopedPtr<QuicAlarm::Delegate> delegate)
       : QuicAlarm(std::move(delegate)), clock_(loop->clock()) {
-    evtimer_assign(
-        &event_, loop->base(),
+    event_.reset(evtimer_new(
+        loop->base(),
         [](evutil_socket_t, LibeventEventMask, void* arg) {
           LibeventAlarm* self = reinterpret_cast<LibeventAlarm*>(arg);
           self->Fire();
         },
-        this);
+        this));
   }
-  ~LibeventAlarm() { event_del(&event_); }
 
  protected:
   void SetImpl() override {
     absl::Duration timeout =
         absl::Microseconds((deadline() - clock_->Now()).ToMicroseconds());
     timeval unix_time = absl::ToTimeval(timeout);
-    event_add(&event_, &unix_time);
+    event_add(event_.get(), &unix_time);
   }
 
-  void CancelImpl() override { event_del(&event_); }
+  void CancelImpl() override { event_del(event_.get()); }
 
  private:
-  // While this decreases ABI portability, we use event inline, rather than
-  // allocating it with event_new(), since this improves cache locality.
-  event event_;
+  // 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_;
 };
 
@@ -205,8 +208,9 @@
   void operator()(event_config* config) { event_config_free(config); }
 };
 
-std::unique_ptr<QuicEventLoop> QuicLibeventEventLoopFactory::Create(
-    QuicClock* clock) {
+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
@@ -219,7 +223,7 @@
 
   std::unique_ptr<event_config, LibeventConfigDeleter> config(
       event_config_new());
-  if (force_level_triggered_) {
+  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");
diff --git a/quiche/quic/bindings/quic_libevent.h b/quiche/quic/bindings/quic_libevent.h
index 5eabb45..d8aa493 100644
--- a/quiche/quic/bindings/quic_libevent.h
+++ b/quiche/quic/bindings/quic_libevent.h
@@ -109,6 +109,9 @@
     : public LibeventLoop,
       public LibeventQuicEventLoop {
  public:
+  static std::unique_ptr<LibeventQuicEventLoopWithOwnership> Create(
+      QuicClock* clock, bool force_level_triggered = false);
+
   // Takes ownership of |base|.
   explicit LibeventQuicEventLoopWithOwnership(struct event_base* base,
                                               QuicClock* clock)
@@ -134,7 +137,10 @@
     return factory;
   }
 
-  std::unique_ptr<QuicEventLoop> Create(QuicClock* clock) override;
+  std::unique_ptr<QuicEventLoop> Create(QuicClock* clock) override {
+    return LibeventQuicEventLoopWithOwnership::Create(clock,
+                                                      force_level_triggered_);
+  }
   std::string GetName() const override { return name_; }
 
  private:
diff --git a/quiche/quic/core/http/end_to_end_test.cc b/quiche/quic/core/http/end_to_end_test.cc
index c1c46f0..8b7add5 100644
--- a/quiche/quic/core/http/end_to_end_test.cc
+++ b/quiche/quic/core/http/end_to_end_test.cc
@@ -21,6 +21,8 @@
 #include "quiche/quic/core/http/http_constants.h"
 #include "quiche/quic/core/http/quic_spdy_client_stream.h"
 #include "quiche/quic/core/http/web_transport_http3.h"
+#include "quiche/quic/core/io/quic_default_event_loop.h"
+#include "quiche/quic/core/io/quic_event_loop.h"
 #include "quiche/quic/core/quic_connection.h"
 #include "quiche/quic/core/quic_constants.h"
 #include "quiche/quic/core/quic_data_writer.h"
@@ -98,9 +100,11 @@
 // Run all tests with the cross products of all versions.
 struct TestParams {
   TestParams(const ParsedQuicVersion& version, QuicTag congestion_control_tag,
+             QuicEventLoopFactory* event_loop,
              int override_server_connection_id_length)
       : version(version),
         congestion_control_tag(congestion_control_tag),
+        event_loop(event_loop),
         override_server_connection_id_length(
             override_server_connection_id_length) {}
 
@@ -108,6 +112,7 @@
     os << "{ version: " << ParsedQuicVersionToString(p.version);
     os << " congestion_control_tag: "
        << QuicTagToString(p.congestion_control_tag)
+       << " event loop: " << p.event_loop->GetName()
        << " connection ID length: " << p.override_server_connection_id_length
        << " }";
     return os;
@@ -115,6 +120,7 @@
 
   ParsedQuicVersion version;
   QuicTag congestion_control_tag;
+  QuicEventLoopFactory* event_loop;
   int override_server_connection_id_length;
 };
 
@@ -122,7 +128,8 @@
 std::string PrintToString(const TestParams& p) {
   std::string rv = absl::StrCat(
       ParsedQuicVersionToString(p.version), "_",
-      QuicTagToString(p.congestion_control_tag), "_",
+      QuicTagToString(p.congestion_control_tag), "_", p.event_loop->GetName(),
+      "_",
       std::to_string((p.override_server_connection_id_length == -1)
                          ? static_cast<int>(kQuicDefaultConnectionIdLength)
                          : p.override_server_connection_id_length));
@@ -148,12 +155,22 @@
         // qQUIC as well.
         if (connection_id_length == -1 || version.UsesTls()) {
           params.push_back(TestParams(version, congestion_control_tag,
+                                      GetDefaultEventLoop(),
                                       connection_id_length));
         }
       }  // End of outer version loop.
     }    // End of congestion_control_tag loop.
   }      // End of connection_id_length loop.
 
+  // Only run every event loop implementation for one fixed configuration.
+  for (QuicEventLoopFactory* event_loop : GetAllSupportedEventLoops()) {
+    if (event_loop == GetDefaultEventLoop()) {
+      continue;
+    }
+    params.push_back(
+        TestParams(ParsedQuicVersion::RFCv1(), kTBBR, event_loop, -1));
+  }
+
   return params;
 }
 
diff --git a/quiche/quic/core/io/quic_default_event_loop.h b/quiche/quic/core/io/quic_default_event_loop.h
index 6315b76..6073a6e 100644
--- a/quiche/quic/core/io/quic_default_event_loop.h
+++ b/quiche/quic/core/io/quic_default_event_loop.h
@@ -8,14 +8,13 @@
 #include <memory>
 
 #include "quiche/quic/core/io/quic_event_loop.h"
-#include "quiche/quic/core/quic_clock.h"
 
 namespace quic {
 
 // Returns the default implementation of QuicheEventLoop.  The embedders can
 // override this using the platform API.  The factory pointer returned is an
 // unowned static variable.
-QUICHE_NO_EXPORT QuicEventLoopFactory* GetDefaultEventLoop(QuicClock* clock);
+QUICHE_NO_EXPORT QuicEventLoopFactory* GetDefaultEventLoop();
 
 // Returns the factory objects for all event loops.  This is particularly useful
 // for the unit tests.  The factory pointers returned are unowned static
diff --git a/quiche/quic/masque/masque_dispatcher.cc b/quiche/quic/masque/masque_dispatcher.cc
index d0440a6..32776ee 100644
--- a/quiche/quic/masque/masque_dispatcher.cc
+++ b/quiche/quic/masque/masque_dispatcher.cc
@@ -11,7 +11,7 @@
 MasqueDispatcher::MasqueDispatcher(
     MasqueMode masque_mode, const QuicConfig* config,
     const QuicCryptoServerConfig* crypto_config,
-    QuicVersionManager* version_manager, QuicEpollServer* epoll_server,
+    QuicVersionManager* version_manager, QuicEventLoop* event_loop,
     std::unique_ptr<QuicConnectionHelperInterface> helper,
     std::unique_ptr<QuicCryptoServerStreamBase::Helper> session_helper,
     std::unique_ptr<QuicAlarmFactory> alarm_factory,
@@ -22,7 +22,7 @@
                            std::move(alarm_factory), masque_server_backend,
                            expected_server_connection_id_length),
       masque_mode_(masque_mode),
-      epoll_server_(epoll_server),
+      event_loop_(event_loop),
       masque_server_backend_(masque_server_backend) {}
 
 std::unique_ptr<QuicSession> MasqueDispatcher::CreateQuicSession(
@@ -39,8 +39,8 @@
 
   auto session = std::make_unique<MasqueServerSession>(
       masque_mode_, config(), GetSupportedVersions(), connection, this,
-      epoll_server_, session_helper(), crypto_config(),
-      compressed_certs_cache(), masque_server_backend_);
+      event_loop_, session_helper(), crypto_config(), compressed_certs_cache(),
+      masque_server_backend_);
   session->Initialize();
   return session;
 }
diff --git a/quiche/quic/masque/masque_dispatcher.h b/quiche/quic/masque/masque_dispatcher.h
index b5ef5bb..804d2e9 100644
--- a/quiche/quic/masque/masque_dispatcher.h
+++ b/quiche/quic/masque/masque_dispatcher.h
@@ -6,10 +6,10 @@
 #define QUICHE_QUIC_MASQUE_MASQUE_DISPATCHER_H_
 
 #include "absl/container/flat_hash_map.h"
+#include "quiche/quic/core/io/quic_event_loop.h"
 #include "quiche/quic/masque/masque_server_backend.h"
 #include "quiche/quic/masque/masque_server_session.h"
 #include "quiche/quic/masque/masque_utils.h"
-#include "quiche/quic/platform/api/quic_epoll.h"
 #include "quiche/quic/platform/api/quic_export.h"
 #include "quiche/quic/tools/quic_simple_dispatcher.h"
 
@@ -22,7 +22,7 @@
   explicit MasqueDispatcher(
       MasqueMode masque_mode, const QuicConfig* config,
       const QuicCryptoServerConfig* crypto_config,
-      QuicVersionManager* version_manager, QuicEpollServer* epoll_server,
+      QuicVersionManager* version_manager, QuicEventLoop* event_loop,
       std::unique_ptr<QuicConnectionHelperInterface> helper,
       std::unique_ptr<QuicCryptoServerStreamBase::Helper> session_helper,
       std::unique_ptr<QuicAlarmFactory> alarm_factory,
@@ -42,7 +42,7 @@
 
  private:
   MasqueMode masque_mode_;
-  QuicEpollServer* epoll_server_;               // Unowned.
+  QuicEventLoop* event_loop_;                   // Unowned.
   MasqueServerBackend* masque_server_backend_;  // Unowned.
 };
 
diff --git a/quiche/quic/masque/masque_epoll_server.cc b/quiche/quic/masque/masque_epoll_server.cc
index 1efc5e1..a67c507 100644
--- a/quiche/quic/masque/masque_epoll_server.cc
+++ b/quiche/quic/masque/masque_epoll_server.cc
@@ -4,7 +4,7 @@
 
 #include "quiche/quic/masque/masque_epoll_server.h"
 
-#include "quiche/quic/core/quic_epoll_alarm_factory.h"
+#include "quiche/quic/core/quic_default_connection_helper.h"
 #include "quiche/quic/masque/masque_dispatcher.h"
 #include "quiche/quic/masque/masque_utils.h"
 #include "quiche/quic/platform/api/quic_default_proof_providers.h"
@@ -22,12 +22,10 @@
 QuicDispatcher* MasqueEpollServer::CreateQuicDispatcher() {
   return new MasqueDispatcher(
       masque_mode_, &config(), &crypto_config(), version_manager(),
-      epoll_server(),
-      std::make_unique<QuicEpollConnectionHelper>(epoll_server(),
-                                                  QuicAllocator::BUFFER_POOL),
+      event_loop(), std::make_unique<QuicDefaultConnectionHelper>(),
       std::make_unique<QuicSimpleCryptoServerStreamHelper>(),
-      std::make_unique<QuicEpollAlarmFactory>(epoll_server()),
-      masque_server_backend_, expected_server_connection_id_length());
+      event_loop()->CreateAlarmFactory(), masque_server_backend_,
+      expected_server_connection_id_length());
 }
 
 }  // namespace quic
diff --git a/quiche/quic/masque/masque_server_session.cc b/quiche/quic/masque/masque_server_session.cc
index 742b0c6..03fc326 100644
--- a/quiche/quic/masque/masque_server_session.cc
+++ b/quiche/quic/masque/masque_server_session.cc
@@ -9,11 +9,13 @@
 #include <cstddef>
 #include <limits>
 
+#include "absl/cleanup/cleanup.h"
 #include "absl/strings/str_cat.h"
 #include "absl/strings/str_split.h"
 #include "absl/strings/string_view.h"
 #include "absl/types/optional.h"
 #include "quiche/quic/core/http/spdy_utils.h"
+#include "quiche/quic/core/io/quic_event_loop.h"
 #include "quiche/quic/core/quic_data_reader.h"
 #include "quiche/quic/core/quic_udp_socket.h"
 #include "quiche/quic/tools/quic_url.h"
@@ -79,7 +81,7 @@
     MasqueMode masque_mode, const QuicConfig& config,
     const ParsedQuicVersionVector& supported_versions,
     QuicConnection* connection, QuicSession::Visitor* visitor,
-    QuicEpollServer* epoll_server, QuicCryptoServerStreamBase::Helper* helper,
+    QuicEventLoop* event_loop, QuicCryptoServerStreamBase::Helper* helper,
     const QuicCryptoServerConfig* crypto_config,
     QuicCompressedCertsCache* compressed_certs_cache,
     MasqueServerBackend* masque_server_backend)
@@ -87,7 +89,7 @@
                               helper, crypto_config, compressed_certs_cache,
                               masque_server_backend),
       masque_server_backend_(masque_server_backend),
-      epoll_server_(epoll_server),
+      event_loop_(event_loop),
       masque_mode_(masque_mode) {
   // Artificially increase the max packet length to 1350 to ensure we can fit
   // QUIC packets inside DATAGRAM frames.
@@ -95,7 +97,7 @@
   connection->SetMaxPacketLength(kDefaultMaxPacketSize);
 
   masque_server_backend_->RegisterBackendClient(connection_id(), this);
-  QUICHE_DCHECK_NE(epoll_server_, nullptr);
+  QUICHE_DCHECK_NE(event_loop_, nullptr);
 }
 
 void MasqueServerSession::OnMessageAcked(QuicMessageId message_id,
@@ -229,7 +231,11 @@
     QUIC_DLOG(ERROR) << "Socket bind failed";
     return CreateBackendErrorResponse("500", "Socket bind failed");
   }
-  epoll_server_->RegisterFDForRead(fd_wrapper.fd(), this);
+  if (!event_loop_->RegisterSocket(fd_wrapper.fd(), kSocketEventReadable,
+                                   this)) {
+    QUIC_DLOG(ERROR) << "Failed to register socket with the event loop";
+    return CreateBackendErrorResponse("500", "Registering socket failed");
+  }
 
   QuicSpdyStream* stream =
       static_cast<QuicSpdyStream*>(GetActiveStream(request_handler->stream_id()));
@@ -252,19 +258,11 @@
   return response;
 }
 
-void MasqueServerSession::OnRegistration(QuicEpollServer* /*eps*/,
-                                         QuicUdpSocketFd fd, int event_mask) {
-  QUIC_DVLOG(1) << "OnRegistration " << fd << " event_mask " << event_mask;
-}
-
-void MasqueServerSession::OnModification(QuicUdpSocketFd fd, int event_mask) {
-  QUIC_DVLOG(1) << "OnModification " << fd << " event_mask " << event_mask;
-}
-
-void MasqueServerSession::OnEvent(QuicUdpSocketFd fd, QuicEpollEvent* event) {
-  if ((event->in_events & EPOLLIN) == 0) {
-    QUIC_DVLOG(1) << "Ignoring OnEvent fd " << fd << " event mask "
-                  << event->in_events;
+void MasqueServerSession::OnSocketEvent(QuicEventLoop* /*event_loop*/,
+                                        QuicUdpSocketFd fd,
+                                        QuicSocketEventMask events) {
+  if ((events & kSocketEventReadable) == 0) {
+    QUIC_DVLOG(1) << "Ignoring OnEvent fd " << fd << " event mask " << events;
     return;
   }
   auto it = absl::c_find_if(connect_udp_server_states_,
@@ -272,16 +270,26 @@
                               return connect_udp.fd() == fd;
                             });
   if (it == connect_udp_server_states_.end()) {
-    QUIC_BUG(quic_bug_10974_1) << "Got unexpected event mask "
-                               << event->in_events << " on unknown fd " << fd;
+    QUIC_BUG(quic_bug_10974_1)
+        << "Got unexpected event mask " << events << " on unknown fd " << fd;
     return;
   }
+
+  auto rearm = absl::MakeCleanup([&]() {
+    if (!event_loop_->SupportsEdgeTriggered()) {
+      if (!event_loop_->RearmSocket(fd, kSocketEventReadable)) {
+        QUIC_BUG(MasqueServerSession_OnSocketEvent_Rearm)
+            << "Failed to re-arm socket " << fd << " for reading";
+      }
+    }
+  });
+
   QuicSocketAddress expected_target_server_address =
       it->target_server_address();
   QUICHE_DCHECK(expected_target_server_address.IsInitialized());
-  QUIC_DVLOG(1) << "Received readable event on fd " << fd << " (mask "
-                << event->in_events << ") stream ID " << it->stream()->id()
-                << " server " << expected_target_server_address;
+  QUIC_DVLOG(1) << "Received readable event on fd " << fd << " (mask " << events
+                << ") stream ID " << it->stream()->id() << " server "
+                << expected_target_server_address;
   QuicUdpSocketApi socket_api;
   BitMask64 packet_info_interested(QuicUdpPacketInfoBit::PEER_ADDRESS);
   char packet_buffer[1 + kMaxIncomingPacketSize];
@@ -329,20 +337,6 @@
   }
 }
 
-void MasqueServerSession::OnUnregistration(QuicUdpSocketFd fd, bool replaced) {
-  QUIC_DVLOG(1) << "OnUnregistration " << fd << " " << (replaced ? "" : "!")
-                << " replaced";
-}
-
-void MasqueServerSession::OnShutdown(QuicEpollServer* /*eps*/,
-                                     QuicUdpSocketFd fd) {
-  QUIC_DVLOG(1) << "OnShutdown " << fd;
-}
-
-std::string MasqueServerSession::Name() const {
-  return std::string("MasqueServerSession-") + connection_id().ToString();
-}
-
 bool MasqueServerSession::OnSettingsFrame(const SettingsFrame& frame) {
   QUIC_DLOG(INFO) << "Received SETTINGS: " << frame;
   if (!QuicSimpleServerSession::OnSettingsFrame(frame)) {
@@ -377,7 +371,9 @@
   }
   QuicUdpSocketApi socket_api;
   QUIC_DLOG(INFO) << "Closing fd " << fd_;
-  masque_session_->epoll_server()->UnregisterFD(fd_);
+  if (!masque_session_->event_loop()->UnregisterSocket(fd_)) {
+    QUIC_DLOG(ERROR) << "Failed to unregister FD " << fd_;
+  }
   socket_api.Destroy(fd_);
 }
 
@@ -393,7 +389,9 @@
   if (fd_ != kQuicInvalidSocketFd) {
     QuicUdpSocketApi socket_api;
     QUIC_DLOG(INFO) << "Closing fd " << fd_;
-    masque_session_->epoll_server()->UnregisterFD(fd_);
+    if (!masque_session_->event_loop()->UnregisterSocket(fd_)) {
+      QUIC_DLOG(ERROR) << "Failed to unregister FD " << fd_;
+    }
     socket_api.Destroy(fd_);
   }
   stream_ = other.stream_;
diff --git a/quiche/quic/masque/masque_server_session.h b/quiche/quic/masque/masque_server_session.h
index 59b33c2..d820137 100644
--- a/quiche/quic/masque/masque_server_session.h
+++ b/quiche/quic/masque/masque_server_session.h
@@ -5,11 +5,11 @@
 #ifndef QUICHE_QUIC_MASQUE_MASQUE_SERVER_SESSION_H_
 #define QUICHE_QUIC_MASQUE_MASQUE_SERVER_SESSION_H_
 
+#include "quiche/quic/core/io/quic_event_loop.h"
 #include "quiche/quic/core/quic_types.h"
 #include "quiche/quic/core/quic_udp_socket.h"
 #include "quiche/quic/masque/masque_server_backend.h"
 #include "quiche/quic/masque/masque_utils.h"
-#include "quiche/quic/platform/api/quic_epoll.h"
 #include "quiche/quic/platform/api/quic_export.h"
 #include "quiche/quic/tools/quic_simple_server_session.h"
 
@@ -19,13 +19,13 @@
 class QUIC_NO_EXPORT MasqueServerSession
     : public QuicSimpleServerSession,
       public MasqueServerBackend::BackendClient,
-      public QuicEpollCallbackInterface {
+      public QuicSocketEventListener {
  public:
   explicit MasqueServerSession(
       MasqueMode masque_mode, const QuicConfig& config,
       const ParsedQuicVersionVector& supported_versions,
       QuicConnection* connection, QuicSession::Visitor* visitor,
-      QuicEpollServer* epoll_server, QuicCryptoServerStreamBase::Helper* helper,
+      QuicEventLoop* event_loop, QuicCryptoServerStreamBase::Helper* helper,
       const QuicCryptoServerConfig* crypto_config,
       QuicCompressedCertsCache* compressed_certs_cache,
       MasqueServerBackend* masque_server_backend);
@@ -47,16 +47,11 @@
       const spdy::Http2HeaderBlock& request_headers,
       QuicSimpleServerBackend::RequestHandler* request_handler) override;
 
-  // From QuicEpollCallbackInterface.
-  void OnRegistration(QuicEpollServer* eps, QuicUdpSocketFd fd,
-                      int event_mask) override;
-  void OnModification(QuicUdpSocketFd fd, int event_mask) override;
-  void OnEvent(QuicUdpSocketFd fd, QuicEpollEvent* event) override;
-  void OnUnregistration(QuicUdpSocketFd fd, bool replaced) override;
-  void OnShutdown(QuicEpollServer* eps, QuicUdpSocketFd fd) override;
-  std::string Name() const override;
+  // From QuicSocketEventListener.
+  void OnSocketEvent(QuicEventLoop* event_loop, QuicUdpSocketFd fd,
+                     QuicSocketEventMask events) override;
 
-  QuicEpollServer* epoll_server() const { return epoll_server_; }
+  QuicEventLoop* event_loop() const { return event_loop_; }
 
  private:
   // State that the MasqueServerSession keeps for each CONNECT-UDP request.
@@ -64,7 +59,7 @@
       : public QuicSpdyStream::Http3DatagramVisitor {
    public:
     // ConnectUdpServerState takes ownership of |fd|. It will unregister it
-    // from |epoll_server| and close the file descriptor when destructed.
+    // from |event_loop| and close the file descriptor when destructed.
     explicit ConnectUdpServerState(
         QuicSpdyStream* stream, const QuicSocketAddress& target_server_address,
         QuicUdpSocketFd fd, MasqueServerSession* masque_session);
@@ -101,7 +96,7 @@
   }
 
   MasqueServerBackend* masque_server_backend_;  // Unowned.
-  QuicEpollServer* epoll_server_;               // Unowned.
+  QuicEventLoop* event_loop_;                   // Unowned.
   MasqueMode masque_mode_;
   std::list<ConnectUdpServerState> connect_udp_server_states_;
   bool masque_initialized_ = false;
diff --git a/quiche/quic/qbone/qbone_client_test.cc b/quiche/quic/qbone/qbone_client_test.cc
index 5fd7229..0e6d5eb 100644
--- a/quiche/quic/qbone/qbone_client_test.cc
+++ b/quiche/quic/qbone/qbone_client_test.cc
@@ -8,6 +8,7 @@
 
 #include "absl/strings/string_view.h"
 #include "quiche/quic/core/quic_alarm_factory.h"
+#include "quiche/quic/core/quic_default_connection_helper.h"
 #include "quiche/quic/core/quic_default_packet_writer.h"
 #include "quiche/quic/core/quic_dispatcher.h"
 #include "quiche/quic/core/quic_epoll_alarm_factory.h"
@@ -137,17 +138,11 @@
   explicit QboneTestServer(std::unique_ptr<ProofSource> proof_source)
       : QuicServer(std::move(proof_source), &response_cache_) {}
   QuicDispatcher* CreateQuicDispatcher() override {
-    QuicEpollAlarmFactory alarm_factory(epoll_server());
     return new QuicQboneDispatcher(
         &config(), &crypto_config(), version_manager(),
-        std::unique_ptr<QuicEpollConnectionHelper>(
-            new QuicEpollConnectionHelper(epoll_server(),
-                                          QuicAllocator::BUFFER_POOL)),
-        std::unique_ptr<QuicCryptoServerStreamBase::Helper>(
-            new QboneCryptoServerStreamHelper()),
-        std::unique_ptr<QuicEpollAlarmFactory>(
-            new QuicEpollAlarmFactory(epoll_server())),
-        &writer_);
+        std::make_unique<QuicDefaultConnectionHelper>(),
+        std::make_unique<QboneCryptoServerStreamHelper>(),
+        event_loop()->CreateAlarmFactory(), &writer_);
   }
 
   std::vector<std::string> data() { return writer_.data(); }
diff --git a/quiche/quic/test_tools/quic_test_server.cc b/quiche/quic/test_tools/quic_test_server.cc
index 53068df..83a060d 100644
--- a/quiche/quic/test_tools/quic_test_server.cc
+++ b/quiche/quic/test_tools/quic_test_server.cc
@@ -8,8 +8,7 @@
 
 #include "absl/memory/memory.h"
 #include "absl/strings/string_view.h"
-#include "quiche/quic/core/quic_epoll_alarm_factory.h"
-#include "quiche/quic/core/quic_epoll_connection_helper.h"
+#include "quiche/quic/core/quic_default_connection_helper.h"
 #include "quiche/quic/tools/quic_simple_crypto_server_stream_helper.h"
 #include "quiche/quic/tools/quic_simple_dispatcher.h"
 #include "quiche/quic/tools/quic_simple_server_session.h"
@@ -178,11 +177,10 @@
 QuicDispatcher* QuicTestServer::CreateQuicDispatcher() {
   return new QuicTestDispatcher(
       &config(), &crypto_config(), version_manager(),
-      std::make_unique<QuicEpollConnectionHelper>(epoll_server(),
-                                                  QuicAllocator::BUFFER_POOL),
+      std::make_unique<QuicDefaultConnectionHelper>(),
       std::unique_ptr<QuicCryptoServerStreamBase::Helper>(
           new QuicSimpleCryptoServerStreamHelper()),
-      std::make_unique<QuicEpollAlarmFactory>(epoll_server()), server_backend(),
+      event_loop()->CreateAlarmFactory(), server_backend(),
       expected_server_connection_id_length());
 }
 
diff --git a/quiche/quic/test_tools/server_thread.cc b/quiche/quic/test_tools/server_thread.cc
index 1594612..c0d32ea 100644
--- a/quiche/quic/test_tools/server_thread.cc
+++ b/quiche/quic/test_tools/server_thread.cc
@@ -4,6 +4,7 @@
 
 #include "quiche/quic/test_tools/server_thread.h"
 
+#include "quiche/quic/core/quic_default_clock.h"
 #include "quiche/quic/core/quic_dispatcher.h"
 #include "quiche/quic/test_tools/crypto_test_utils.h"
 #include "quiche/quic/test_tools/quic_dispatcher_peer.h"
@@ -16,7 +17,7 @@
                            const QuicSocketAddress& address)
     : QuicThread("server_thread"),
       server_(std::move(server)),
-      clock_(server_->epoll_server()),
+      clock_(QuicDefaultClock::Get()),
       address_(address),
       port_(0),
       initialized_(false) {}
@@ -73,8 +74,8 @@
 
 bool ServerThread::WaitUntil(std::function<bool()> termination_predicate,
                              QuicTime::Delta timeout) {
-  const QuicTime deadline = clock_.Now() + timeout;
-  while (clock_.Now() < deadline) {
+  const QuicTime deadline = clock_->Now() + timeout;
+  while (clock_->Now() < deadline) {
     QuicNotification done_checking;
     bool should_terminate = false;
     Schedule([&] {
diff --git a/quiche/quic/test_tools/server_thread.h b/quiche/quic/test_tools/server_thread.h
index fc53467..c29d633 100644
--- a/quiche/quic/test_tools/server_thread.h
+++ b/quiche/quic/test_tools/server_thread.h
@@ -8,7 +8,6 @@
 #include <memory>
 
 #include "quiche/quic/core/quic_config.h"
-#include "quiche/quic/core/quic_epoll_clock.h"
 #include "quiche/quic/platform/api/quic_mutex.h"
 #include "quiche/quic/platform/api/quic_socket_address.h"
 #include "quiche/quic/platform/api/quic_thread.h"
@@ -79,7 +78,7 @@
   QuicNotification quit_;    // Notified when the server should quit.
 
   std::unique_ptr<QuicServer> server_;
-  QuicEpollClock clock_;
+  QuicClock* clock_;
   QuicSocketAddress address_;
   mutable QuicMutex port_lock_;
   int port_ QUIC_GUARDED_BY(port_lock_);
diff --git a/quiche/quic/tools/quic_server.cc b/quiche/quic/tools/quic_server.cc
index c5ab6ea..53985e7 100644
--- a/quiche/quic/tools/quic_server.cc
+++ b/quiche/quic/tools/quic_server.cc
@@ -16,14 +16,15 @@
 
 #include "quiche/quic/core/crypto/crypto_handshake.h"
 #include "quiche/quic/core/crypto/quic_random.h"
+#include "quiche/quic/core/io/quic_default_event_loop.h"
+#include "quiche/quic/core/io/quic_event_loop.h"
 #include "quiche/quic/core/quic_clock.h"
 #include "quiche/quic/core/quic_crypto_stream.h"
 #include "quiche/quic/core/quic_data_reader.h"
+#include "quiche/quic/core/quic_default_clock.h"
+#include "quiche/quic/core/quic_default_connection_helper.h"
 #include "quiche/quic/core/quic_default_packet_writer.h"
 #include "quiche/quic/core/quic_dispatcher.h"
-#include "quiche/quic/core/quic_epoll_alarm_factory.h"
-#include "quiche/quic/core/quic_epoll_clock.h"
-#include "quiche/quic/core/quic_epoll_connection_helper.h"
 #include "quiche/quic/core/quic_packet_reader.h"
 #include "quiche/quic/core/quic_packets.h"
 #include "quiche/quic/platform/api/quic_flags.h"
@@ -36,7 +37,6 @@
 
 namespace {
 
-const int kEpollFlags = EPOLLIN | EPOLLOUT | EPOLLET;
 const char kSourceAddressTokenSecret[] = "secret";
 
 }  // namespace
@@ -95,17 +95,19 @@
         kInitialSessionFlowControlWindow);
   }
 
-  epoll_server_.set_timeout_in_us(50 * 1000);
-
-  QuicEpollClock clock(&epoll_server_);
-
   std::unique_ptr<CryptoHandshakeMessage> scfg(crypto_config_.AddDefaultConfig(
-      QuicRandom::GetInstance(), &clock, crypto_config_options_));
+      QuicRandom::GetInstance(), QuicDefaultClock::Get(),
+      crypto_config_options_));
 }
 
-QuicServer::~QuicServer() = default;
+QuicServer::~QuicServer() {
+  close(fd_);
+  fd_ = -1;
+}
 
 bool QuicServer::CreateUDPSocketAndListen(const QuicSocketAddress& address) {
+  event_loop_ = CreateEventLoop();
+
   QuicUdpSocketApi socket_api;
   fd_ = socket_api.Create(address.host().AddressFamilyToInt(),
                           /*receive_buffer_size =*/kDefaultSocketReceiveBuffer,
@@ -135,7 +137,11 @@
     port_ = address.port();
   }
 
-  epoll_server_.RegisterFD(fd_, this, kEpollFlags);
+  bool register_result = event_loop_->RegisterSocket(
+      fd_, kSocketEventReadable | kSocketEventWritable, this);
+  if (!register_result) {
+    return false;
+  }
   dispatcher_.reset(CreateQuicDispatcher());
   dispatcher_->InitializeWithWriter(CreateWriter(fd_));
 
@@ -147,16 +153,17 @@
 }
 
 QuicDispatcher* QuicServer::CreateQuicDispatcher() {
-  QuicEpollAlarmFactory alarm_factory(&epoll_server_);
   return new QuicSimpleDispatcher(
       &config_, &crypto_config_, &version_manager_,
-      std::unique_ptr<QuicEpollConnectionHelper>(new QuicEpollConnectionHelper(
-          &epoll_server_, QuicAllocator::BUFFER_POOL)),
+      std::make_unique<QuicDefaultConnectionHelper>(),
       std::unique_ptr<QuicCryptoServerStreamBase::Helper>(
           new QuicSimpleCryptoServerStreamHelper()),
-      std::unique_ptr<QuicEpollAlarmFactory>(
-          new QuicEpollAlarmFactory(&epoll_server_)),
-      quic_simple_server_backend_, expected_server_connection_id_length_);
+      event_loop_->CreateAlarmFactory(), quic_simple_server_backend_,
+      expected_server_connection_id_length_);
+}
+
+std::unique_ptr<QuicEventLoop> QuicServer::CreateEventLoop() {
+  return GetDefaultEventLoop()->Create(QuicDefaultClock::Get());
 }
 
 void QuicServer::HandleEventsForever() {
@@ -166,7 +173,7 @@
 }
 
 void QuicServer::WaitForEvents() {
-  epoll_server_.WaitForEventsAndExecuteCallbacks();
+  event_loop_->RunEventLoopOnce(QuicTime::Delta::FromMilliseconds(50));
 }
 
 void QuicServer::Shutdown() {
@@ -176,17 +183,15 @@
     dispatcher_->Shutdown();
   }
 
-  epoll_server_.Shutdown();
-
-  close(fd_);
-  fd_ = -1;
+  dispatcher_.reset();
+  event_loop_.reset();
 }
 
-void QuicServer::OnEvent(int fd, QuicEpollEvent* event) {
+void QuicServer::OnSocketEvent(QuicEventLoop* /*event_loop*/,
+                               QuicUdpSocketFd fd, QuicSocketEventMask events) {
   QUICHE_DCHECK_EQ(fd, fd_);
-  event->out_ready_mask = 0;
 
-  if (event->in_events & EPOLLIN) {
+  if (events & kSocketEventReadable) {
     QUIC_DVLOG(1) << "EPOLLIN";
 
     dispatcher_->ProcessBufferedChlos(kNumSessionsToCreatePerSocketEvent);
@@ -194,19 +199,27 @@
     bool more_to_read = true;
     while (more_to_read) {
       more_to_read = packet_reader_->ReadAndDispatchPackets(
-          fd_, port_, QuicEpollClock(&epoll_server_), dispatcher_.get(),
+          fd_, port_, *QuicDefaultClock::Get(), dispatcher_.get(),
           overflow_supported_ ? &packets_dropped_ : nullptr);
     }
 
     if (dispatcher_->HasChlosBuffered()) {
       // Register EPOLLIN event to consume buffered CHLO(s).
-      event->out_ready_mask |= EPOLLIN;
+      bool success =
+          event_loop_->ArtificiallyNotifyEvent(fd_, kSocketEventReadable);
+      QUICHE_DCHECK(success);
+    }
+    if (!event_loop_->SupportsEdgeTriggered()) {
+      bool success = event_loop_->RearmSocket(fd_, kSocketEventReadable);
+      QUICHE_DCHECK(success);
     }
   }
-  if (event->in_events & EPOLLOUT) {
+  if (events & kSocketEventWritable) {
     dispatcher_->OnCanWrite();
-    if (dispatcher_->HasPendingWrites()) {
-      event->out_ready_mask |= EPOLLOUT;
+    if (!event_loop_->SupportsEdgeTriggered() &&
+        dispatcher_->HasPendingWrites()) {
+      bool success = event_loop_->RearmSocket(fd_, kSocketEventWritable);
+      QUICHE_DCHECK(success);
     }
   }
 }
diff --git a/quiche/quic/tools/quic_server.h b/quiche/quic/tools/quic_server.h
index 5c65fe0..61531fb 100644
--- a/quiche/quic/tools/quic_server.h
+++ b/quiche/quic/tools/quic_server.h
@@ -15,8 +15,8 @@
 
 #include "absl/strings/string_view.h"
 #include "quiche/quic/core/crypto/quic_crypto_server_config.h"
+#include "quiche/quic/core/io/quic_event_loop.h"
 #include "quiche/quic/core/quic_config.h"
-#include "quiche/quic/core/quic_epoll_connection_helper.h"
 #include "quiche/quic/core/quic_framer.h"
 #include "quiche/quic/core/quic_packet_writer.h"
 #include "quiche/quic/core/quic_udp_socket.h"
@@ -35,8 +35,7 @@
 class QuicDispatcher;
 class QuicPacketReader;
 
-class QuicServer : public QuicSpdyServerBase,
-                   public QuicEpollCallbackInterface {
+class QuicServer : public QuicSpdyServerBase, public QuicSocketEventListener {
  public:
   QuicServer(std::unique_ptr<ProofSource> proof_source,
              QuicSimpleServerBackend* quic_simple_server_backend);
@@ -54,8 +53,6 @@
 
   ~QuicServer() override;
 
-  std::string Name() const override { return "QuicServer"; }
-
   // Start listening on the specified address.
   bool CreateUDPSocketAndListen(const QuicSocketAddress& address) override;
   // Handles all events. Does not return.
@@ -64,17 +61,12 @@
   // Wait up to 50ms, and handle any events which occur.
   void WaitForEvents();
 
-  // Server deletion is imminent.  Start cleaning up the epoll server.
+  // Server deletion is imminent.  Start cleaning up any pending sessions.
   virtual void Shutdown();
 
-  // From EpollCallbackInterface
-  void OnRegistration(QuicEpollServer* /*eps*/, int /*fd*/,
-                      int /*event_mask*/) override {}
-  void OnModification(int /*fd*/, int /*event_mask*/) override {}
-  void OnEvent(int /*fd*/, QuicEpollEvent* /*event*/) override;
-  void OnUnregistration(int /*fd*/, bool /*replaced*/) override {}
-
-  void OnShutdown(QuicEpollServer* /*eps*/, int /*fd*/) override {}
+  // QuicSocketEventListener implementation.
+  void OnSocketEvent(QuicEventLoop* event_loop, QuicUdpSocketFd fd,
+                     QuicSocketEventMask events) override;
 
   void SetChloMultiplier(size_t multiplier) {
     crypto_config_.set_chlo_multiplier(multiplier);
@@ -90,13 +82,15 @@
 
   int port() { return port_; }
 
-  QuicEpollServer* epoll_server() { return &epoll_server_; }
+  QuicEventLoop* event_loop() { return event_loop_.get(); }
 
  protected:
   virtual QuicPacketWriter* CreateWriter(int fd);
 
   virtual QuicDispatcher* CreateQuicDispatcher();
 
+  virtual std::unique_ptr<QuicEventLoop> CreateEventLoop();
+
   const QuicConfig& config() const { return config_; }
   const QuicCryptoServerConfig& crypto_config() const { return crypto_config_; }
 
@@ -120,10 +114,10 @@
   // Initialize the internal state of the server.
   void Initialize();
 
+  // Schedules alarms and notifies the server of the I/O events.
+  std::unique_ptr<QuicEventLoop> event_loop_;
   // Accepts data from the framer and demuxes clients to sessions.
   std::unique_ptr<QuicDispatcher> dispatcher_;
-  // Frames incoming packets and hands them to the dispatcher.
-  QuicEpollServer epoll_server_;
 
   // The port the server is listening on.
   int port_;
diff --git a/quiche/quic/tools/quic_server_test.cc b/quiche/quic/tools/quic_server_test.cc
index b0094be..d193fd3 100644
--- a/quiche/quic/tools/quic_server_test.cc
+++ b/quiche/quic/tools/quic_server_test.cc
@@ -4,10 +4,15 @@
 
 #include "quiche/quic/tools/quic_server.h"
 
+#include <memory>
+
 #include "absl/base/macros.h"
 #include "quiche/quic/core/crypto/quic_random.h"
-#include "quiche/quic/core/quic_epoll_alarm_factory.h"
-#include "quiche/quic/core/quic_epoll_connection_helper.h"
+#include "quiche/quic/core/io/quic_default_event_loop.h"
+#include "quiche/quic/core/io/quic_event_loop.h"
+#include "quiche/quic/core/quic_default_clock.h"
+#include "quiche/quic/core/quic_default_connection_helper.h"
+#include "quiche/quic/core/quic_default_packet_writer.h"
 #include "quiche/quic/core/quic_utils.h"
 #include "quiche/quic/platform/api/quic_flags.h"
 #include "quiche/quic/platform/api/quic_logging.h"
@@ -62,14 +67,10 @@
   QuicDispatcher* CreateQuicDispatcher() override {
     mock_dispatcher_ = new MockQuicSimpleDispatcher(
         &config(), &crypto_config(), version_manager(),
-        std::unique_ptr<QuicEpollConnectionHelper>(
-            new QuicEpollConnectionHelper(epoll_server(),
-                                          QuicAllocator::BUFFER_POOL)),
+        std::make_unique<QuicDefaultConnectionHelper>(),
         std::unique_ptr<QuicCryptoServerStreamBase::Helper>(
             new QuicSimpleCryptoServerStreamHelper()),
-        std::unique_ptr<QuicEpollAlarmFactory>(
-            new QuicEpollAlarmFactory(epoll_server())),
-        &quic_simple_server_backend_);
+        event_loop()->CreateAlarmFactory(), &quic_simple_server_backend_);
     return mock_dispatcher_;
   }
 
@@ -147,14 +148,11 @@
                        crypto_test_utils::ProofSourceForTesting(),
                        KeyExchangeSource::Default()),
         version_manager_(AllSupportedVersions()),
+        event_loop_(GetDefaultEventLoop()->Create(QuicDefaultClock::Get())),
         dispatcher_(&config_, &crypto_config_, &version_manager_,
-                    std::unique_ptr<QuicEpollConnectionHelper>(
-                        new QuicEpollConnectionHelper(
-                            &eps_, QuicAllocator::BUFFER_POOL)),
-                    std::unique_ptr<QuicCryptoServerStreamBase::Helper>(
-                        new QuicSimpleCryptoServerStreamHelper()),
-                    std::unique_ptr<QuicEpollAlarmFactory>(
-                        new QuicEpollAlarmFactory(&eps_)),
+                    std::make_unique<QuicDefaultConnectionHelper>(),
+                    std::make_unique<QuicSimpleCryptoServerStreamHelper>(),
+                    event_loop_->CreateAlarmFactory(),
                     &quic_simple_server_backend_) {
     dispatcher_.InitializeWithWriter(new QuicDefaultPacketWriter(1234));
   }
@@ -168,7 +166,7 @@
   QuicConfig config_;
   QuicCryptoServerConfig crypto_config_;
   QuicVersionManager version_manager_;
-  QuicEpollServer eps_;
+  std::unique_ptr<QuicEventLoop> event_loop_;
   QuicMemoryCacheBackend quic_simple_server_backend_;
   MockQuicDispatcher dispatcher_;
 };