Unexport //third_party/epoll_server
Nothing in QUICHE depends on it anymore.
PiperOrigin-RevId: 474535563
diff --git a/build/source_list.bzl b/build/source_list.bzl
index fa32a50..808114a 100644
--- a/build/source_list.bzl
+++ b/build/source_list.bzl
@@ -948,10 +948,6 @@
"common/platform/api/quiche_event_loop.h",
"common/platform/api/quiche_stream_buffer_allocator.h",
"common/platform/api/quiche_udp_socket_platform_api.h",
- "epoll_server/platform/api/epoll_bug.h",
- "epoll_server/platform/api/epoll_logging.h",
- "epoll_server/platform/api/epoll_thread.h",
- "epoll_server/simple_epoll_server.h",
"quic/core/batch_writer/quic_batch_writer_base.h",
"quic/core/batch_writer/quic_batch_writer_buffer.h",
"quic/core/batch_writer/quic_batch_writer_test.h",
@@ -984,7 +980,6 @@
"quic/tools/quic_server.h",
]
epoll_tool_support_srcs = [
- "epoll_server/simple_epoll_server.cc",
"quic/core/batch_writer/quic_batch_writer_base.cc",
"quic/core/batch_writer/quic_batch_writer_buffer.cc",
"quic/core/batch_writer/quic_gso_batch_writer.cc",
@@ -1012,10 +1007,6 @@
"quic/tools/quic_server.cc",
]
epoll_test_support_hdrs = [
- "epoll_server/fake_simple_epoll_server.h",
- "epoll_server/platform/api/epoll_address_test_utils.h",
- "epoll_server/platform/api/epoll_expect_bug.h",
- "epoll_server/platform/api/epoll_test.h",
"quic/bindings/quic_libevent.h",
"quic/test_tools/quic_mock_syscall_wrapper.h",
"quic/test_tools/quic_server_peer.h",
@@ -1024,7 +1015,6 @@
"quic/test_tools/server_thread.h",
]
epoll_test_support_srcs = [
- "epoll_server/fake_simple_epoll_server.cc",
"quic/bindings/quic_libevent.cc",
"quic/test_tools/quic_mock_syscall_wrapper.cc",
"quic/test_tools/quic_server_peer.cc",
@@ -1296,7 +1286,6 @@
]
epoll_tests_srcs = [
- "epoll_server/simple_epoll_server_test.cc",
"quic/core/batch_writer/quic_batch_writer_buffer_test.cc",
"quic/core/batch_writer/quic_batch_writer_test.cc",
"quic/core/batch_writer/quic_gso_batch_writer_test.cc",
diff --git a/build/source_list.gni b/build/source_list.gni
index d36d591..a03ebc5 100644
--- a/build/source_list.gni
+++ b/build/source_list.gni
@@ -948,10 +948,6 @@
"src/quiche/common/platform/api/quiche_event_loop.h",
"src/quiche/common/platform/api/quiche_stream_buffer_allocator.h",
"src/quiche/common/platform/api/quiche_udp_socket_platform_api.h",
- "src/quiche/epoll_server/platform/api/epoll_bug.h",
- "src/quiche/epoll_server/platform/api/epoll_logging.h",
- "src/quiche/epoll_server/platform/api/epoll_thread.h",
- "src/quiche/epoll_server/simple_epoll_server.h",
"src/quiche/quic/core/batch_writer/quic_batch_writer_base.h",
"src/quiche/quic/core/batch_writer/quic_batch_writer_buffer.h",
"src/quiche/quic/core/batch_writer/quic_batch_writer_test.h",
@@ -984,7 +980,6 @@
"src/quiche/quic/tools/quic_server.h",
]
epoll_tool_support_srcs = [
- "src/quiche/epoll_server/simple_epoll_server.cc",
"src/quiche/quic/core/batch_writer/quic_batch_writer_base.cc",
"src/quiche/quic/core/batch_writer/quic_batch_writer_buffer.cc",
"src/quiche/quic/core/batch_writer/quic_gso_batch_writer.cc",
@@ -1012,10 +1007,6 @@
"src/quiche/quic/tools/quic_server.cc",
]
epoll_test_support_hdrs = [
- "src/quiche/epoll_server/fake_simple_epoll_server.h",
- "src/quiche/epoll_server/platform/api/epoll_address_test_utils.h",
- "src/quiche/epoll_server/platform/api/epoll_expect_bug.h",
- "src/quiche/epoll_server/platform/api/epoll_test.h",
"src/quiche/quic/bindings/quic_libevent.h",
"src/quiche/quic/test_tools/quic_mock_syscall_wrapper.h",
"src/quiche/quic/test_tools/quic_server_peer.h",
@@ -1024,7 +1015,6 @@
"src/quiche/quic/test_tools/server_thread.h",
]
epoll_test_support_srcs = [
- "src/quiche/epoll_server/fake_simple_epoll_server.cc",
"src/quiche/quic/bindings/quic_libevent.cc",
"src/quiche/quic/test_tools/quic_mock_syscall_wrapper.cc",
"src/quiche/quic/test_tools/quic_server_peer.cc",
@@ -1296,7 +1286,6 @@
]
epoll_tests_srcs = [
- "src/quiche/epoll_server/simple_epoll_server_test.cc",
"src/quiche/quic/core/batch_writer/quic_batch_writer_buffer_test.cc",
"src/quiche/quic/core/batch_writer/quic_batch_writer_test.cc",
"src/quiche/quic/core/batch_writer/quic_gso_batch_writer_test.cc",
diff --git a/build/source_list.json b/build/source_list.json
index 753c2fd..cf5abb2 100644
--- a/build/source_list.json
+++ b/build/source_list.json
@@ -947,10 +947,6 @@
"quiche/common/platform/api/quiche_event_loop.h",
"quiche/common/platform/api/quiche_stream_buffer_allocator.h",
"quiche/common/platform/api/quiche_udp_socket_platform_api.h",
- "quiche/epoll_server/platform/api/epoll_bug.h",
- "quiche/epoll_server/platform/api/epoll_logging.h",
- "quiche/epoll_server/platform/api/epoll_thread.h",
- "quiche/epoll_server/simple_epoll_server.h",
"quiche/quic/core/batch_writer/quic_batch_writer_base.h",
"quiche/quic/core/batch_writer/quic_batch_writer_buffer.h",
"quiche/quic/core/batch_writer/quic_batch_writer_test.h",
@@ -983,7 +979,6 @@
"quiche/quic/tools/quic_server.h"
],
"epoll_tool_support_srcs": [
- "quiche/epoll_server/simple_epoll_server.cc",
"quiche/quic/core/batch_writer/quic_batch_writer_base.cc",
"quiche/quic/core/batch_writer/quic_batch_writer_buffer.cc",
"quiche/quic/core/batch_writer/quic_gso_batch_writer.cc",
@@ -1011,10 +1006,6 @@
"quiche/quic/tools/quic_server.cc"
],
"epoll_test_support_hdrs": [
- "quiche/epoll_server/fake_simple_epoll_server.h",
- "quiche/epoll_server/platform/api/epoll_address_test_utils.h",
- "quiche/epoll_server/platform/api/epoll_expect_bug.h",
- "quiche/epoll_server/platform/api/epoll_test.h",
"quiche/quic/bindings/quic_libevent.h",
"quiche/quic/test_tools/quic_mock_syscall_wrapper.h",
"quiche/quic/test_tools/quic_server_peer.h",
@@ -1023,7 +1014,6 @@
"quiche/quic/test_tools/server_thread.h"
],
"epoll_test_support_srcs": [
- "quiche/epoll_server/fake_simple_epoll_server.cc",
"quiche/quic/bindings/quic_libevent.cc",
"quiche/quic/test_tools/quic_mock_syscall_wrapper.cc",
"quiche/quic/test_tools/quic_server_peer.cc",
@@ -1295,7 +1285,6 @@
],
"epoll_tests_srcs": [
- "quiche/epoll_server/simple_epoll_server_test.cc",
"quiche/quic/core/batch_writer/quic_batch_writer_buffer_test.cc",
"quiche/quic/core/batch_writer/quic_batch_writer_test.cc",
"quiche/quic/core/batch_writer/quic_gso_batch_writer_test.cc",
diff --git a/quiche/epoll_server/fake_simple_epoll_server.cc b/quiche/epoll_server/fake_simple_epoll_server.cc
deleted file mode 100644
index f21ae1c..0000000
--- a/quiche/epoll_server/fake_simple_epoll_server.cc
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright (c) 2012 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/epoll_server/fake_simple_epoll_server.h"
-
-namespace epoll_server {
-namespace test {
-
-FakeTimeSimpleEpollServer::FakeTimeSimpleEpollServer() : now_in_usec_(0) {}
-
-FakeTimeSimpleEpollServer::~FakeTimeSimpleEpollServer() = default;
-
-int64_t FakeTimeSimpleEpollServer::NowInUsec() const { return now_in_usec_; }
-
-FakeSimpleEpollServer::FakeSimpleEpollServer() : until_in_usec_(-1) {}
-
-FakeSimpleEpollServer::~FakeSimpleEpollServer() = default;
-
-int FakeSimpleEpollServer::epoll_wait_impl(int /*epfd*/,
- struct epoll_event* events,
- int max_events, int timeout_in_ms) {
- int num_events = 0;
- while (!event_queue_.empty() && num_events < max_events &&
- event_queue_.begin()->first <= NowInUsec() &&
- ((until_in_usec_ == -1) ||
- (event_queue_.begin()->first < until_in_usec_))) {
- int64_t event_time_in_usec = event_queue_.begin()->first;
- events[num_events] = event_queue_.begin()->second;
- if (event_time_in_usec > NowInUsec()) {
- set_now_in_usec(event_time_in_usec);
- }
- event_queue_.erase(event_queue_.begin());
- ++num_events;
- }
- if (num_events == 0) { // then we'd have waited 'till the timeout.
- if (until_in_usec_ < 0) { // then we don't care what the final time is.
- if (timeout_in_ms > 0) {
- AdvanceBy(timeout_in_ms * 1000);
- }
- } else { // except we assume that we don't wait for the timeout
- // period if until_in_usec_ is a positive number.
- set_now_in_usec(until_in_usec_);
- // And reset until_in_usec_ to signal no waiting (as
- // the AdvanceByExactly* stuff is meant to be one-shot,
- // as are all similar net::EpollServer functions)
- until_in_usec_ = -1;
- }
- }
- if (until_in_usec_ >= 0) {
- CHECK(until_in_usec_ >= NowInUsec());
- }
- return num_events;
-}
-
-} // namespace test
-} // namespace epoll_server
diff --git a/quiche/epoll_server/fake_simple_epoll_server.h b/quiche/epoll_server/fake_simple_epoll_server.h
deleted file mode 100644
index 62c9dd1..0000000
--- a/quiche/epoll_server/fake_simple_epoll_server.h
+++ /dev/null
@@ -1,112 +0,0 @@
-// Copyright (c) 2012 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.
-
-#ifndef QUICHE_EPOLL_SERVER_FAKE_SIMPLE_EPOLL_SERVER_H_
-#define QUICHE_EPOLL_SERVER_FAKE_SIMPLE_EPOLL_SERVER_H_
-
-#include <stddef.h>
-#include <stdint.h>
-
-#include <map>
-
-#include "quiche/epoll_server/simple_epoll_server.h"
-
-namespace epoll_server {
-namespace test {
-
-// Unlike the full FakeEpollServer, this only lies about the time but lets
-// fd events operate normally. Usefully when interacting with real backends
-// but wanting to skip forward in time to trigger timeouts.
-class FakeTimeSimpleEpollServer : public SimpleEpollServer {
- public:
- FakeTimeSimpleEpollServer();
- FakeTimeSimpleEpollServer(const FakeTimeSimpleEpollServer&) = delete;
- FakeTimeSimpleEpollServer operator=(const FakeTimeSimpleEpollServer&) =
- delete;
-
- ~FakeTimeSimpleEpollServer() override;
-
- // Replaces the net::EpollServer NowInUsec.
- int64_t NowInUsec() const override;
-
- void set_now_in_usec(int64_t nius) { now_in_usec_ = nius; }
-
- // Advances the virtual 'now' by advancement_usec.
- void AdvanceBy(int64_t advancement_usec) {
- set_now_in_usec(NowInUsec() + advancement_usec);
- }
-
- // Advances the virtual 'now' by advancement_usec, and
- // calls WaitForEventAndExecteCallbacks.
- // Note that the WaitForEventsAndExecuteCallbacks invocation
- // may cause NowInUs to advance beyond what was specified here.
- // If that is not desired, use the AdvanceByExactly calls.
- void AdvanceByAndWaitForEventsAndExecuteCallbacks(int64_t advancement_usec) {
- AdvanceBy(advancement_usec);
- WaitForEventsAndExecuteCallbacks();
- }
-
- private:
- int64_t now_in_usec_;
-};
-
-class FakeSimpleEpollServer : public FakeTimeSimpleEpollServer {
- public: // type definitions
- using EventQueue = std::multimap<int64_t, struct epoll_event>;
-
- FakeSimpleEpollServer();
- FakeSimpleEpollServer(const FakeSimpleEpollServer&) = delete;
- FakeSimpleEpollServer operator=(const FakeSimpleEpollServer&) = delete;
-
- ~FakeSimpleEpollServer() override;
-
- // time_in_usec is the time at which the event specified
- // by 'ee' will be delivered. Note that it -is- possible
- // to add an event for a time which has already been passed..
- // .. upon the next time that the callbacks are invoked,
- // all events which are in the 'past' will be delivered.
- void AddEvent(int64_t time_in_usec, const struct epoll_event& ee) {
- event_queue_.insert(std::make_pair(time_in_usec, ee));
- }
-
- // Advances the virtual 'now' by advancement_usec,
- // and ensure that the next invocation of
- // WaitForEventsAndExecuteCallbacks goes no farther than
- // advancement_usec from the current time.
- void AdvanceByExactly(int64_t advancement_usec) {
- until_in_usec_ = NowInUsec() + advancement_usec;
- set_now_in_usec(NowInUsec() + advancement_usec);
- }
-
- // As above, except calls WaitForEventsAndExecuteCallbacks.
- void AdvanceByExactlyAndCallCallbacks(int64_t advancement_usec) {
- AdvanceByExactly(advancement_usec);
- WaitForEventsAndExecuteCallbacks();
- }
-
- std::unordered_set<AlarmCB*>::size_type NumberOfAlarms() const {
- return all_alarms_.size();
- }
-
- protected: // functions
- // These functions do nothing here, as we're not actually
- // using the epoll_* syscalls.
- void DelFD(int /*fd*/) const override {}
- void AddFD(int /*fd*/, int /*event_mask*/) const override {}
- void ModFD(int /*fd*/, int /*event_mask*/) const override {}
-
- // Replaces the epoll_server's epoll_wait_impl.
- int epoll_wait_impl(int epfd, struct epoll_event* events, int max_events,
- int timeout_in_ms) override;
- void SetNonblocking(int /*fd*/) override {}
-
- private: // members
- EventQueue event_queue_;
- int64_t until_in_usec_;
-};
-
-} // namespace test
-} // namespace epoll_server
-
-#endif // QUICHE_EPOLL_SERVER_FAKE_SIMPLE_EPOLL_SERVER_H_
diff --git a/quiche/epoll_server/platform/api/epoll_address_test_utils.h b/quiche/epoll_server/platform/api/epoll_address_test_utils.h
deleted file mode 100644
index 883d510..0000000
--- a/quiche/epoll_server/platform/api/epoll_address_test_utils.h
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright 2013 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.
-
-#ifndef QUICHE_EPOLL_SERVER_PLATFORM_API_EPOLL_ADDRESS_TEST_UTILS_H_
-#define QUICHE_EPOLL_SERVER_PLATFORM_API_EPOLL_ADDRESS_TEST_UTILS_H_
-
-#include "quiche_platform_impl/epoll_address_test_utils_impl.h"
-
-namespace epoll_server {
-
-inline int AddressFamilyUnderTest() { return AddressFamilyUnderTestImpl(); }
-
-} // namespace epoll_server
-
-#endif // QUICHE_EPOLL_SERVER_PLATFORM_API_EPOLL_ADDRESS_TEST_UTILS_H_
diff --git a/quiche/epoll_server/platform/api/epoll_bug.h b/quiche/epoll_server/platform/api/epoll_bug.h
deleted file mode 100644
index eeb707a..0000000
--- a/quiche/epoll_server/platform/api/epoll_bug.h
+++ /dev/null
@@ -1,12 +0,0 @@
-// Copyright 2013 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.
-
-#ifndef QUICHE_EPOLL_SERVER_PLATFORM_API_EPOLL_BUG_H_
-#define QUICHE_EPOLL_SERVER_PLATFORM_API_EPOLL_BUG_H_
-
-#include "quiche_platform_impl/epoll_bug_impl.h"
-
-#define EPOLL_BUG EPOLL_BUG_IMPL
-
-#endif // QUICHE_EPOLL_SERVER_PLATFORM_API_EPOLL_BUG_H_
diff --git a/quiche/epoll_server/platform/api/epoll_expect_bug.h b/quiche/epoll_server/platform/api/epoll_expect_bug.h
deleted file mode 100644
index 36ebd97..0000000
--- a/quiche/epoll_server/platform/api/epoll_expect_bug.h
+++ /dev/null
@@ -1,12 +0,0 @@
-// Copyright (c) 2018 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.
-
-#ifndef QUICHE_EPOLL_SERVER_PLATFORM_API_EPOLL_EXPECT_BUG_H_
-#define QUICHE_EPOLL_SERVER_PLATFORM_API_EPOLL_EXPECT_BUG_H_
-
-#include "quiche_platform_impl/epoll_expect_bug_impl.h"
-
-#define EXPECT_EPOLL_BUG EXPECT_EPOLL_BUG_IMPL
-
-#endif // QUICHE_EPOLL_SERVER_PLATFORM_API_EPOLL_EXPECT_BUG_H_
diff --git a/quiche/epoll_server/platform/api/epoll_logging.h b/quiche/epoll_server/platform/api/epoll_logging.h
deleted file mode 100644
index 2d5a0f4..0000000
--- a/quiche/epoll_server/platform/api/epoll_logging.h
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright 2013 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.
-
-#ifndef QUICHE_EPOLL_SERVER_PLATFORM_API_EPOLL_LOGGING_H_
-#define QUICHE_EPOLL_SERVER_PLATFORM_API_EPOLL_LOGGING_H_
-
-#include "quiche_platform_impl/epoll_logging_impl.h"
-
-namespace epoll_server {
-
-#define EPOLL_LOG(severity) EPOLL_LOG_IMPL(severity)
-#define EPOLL_VLOG(verbosity) EPOLL_VLOG_IMPL(verbosity)
-#define EPOLL_DVLOG(verbosity) EPOLL_DVLOG_IMPL(verbosity)
-#define EPOLL_PLOG(severity) EPOLL_PLOG_IMPL(severity)
-
-} // namespace epoll_server
-
-#endif // QUICHE_EPOLL_SERVER_PLATFORM_API_EPOLL_LOGGING_H_
diff --git a/quiche/epoll_server/platform/api/epoll_test.h b/quiche/epoll_server/platform/api/epoll_test.h
deleted file mode 100644
index c7f0eea..0000000
--- a/quiche/epoll_server/platform/api/epoll_test.h
+++ /dev/null
@@ -1,11 +0,0 @@
-// Copyright 2013 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.
-
-#ifndef QUICHE_EPOLL_SERVER_PLATFORM_API_EPOLL_TEST_H_
-#define QUICHE_EPOLL_SERVER_PLATFORM_API_EPOLL_TEST_H_
-
-#include "quiche_platform_impl/epoll_test_impl.h"
-#define EpollTest EpollTestImpl
-
-#endif // QUICHE_EPOLL_SERVER_PLATFORM_API_EPOLL_TEST_H_
diff --git a/quiche/epoll_server/platform/api/epoll_thread.h b/quiche/epoll_server/platform/api/epoll_thread.h
deleted file mode 100644
index 5b159cb..0000000
--- a/quiche/epoll_server/platform/api/epoll_thread.h
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright 2013 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.
-
-#ifndef QUICHE_EPOLL_SERVER_PLATFORM_API_EPOLL_THREAD_H_
-#define QUICHE_EPOLL_SERVER_PLATFORM_API_EPOLL_THREAD_H_
-
-#include <string>
-
-#include "quiche_platform_impl/epoll_thread_impl.h"
-
-namespace epoll_server {
-
-// A class representing a thread of execution in QUIC.
-class EpollThread : public EpollThreadImpl {
- public:
- EpollThread(const std::string& string) : EpollThreadImpl(string) {}
- EpollThread(const EpollThread&) = delete;
- EpollThread& operator=(const EpollThread&) = delete;
-
- // Impl defines a virtual void Run() method which subclasses
- // must implement.
-};
-
-} // namespace epoll_server
-
-#endif // QUICHE_EPOLL_SERVER_PLATFORM_API_EPOLL_THREAD_H_
diff --git a/quiche/epoll_server/simple_epoll_server.cc b/quiche/epoll_server/simple_epoll_server.cc
deleted file mode 100644
index eb9d6c4..0000000
--- a/quiche/epoll_server/simple_epoll_server.cc
+++ /dev/null
@@ -1,815 +0,0 @@
-// Copyright 2013 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/epoll_server/simple_epoll_server.h"
-
-#include <errno.h> // for errno
-#include <stdlib.h> // for abort
-#include <string.h> // for strerror_r
-#include <unistd.h> // For read, pipe, close and write.
-
-#include <algorithm>
-#include <utility>
-
-#include "absl/time/clock.h"
-#include "quiche/epoll_server/platform/api/epoll_bug.h"
-
-// Design notes: An efficient implementation of ready list has the following
-// desirable properties:
-//
-// A. O(1) insertion into/removal from the list in any location.
-// B. Once the callback is found by hash lookup using the fd, the lookup of
-// corresponding entry in the list is O(1).
-// C. Safe insertion into/removal from the list during list iteration. (The
-// ready list's purpose is to enable completely event driven I/O model.
-// Thus, all the interesting bits happen in the callback. It is critical
-// to not place any restriction on the API during list iteration.
-//
-// The current implementation achieves these goals with the following design:
-//
-// - The ready list is constructed as a doubly linked list to enable O(1)
-// insertion/removal (see man 3 queue).
-// - The forward and backward links are directly embedded inside the
-// CBAndEventMask struct. This enables O(1) lookup in the list for a given
-// callback. (Techincally, we could've used std::list of hash_set::iterator,
-// and keep a list::iterator in CBAndEventMask to achieve the same effect.
-// However, iterators have two problems: no way to portably invalidate them,
-// and no way to tell whether an iterator is singular or not. The only way to
-// overcome these issues is to keep bools in both places, but that throws off
-// memory alignment (up to 7 wasted bytes for each bool). The extra level of
-// indirection will also likely be less cache friendly. Direct manipulation
-// of link pointers makes it easier to retrieve the CBAndEventMask from the
-// list, easier to check whether an CBAndEventMask is in the list, uses less
-// memory (save 32 bytes/fd), and does not affect cache usage (we need to
-// read in the struct to use the callback anyway).)
-// - Embed the fd directly into CBAndEventMask and switch to using hash_set.
-// This removes the need to store hash_map::iterator in the list just so that
-// we can get both the fd and the callback.
-// - The ready list is "one shot": each entry is removed before OnEvent is
-// called. This removes the mutation-while-iterating problem.
-// - Use two lists to keep track of callbacks. The ready_list_ is the one used
-// for registration. Before iteration, the ready_list_ is swapped into the
-// tmp_list_. Once iteration is done, tmp_list_ will be empty, and
-// ready_list_ will have all the new ready fds.
-
-// The size we use for buffers passed to strerror_r
-static const int kErrorBufferSize = 256;
-
-namespace epoll_server {
-
-template <typename T>
-class AutoReset {
- public:
- AutoReset(T* scoped_variable, T new_value)
- : scoped_variable_(scoped_variable),
- original_value_(std::move(*scoped_variable)) {
- *scoped_variable_ = std::move(new_value);
- }
- AutoReset(const AutoReset&) = delete;
- AutoReset& operator=(const AutoReset&) = delete;
-
- ~AutoReset() { *scoped_variable_ = std::move(original_value_); }
-
- private:
- T* scoped_variable_;
- T original_value_;
-};
-
-// Clears the pipe and returns. Used for waking the epoll server up.
-class ReadPipeCallback : public EpollCallbackInterface {
- public:
- void OnEvent(int fd, EpollEvent* event) override {
- DCHECK(event->in_events == EPOLLIN);
- int data;
- int data_read = 1;
- // Read until the pipe is empty.
- while (data_read > 0) {
- data_read = read(fd, &data, sizeof(data));
- }
- }
- void OnShutdown(SimpleEpollServer* /*eps*/, int /*fd*/) override {}
- void OnRegistration(SimpleEpollServer*, int, int) override {}
- void OnModification(int, int) override {} // COV_NF_LINE
- void OnUnregistration(int, bool) override {} // COV_NF_LINE
- std::string Name() const override { return "ReadPipeCallback"; }
-};
-
-////////////////////////////////////////////////////////////////////////////////
-////////////////////////////////////////////////////////////////////////////////
-
-SimpleEpollServer::SimpleEpollServer()
- : epoll_fd_(epoll_create(1024)),
- timeout_in_us_(0),
- recorded_now_in_us_(0),
- ready_list_size_(0),
- wake_cb_(new ReadPipeCallback),
- read_fd_(-1),
- write_fd_(-1),
- in_wait_for_events_and_execute_callbacks_(false),
- in_shutdown_(false),
- last_delay_in_usec_(0) {
- // ensure that the epoll_fd_ is valid.
- CHECK_NE(epoll_fd_, -1);
- LIST_INIT(&ready_list_);
- LIST_INIT(&tmp_list_);
-
- int pipe_fds[2];
- if (pipe(pipe_fds) < 0) {
- // Unfortunately, it is impossible to test any such initialization in
- // a constructor (as virtual methods do not yet work).
- // This -could- be solved by moving initialization to an outside
- // call...
- int saved_errno = errno;
- char buf[kErrorBufferSize];
- EPOLL_LOG(FATAL) << "Error " << saved_errno << " in pipe(): "
- << strerror_r(saved_errno, buf, sizeof(buf));
- }
- read_fd_ = pipe_fds[0];
- write_fd_ = pipe_fds[1];
- RegisterFD(read_fd_, wake_cb_.get(), EPOLLIN);
-}
-
-void SimpleEpollServer::CleanupFDToCBMap() {
- auto cb_iter = cb_map_.begin();
- while (cb_iter != cb_map_.end()) {
- int fd = cb_iter->fd;
- CB* cb = cb_iter->cb;
-
- cb_iter->in_use = true;
- if (cb) {
- cb->OnShutdown(this, fd);
- }
-
- cb_map_.erase(cb_iter);
- cb_iter = cb_map_.begin();
- }
-}
-
-void SimpleEpollServer::CleanupTimeToAlarmCBMap() {
- TimeToAlarmCBMap::iterator erase_it;
-
- // Call OnShutdown() on alarms. Note that the structure of the loop
- // is similar to the structure of loop in the function HandleAlarms()
- for (auto i = alarm_map_.begin(); i != alarm_map_.end();) {
- // Note that OnShutdown() can call UnregisterAlarm() on
- // other iterators. OnShutdown() should not call UnregisterAlarm()
- // on self because by definition the iterator is not valid any more.
- i->second->OnShutdown(this);
- erase_it = i;
- ++i;
- alarm_map_.erase(erase_it);
- }
-}
-
-SimpleEpollServer::~SimpleEpollServer() {
- DCHECK_EQ(in_shutdown_, false);
- in_shutdown_ = true;
-#ifdef EPOLL_SERVER_EVENT_TRACING
- EPOLL_LOG(INFO) << "\n" << event_recorder_;
-#endif
- EPOLL_VLOG(2) << "Shutting down epoll server ";
- CleanupFDToCBMap();
-
- LIST_INIT(&ready_list_);
- LIST_INIT(&tmp_list_);
-
- CleanupTimeToAlarmCBMap();
-
- close(read_fd_);
- close(write_fd_);
- close(epoll_fd_);
-}
-
-// Whether a CBAandEventMask is on the ready list is determined by a non-NULL
-// le_prev pointer (le_next being NULL indicates end of list).
-inline void SimpleEpollServer::AddToReadyList(CBAndEventMask* cb_and_mask) {
- if (cb_and_mask->entry.le_prev == NULL) {
- LIST_INSERT_HEAD(&ready_list_, cb_and_mask, entry);
- ++ready_list_size_;
- }
-}
-
-inline void SimpleEpollServer::RemoveFromReadyList(
- const CBAndEventMask& cb_and_mask) {
- if (cb_and_mask.entry.le_prev != NULL) {
- LIST_REMOVE(&cb_and_mask, entry);
- // Clean up all the ready list states. Don't bother with the other fields
- // as they are initialized when the CBAandEventMask is added to the ready
- // list. This saves a few cycles in the inner loop.
- cb_and_mask.entry.le_prev = NULL;
- --ready_list_size_;
- if (ready_list_size_ == 0) {
- DCHECK(ready_list_.lh_first == NULL);
- DCHECK(tmp_list_.lh_first == NULL);
- }
- }
-}
-
-void SimpleEpollServer::RegisterFD(int fd, CB* cb, int event_mask) {
- CHECK(cb);
- EPOLL_VLOG(3) << "RegisterFD fd=" << fd << " event_mask=" << event_mask;
- auto fd_i = cb_map_.find(CBAndEventMask(NULL, 0, fd));
- if (cb_map_.end() != fd_i) {
- // do we just abort, or do we just unregister the other callback?
- // for now, lets just unregister the other callback.
-
- // unregister any callback that may already be registered for this FD.
- CB* other_cb = fd_i->cb;
- if (other_cb) {
- // Must remove from the ready list before erasing.
- RemoveFromReadyList(*fd_i);
- other_cb->OnUnregistration(fd, true);
- ModFD(fd, event_mask);
- } else {
- // already unregistered, so just recycle the node.
- AddFD(fd, event_mask);
- }
- fd_i->cb = cb;
- fd_i->event_mask = event_mask;
- fd_i->events_to_fake = 0;
- } else {
- AddFD(fd, event_mask);
- cb_map_.insert(CBAndEventMask(cb, event_mask, fd));
- }
-
- // set the FD to be non-blocking.
- SetNonblocking(fd);
-
- cb->OnRegistration(this, fd, event_mask);
-}
-
-void SimpleEpollServer::SetNonblocking(int fd) {
- int flags = fcntl(fd, F_GETFL, 0);
- if (flags == -1) {
- int saved_errno = errno;
- char buf[kErrorBufferSize];
- EPOLL_LOG(FATAL) << "Error " << saved_errno << " doing fcntl(" << fd
- << ", F_GETFL, 0): "
- << strerror_r(saved_errno, buf, sizeof(buf));
- }
- if (!(flags & O_NONBLOCK)) {
- int saved_flags = flags;
- flags = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
- if (flags == -1) {
- // bad.
- int saved_errno = errno;
- char buf[kErrorBufferSize];
- EPOLL_LOG(FATAL) << "Error " << saved_errno << " doing fcntl(" << fd
- << ", F_SETFL, " << saved_flags
- << "): " << strerror_r(saved_errno, buf, sizeof(buf));
- }
- }
-}
-
-int SimpleEpollServer::epoll_wait_impl(int epfd, struct epoll_event* events,
- int max_events, int timeout_in_ms) {
- return epoll_wait(epfd, events, max_events, timeout_in_ms);
-}
-
-void SimpleEpollServer::RegisterFDForWrite(int fd, CB* cb) {
- RegisterFD(fd, cb, EPOLLOUT);
-}
-
-void SimpleEpollServer::RegisterFDForReadWrite(int fd, CB* cb) {
- RegisterFD(fd, cb, EPOLLIN | EPOLLOUT);
-}
-
-void SimpleEpollServer::RegisterFDForRead(int fd, CB* cb) {
- RegisterFD(fd, cb, EPOLLIN);
-}
-
-void SimpleEpollServer::UnregisterFD(int fd) {
- auto fd_i = cb_map_.find(CBAndEventMask(NULL, 0, fd));
- if (cb_map_.end() == fd_i || fd_i->cb == NULL) {
- // Doesn't exist in server, or has gone through UnregisterFD once and still
- // inside the callchain of OnEvent.
- return;
- }
-#ifdef EPOLL_SERVER_EVENT_TRACING
- event_recorder_.RecordUnregistration(fd);
-#endif
- CB* cb = fd_i->cb;
- // Since the links are embedded within the struct, we must remove it from the
- // list before erasing it from the hash_set.
- RemoveFromReadyList(*fd_i);
- DelFD(fd);
- cb->OnUnregistration(fd, false);
- // fd_i->cb is NULL if that fd is unregistered inside the callchain of
- // OnEvent. Since the SimpleEpollServer needs a valid CBAndEventMask after
- // OnEvent returns in order to add it to the ready list, we cannot have
- // UnregisterFD erase the entry if it is in use. Thus, a NULL fd_i->cb is used
- // as a condition that tells the SimpleEpollServer that this entry is unused
- // at a later point.
- if (!fd_i->in_use) {
- cb_map_.erase(fd_i);
- } else {
- // Remove all trace of the registration, and just keep the node alive long
- // enough so the code that calls OnEvent doesn't have to worry about
- // figuring out whether the CBAndEventMask is valid or not.
- fd_i->cb = NULL;
- fd_i->event_mask = 0;
- fd_i->events_to_fake = 0;
- }
-}
-
-void SimpleEpollServer::ModifyCallback(int fd, int event_mask) {
- ModifyFD(fd, ~0, event_mask);
-}
-
-void SimpleEpollServer::StopRead(int fd) { ModifyFD(fd, EPOLLIN, 0); }
-
-void SimpleEpollServer::StartRead(int fd) { ModifyFD(fd, 0, EPOLLIN); }
-
-void SimpleEpollServer::StopWrite(int fd) { ModifyFD(fd, EPOLLOUT, 0); }
-
-void SimpleEpollServer::StartWrite(int fd) { ModifyFD(fd, 0, EPOLLOUT); }
-
-void SimpleEpollServer::HandleEvent(int fd, int event_mask) {
-#ifdef EPOLL_SERVER_EVENT_TRACING
- event_recorder_.RecordEpollEvent(fd, event_mask);
-#endif
- auto fd_i = cb_map_.find(CBAndEventMask(NULL, 0, fd));
- if (fd_i == cb_map_.end() || fd_i->cb == NULL) {
- // Ignore the event.
- // This could occur if epoll() returns a set of events, and
- // while processing event A (earlier) we removed the callback
- // for event B (and are now processing event B).
- return;
- }
- fd_i->events_asserted = event_mask;
- CBAndEventMask* cb_and_mask = const_cast<CBAndEventMask*>(&*fd_i);
- AddToReadyList(cb_and_mask);
-}
-
-void SimpleEpollServer::WaitForEventsAndExecuteCallbacks() {
- if (in_wait_for_events_and_execute_callbacks_) {
- EPOLL_LOG(DFATAL) << "Attempting to call WaitForEventsAndExecuteCallbacks"
- " when an ancestor to the current function is already"
- " WaitForEventsAndExecuteCallbacks!";
- // The line below is actually tested, but in coverage mode,
- // we never see it.
- return; // COV_NF_LINE
- }
- AutoReset<bool> recursion_guard(&in_wait_for_events_and_execute_callbacks_,
- true);
- if (alarm_map_.empty()) {
- // no alarms, this is business as usual.
- WaitForEventsAndCallHandleEvents(timeout_in_us_, events_, events_size_);
- recorded_now_in_us_ = 0;
- return;
- }
-
- // store the 'now'. If we recomputed 'now' every iteration
- // down below, then we might never exit that loop-- any
- // long-running alarms might install other long-running
- // alarms, etc. By storing it here now, we ensure that
- // a more reasonable amount of work is done here.
- int64_t now_in_us = NowInUsec();
-
- // Get the first timeout from the alarm_map where it is
- // stored in absolute time.
- int64_t next_alarm_time_in_us = alarm_map_.begin()->first;
- EPOLL_VLOG(4) << "next_alarm_time = " << next_alarm_time_in_us
- << " now = " << now_in_us
- << " timeout_in_us = " << timeout_in_us_;
-
- int64_t wait_time_in_us;
- int64_t alarm_timeout_in_us = next_alarm_time_in_us - now_in_us;
-
- // If the next alarm is sooner than the default timeout, or if there is no
- // timeout (timeout_in_us_ == -1), wake up when the alarm should fire.
- // Otherwise use the default timeout.
- if (alarm_timeout_in_us < timeout_in_us_ || timeout_in_us_ < 0) {
- wait_time_in_us = std::max(alarm_timeout_in_us, static_cast<int64_t>(0));
- } else {
- wait_time_in_us = timeout_in_us_;
- }
-
- EPOLL_VLOG(4) << "wait_time_in_us = " << wait_time_in_us;
-
- // wait for events.
-
- WaitForEventsAndCallHandleEvents(wait_time_in_us, events_, events_size_);
- CallAndReregisterAlarmEvents();
- recorded_now_in_us_ = 0;
-}
-
-void SimpleEpollServer::SetFDReady(int fd, int events_to_fake) {
- auto fd_i = cb_map_.find(CBAndEventMask(NULL, 0, fd));
- if (cb_map_.end() != fd_i && fd_i->cb != NULL) {
- // This const_cast is necessary for LIST_HEAD_INSERT to work. Declaring
- // entry mutable is insufficient because LIST_HEAD_INSERT assigns the
- // forward pointer of the list head to the current cb_and_mask, and the
- // compiler complains that it can't assign a const T* to a T*.
- CBAndEventMask* cb_and_mask = const_cast<CBAndEventMask*>(&*fd_i);
- // Note that there is no clearly correct behavior here when
- // cb_and_mask->events_to_fake != 0 and this function is called.
- // Of the two operations:
- // cb_and_mask->events_to_fake = events_to_fake
- // cb_and_mask->events_to_fake |= events_to_fake
- // the first was picked because it discourages users from calling
- // SetFDReady repeatedly to build up the correct event set as it is more
- // efficient to call SetFDReady once with the correct, final mask.
- cb_and_mask->events_to_fake = events_to_fake;
- AddToReadyList(cb_and_mask);
- }
-}
-
-void SimpleEpollServer::SetFDNotReady(int fd) {
- auto fd_i = cb_map_.find(CBAndEventMask(NULL, 0, fd));
- if (cb_map_.end() != fd_i) {
- RemoveFromReadyList(*fd_i);
- }
-}
-
-bool SimpleEpollServer::IsFDReady(int fd) const {
- auto fd_i = cb_map_.find(CBAndEventMask(NULL, 0, fd));
- return (cb_map_.end() != fd_i && fd_i->cb != NULL &&
- fd_i->entry.le_prev != NULL);
-}
-
-void SimpleEpollServer::VerifyReadyList() const {
- int count = 0;
- CBAndEventMask* cur = ready_list_.lh_first;
- for (; cur; cur = cur->entry.le_next) {
- ++count;
- }
- for (cur = tmp_list_.lh_first; cur; cur = cur->entry.le_next) {
- ++count;
- }
- CHECK_EQ(ready_list_size_, count) << "Ready list size does not match count";
-}
-
-void SimpleEpollServer::RegisterAlarm(int64_t timeout_time_in_us, AlarmCB* ac) {
- EPOLL_VLOG(4) << "RegisteringAlarm " << ac << " at : " << timeout_time_in_us;
- CHECK(ac);
- if (all_alarms_.find(ac) != all_alarms_.end()) {
- EPOLL_BUG(epoll_bug_1_1) << "Alarm already exists";
- }
-
- auto alarm_iter = alarm_map_.insert(std::make_pair(timeout_time_in_us, ac));
-
- all_alarms_.insert(ac);
- // Pass the iterator to the EpollAlarmCallbackInterface.
- ac->OnRegistration(alarm_iter, this);
-}
-
-// Unregister a specific alarm callback: iterator_token must be a
-// valid iterator. The caller must ensure the validity of the iterator.
-void SimpleEpollServer::UnregisterAlarm(const AlarmRegToken& iterator_token) {
- AlarmCB* cb = iterator_token->second;
- EPOLL_VLOG(4) << "UnregisteringAlarm " << cb;
- alarm_map_.erase(iterator_token);
- all_alarms_.erase(cb);
- cb->OnUnregistration();
-}
-
-SimpleEpollServer::AlarmRegToken SimpleEpollServer::ReregisterAlarm(
- SimpleEpollServer::AlarmRegToken iterator_token,
- int64_t timeout_time_in_us) {
- AlarmCB* cb = iterator_token->second;
- alarm_map_.erase(iterator_token);
- return alarm_map_.emplace(timeout_time_in_us, cb);
-}
-
-int SimpleEpollServer::NumFDsRegistered() const {
- DCHECK_GE(cb_map_.size(), 1u);
- // Omit the internal FD (read_fd_)
- return cb_map_.size() - 1;
-}
-
-void SimpleEpollServer::Wake() {
- char data = 'd'; // 'd' is for data. It's good enough for me.
- int rv = write(write_fd_, &data, 1);
- DCHECK_EQ(rv, 1);
-}
-
-int64_t SimpleEpollServer::NowInUsec() const {
- return absl::GetCurrentTimeNanos() / 1000;
-}
-
-int64_t SimpleEpollServer::ApproximateNowInUsec() const {
- if (recorded_now_in_us_ != 0) {
- return recorded_now_in_us_;
- }
- return this->NowInUsec();
-}
-
-std::string SimpleEpollServer::EventMaskToString(int event_mask) {
- std::string s;
- if (event_mask & EPOLLIN) s += "EPOLLIN ";
- if (event_mask & EPOLLPRI) s += "EPOLLPRI ";
- if (event_mask & EPOLLOUT) s += "EPOLLOUT ";
- if (event_mask & EPOLLRDNORM) s += "EPOLLRDNORM ";
- if (event_mask & EPOLLRDBAND) s += "EPOLLRDBAND ";
- if (event_mask & EPOLLWRNORM) s += "EPOLLWRNORM ";
- if (event_mask & EPOLLWRBAND) s += "EPOLLWRBAND ";
- if (event_mask & EPOLLMSG) s += "EPOLLMSG ";
- if (event_mask & EPOLLERR) s += "EPOLLERR ";
- if (event_mask & EPOLLHUP) s += "EPOLLHUP ";
- if (event_mask & EPOLLONESHOT) s += "EPOLLONESHOT ";
- if (event_mask & EPOLLET) s += "EPOLLET ";
- return s;
-}
-
-void SimpleEpollServer::LogStateOnCrash() {
- EPOLL_LOG(ERROR)
- << "-------------------Epoll Server-------------------------";
- EPOLL_LOG(ERROR) << "Epoll server " << this << " polling on fd " << epoll_fd_;
- EPOLL_LOG(ERROR) << "timeout_in_us_: " << timeout_in_us_;
-
- // Log sessions with alarms.
- EPOLL_LOG(ERROR) << alarm_map_.size() << " alarms registered.";
- for (auto it = alarm_map_.begin(); it != alarm_map_.end(); ++it) {
- const bool skipped =
- alarms_reregistered_and_should_be_skipped_.find(it->second) !=
- alarms_reregistered_and_should_be_skipped_.end();
- EPOLL_LOG(ERROR) << "Alarm " << it->second << " registered at time "
- << it->first << " and should be skipped = " << skipped;
- }
-
- EPOLL_LOG(ERROR) << cb_map_.size() << " fd callbacks registered.";
- for (auto it = cb_map_.begin(); it != cb_map_.end(); ++it) {
- EPOLL_LOG(ERROR) << "fd: " << it->fd << " with mask " << it->event_mask
- << " registered with cb: " << it->cb;
- }
- EPOLL_LOG(ERROR)
- << "-------------------/Epoll Server------------------------";
-}
-
-////////////////////////////////////////////////////////////////////////////////
-////////////////////////////////////////////////////////////////////////////////
-
-void SimpleEpollServer::DelFD(int fd) const {
- struct epoll_event ee;
- memset(&ee, 0, sizeof(ee));
-#ifdef EPOLL_SERVER_EVENT_TRACING
- event_recorder_.RecordFDMaskEvent(fd, 0, "DelFD");
-#endif
- if (epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, fd, &ee)) {
- int saved_errno = errno;
- char buf[kErrorBufferSize];
- EPOLL_LOG(FATAL) << "Epoll set removal error for fd " << fd << ": "
- << strerror_r(saved_errno, buf, sizeof(buf));
- }
-}
-
-////////////////////////////////////////
-
-void SimpleEpollServer::AddFD(int fd, int event_mask) const {
- struct epoll_event ee;
- memset(&ee, 0, sizeof(ee));
- ee.events = event_mask | EPOLLERR | EPOLLHUP;
- ee.data.fd = fd;
-#ifdef EPOLL_SERVER_EVENT_TRACING
- event_recorder_.RecordFDMaskEvent(fd, ee.events, "AddFD");
-#endif
- if (epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, fd, &ee)) {
- int saved_errno = errno;
- char buf[kErrorBufferSize];
- EPOLL_LOG(FATAL) << "Epoll set insertion error for fd " << fd << ": "
- << strerror_r(saved_errno, buf, sizeof(buf));
- }
-}
-
-////////////////////////////////////////
-
-void SimpleEpollServer::ModFD(int fd, int event_mask) const {
- struct epoll_event ee;
- memset(&ee, 0, sizeof(ee));
- ee.events = event_mask | EPOLLERR | EPOLLHUP;
- ee.data.fd = fd;
-#ifdef EPOLL_SERVER_EVENT_TRACING
- event_recorder_.RecordFDMaskEvent(fd, ee.events, "ModFD");
-#endif
- EPOLL_VLOG(3) << "modifying fd= " << fd << " "
- << EventMaskToString(ee.events);
- if (epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, fd, &ee)) {
- int saved_errno = errno;
- char buf[kErrorBufferSize];
- EPOLL_LOG(FATAL) << "Epoll set modification error for fd " << fd << ": "
- << strerror_r(saved_errno, buf, sizeof(buf));
- }
-}
-
-////////////////////////////////////////
-
-void SimpleEpollServer::ModifyFD(int fd, int remove_event, int add_event) {
- auto fd_i = cb_map_.find(CBAndEventMask(NULL, 0, fd));
- if (cb_map_.end() == fd_i) {
- EPOLL_VLOG(2) << "Didn't find the fd " << fd << "in internal structures";
- return;
- }
-
- if (fd_i->cb != NULL) {
- int& event_mask = fd_i->event_mask;
- EPOLL_VLOG(3) << "fd= " << fd
- << " event_mask before: " << EventMaskToString(event_mask);
- event_mask &= ~remove_event;
- event_mask |= add_event;
-
- EPOLL_VLOG(3) << " event_mask after: " << EventMaskToString(event_mask);
-
- ModFD(fd, event_mask);
-
- fd_i->cb->OnModification(fd, event_mask);
- }
-}
-
-void SimpleEpollServer::WaitForEventsAndCallHandleEvents(
- int64_t timeout_in_us, struct epoll_event events[], int events_size) {
- if (timeout_in_us == 0 || ready_list_.lh_first != NULL) {
- // If ready list is not empty, then don't sleep at all.
- timeout_in_us = 0;
- } else if (timeout_in_us < 0) {
- EPOLL_LOG(INFO) << "Negative epoll timeout: " << timeout_in_us
- << "us; epoll will wait forever for events.";
- // If timeout_in_us is < 0 we are supposed to Wait forever. This means we
- // should set timeout_in_us to -1000 so we will
- // Wait(-1000/1000) == Wait(-1) == Wait forever.
- timeout_in_us = -1000;
- } else {
- // If timeout is specified, and the ready list is empty.
- if (timeout_in_us < 1000) {
- timeout_in_us = 1000;
- }
- }
- const int timeout_in_ms = timeout_in_us / 1000;
- int64_t expected_wakeup_us = NowInUsec() + timeout_in_us;
-
- int nfds = epoll_wait_impl(epoll_fd_, events, events_size, timeout_in_ms);
- EPOLL_VLOG(3) << "nfds=" << nfds;
-
-#ifdef EPOLL_SERVER_EVENT_TRACING
- event_recorder_.RecordEpollWaitEvent(timeout_in_ms, nfds);
-#endif
-
- // If you're wondering why the NowInUsec() is recorded here, the answer is
- // simple: If we did it before the epoll_wait_impl, then the max error for
- // the ApproximateNowInUs() call would be as large as the maximum length of
- // epoll_wait, which can be arbitrarily long. Since this would make
- // ApproximateNowInUs() worthless, we instead record the time -after- we've
- // done epoll_wait, which guarantees that the maximum error is the amount of
- // time it takes to process all the events generated by epoll_wait.
- recorded_now_in_us_ = NowInUsec();
-
- if (timeout_in_us > 0) {
- int64_t delta = NowInUsec() - expected_wakeup_us;
- last_delay_in_usec_ = delta > 0 ? delta : 0;
- } else {
- // timeout_in_us < 0 means we waited forever until an event;
- // timeout_in_us == 0 means there was no kernel delay to track.
- last_delay_in_usec_ = 0;
- }
-
- if (nfds > 0) {
- for (int i = 0; i < nfds; ++i) {
- int event_mask = events[i].events;
- int fd = events[i].data.fd;
- HandleEvent(fd, event_mask);
- }
- } else if (nfds < 0) {
- // Catch interrupted syscall and just ignore it and move on.
- if (errno != EINTR && errno != 0) {
- int saved_errno = errno;
- char buf[kErrorBufferSize];
- EPOLL_LOG(FATAL) << "Error " << saved_errno << " in epoll_wait: "
- << strerror_r(saved_errno, buf, sizeof(buf));
- }
- }
-
- // Now run through the ready list.
- if (ready_list_.lh_first) {
- CallReadyListCallbacks();
- }
-}
-
-void SimpleEpollServer::CallReadyListCallbacks() {
- // Check pre-conditions.
- DCHECK(tmp_list_.lh_first == NULL);
- // Swap out the ready_list_ into the tmp_list_ before traversing the list to
- // enable SetFDReady() to just push new items into the ready_list_.
- std::swap(ready_list_.lh_first, tmp_list_.lh_first);
- if (tmp_list_.lh_first) {
- tmp_list_.lh_first->entry.le_prev = &tmp_list_.lh_first;
- EpollEvent event(0);
- while (tmp_list_.lh_first != NULL) {
- DCHECK_GT(ready_list_size_, 0);
- CBAndEventMask* cb_and_mask = tmp_list_.lh_first;
- RemoveFromReadyList(*cb_and_mask);
-
- event.out_ready_mask = 0;
- event.in_events =
- cb_and_mask->events_asserted | cb_and_mask->events_to_fake;
- // TODO(fenix): get rid of the two separate fields in cb_and_mask.
- cb_and_mask->events_asserted = 0;
- cb_and_mask->events_to_fake = 0;
- {
- // OnEvent() may call UnRegister, so we set in_use, here. Any
- // UnRegister call will now simply set the cb to NULL instead of
- // invalidating the cb_and_mask object (by deleting the object in the
- // map to which cb_and_mask refers)
- AutoReset<bool> in_use_guard(&(cb_and_mask->in_use), true);
- cb_and_mask->cb->OnEvent(cb_and_mask->fd, &event);
- }
-
- // Since OnEvent may have called UnregisterFD, we must check here that
- // the callback is still valid. If it isn't, then UnregisterFD *was*
- // called, and we should now get rid of the object.
- if (cb_and_mask->cb == NULL) {
- cb_map_.erase(*cb_and_mask);
- } else if (event.out_ready_mask != 0) {
- cb_and_mask->events_to_fake = event.out_ready_mask;
- AddToReadyList(cb_and_mask);
- }
- }
- }
- DCHECK(tmp_list_.lh_first == NULL);
-}
-
-void SimpleEpollServer::CallAndReregisterAlarmEvents() {
- int64_t now_in_us = recorded_now_in_us_;
- DCHECK_NE(0, recorded_now_in_us_);
-
- TimeToAlarmCBMap::iterator erase_it;
-
- // execute alarms.
- for (auto i = alarm_map_.begin(); i != alarm_map_.end();) {
- if (i->first > now_in_us) {
- break;
- }
- AlarmCB* cb = i->second;
- // Execute the OnAlarm() only if we did not register
- // it in this loop itself.
- const bool added_in_this_round =
- alarms_reregistered_and_should_be_skipped_.find(cb) !=
- alarms_reregistered_and_should_be_skipped_.end();
- if (added_in_this_round) {
- ++i;
- continue;
- }
- all_alarms_.erase(cb);
- const int64_t new_timeout_time_in_us = cb->OnAlarm();
-
- erase_it = i;
- ++i;
- alarm_map_.erase(erase_it);
-
- if (new_timeout_time_in_us > 0) {
- // We add to hash_set only if the new timeout is <= now_in_us.
- // if timeout is > now_in_us then we have no fear that this alarm
- // can be reexecuted in this loop, and hence we do not need to
- // worry about a recursive loop.
- EPOLL_DVLOG(3) << "Reregistering alarm "
- << " " << cb << " " << new_timeout_time_in_us << " "
- << now_in_us;
- if (new_timeout_time_in_us <= now_in_us) {
- alarms_reregistered_and_should_be_skipped_.insert(cb);
- }
- RegisterAlarm(new_timeout_time_in_us, cb);
- }
- }
- alarms_reregistered_and_should_be_skipped_.clear();
-}
-
-EpollAlarm::EpollAlarm() : eps_(NULL), registered_(false) {}
-
-EpollAlarm::~EpollAlarm() { UnregisterIfRegistered(); }
-
-int64_t EpollAlarm::OnAlarm() {
- registered_ = false;
- return 0;
-}
-
-void EpollAlarm::OnRegistration(const SimpleEpollServer::AlarmRegToken& token,
- SimpleEpollServer* eps) {
- DCHECK_EQ(false, registered_);
-
- token_ = token;
- eps_ = eps;
- registered_ = true;
-}
-
-void EpollAlarm::OnUnregistration() { registered_ = false; }
-
-void EpollAlarm::OnShutdown(SimpleEpollServer* /*eps*/) {
- registered_ = false;
- eps_ = NULL;
-}
-
-// If the alarm was registered, unregister it.
-void EpollAlarm::UnregisterIfRegistered() {
- if (!registered_) {
- return;
- }
-
- eps_->UnregisterAlarm(token_);
-}
-
-void EpollAlarm::ReregisterAlarm(int64_t timeout_time_in_us) {
- DCHECK(registered_);
- token_ = eps_->ReregisterAlarm(token_, timeout_time_in_us);
-}
-
-} // namespace epoll_server
diff --git a/quiche/epoll_server/simple_epoll_server.h b/quiche/epoll_server/simple_epoll_server.h
deleted file mode 100644
index 405e306..0000000
--- a/quiche/epoll_server/simple_epoll_server.h
+++ /dev/null
@@ -1,1051 +0,0 @@
-// Copyright 2013 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.
-
-#ifndef QUICHE_EPOLL_SERVER_SIMPLE_EPOLL_SERVER_H_
-#define QUICHE_EPOLL_SERVER_SIMPLE_EPOLL_SERVER_H_
-
-#include <fcntl.h>
-#include <stddef.h>
-#include <stdint.h>
-#include <sys/queue.h>
-
-#include <map>
-#include <memory>
-#include <string>
-#include <unordered_map>
-#include <unordered_set>
-#include <vector>
-
-// #define EPOLL_SERVER_EVENT_TRACING 1
-//
-// Defining EPOLL_SERVER_EVENT_TRACING
-// causes code to exist which didn't before.
-// This code tracks each event generated by the epollserver,
-// as well as providing a per-fd-registered summary of
-// events. Note that enabling this code vastly slows
-// down operations, and uses substantially more
-// memory. For these reasons, it should only be enabled by developers doing
-// development at their workstations.
-//
-// A structure called 'EventRecorder' will exist when
-// the macro is defined. See the EventRecorder class interface
-// within the SimpleEpollServer class for more details.
-#ifdef EPOLL_SERVER_EVENT_TRACING
-#include <ostream>
-#endif
-
-#include <sys/epoll.h>
-
-#include "quiche/epoll_server/platform/api/epoll_logging.h"
-
-namespace epoll_server {
-
-class SimpleEpollServer;
-class EpollAlarmCallbackInterface;
-class ReadPipeCallback;
-
-struct EpollEvent {
- EpollEvent(int events) : in_events(events), out_ready_mask(0) {}
-
- int in_events; // incoming events
- int out_ready_mask; // the new event mask for ready list (0 means don't
- // get on the ready list). This field is always
- // initialized to 0 when the event is passed to
- // OnEvent.
-};
-
-// Callbacks which go into SimpleEpollServers are expected to derive from this
-// class.
-class EpollCallbackInterface {
- public:
- // Summary:
- // Called when the callback is registered into a SimpleEpollServer.
- // Args:
- // eps - the poll server into which this callback was registered
- // fd - the file descriptor which was registered
- // event_mask - the event mask (composed of EPOLLIN, EPOLLOUT, etc)
- // which was registered (and will initially be used
- // in the epoll() calls)
- virtual void OnRegistration(SimpleEpollServer* eps, int fd,
- int event_mask) = 0;
-
- // Summary:
- // Called when the event_mask is modified (for a file-descriptor)
- // Args:
- // fd - the file descriptor which was registered
- // event_mask - the event mask (composed of EPOLLIN, EPOLLOUT, etc)
- // which was is now curren (and will be used
- // in subsequent epoll() calls)
- virtual void OnModification(int fd, int event_mask) = 0;
-
- // Summary:
- // Called whenever an event occurs on the file-descriptor.
- // This is where the bulk of processing is expected to occur.
- // Args:
- // fd - the file descriptor which was registered
- // event - a struct that contains the event mask (composed of EPOLLIN,
- // EPOLLOUT, etc), a flag that indicates whether this is a true
- // epoll_wait event vs one from the ready list, and an output
- // parameter for OnEvent to inform the SimpleEpollServer whether to
- // put this fd on the ready list.
- virtual void OnEvent(int fd, EpollEvent* event) = 0;
-
- // Summary:
- // Called when the file-descriptor is unregistered from the poll-server.
- // Args:
- // fd - the file descriptor which was registered, and of this call, is now
- // unregistered.
- // replaced - If true, this callback is being replaced by another, otherwise
- // it is simply being removed.
- virtual void OnUnregistration(int fd, bool replaced) = 0;
-
- // Summary:
- // Called when the epoll server is shutting down. This is different from
- // OnUnregistration because the subclass may want to clean up memory.
- // This is called in leiu of OnUnregistration.
- // Args:
- // fd - the file descriptor which was registered.
- virtual void OnShutdown(SimpleEpollServer* eps, int fd) = 0;
-
- // Summary:
- // Returns a name describing the class for use in debug/error reporting.
- virtual std::string Name() const = 0;
-
- virtual ~EpollCallbackInterface() {}
-
- protected:
- EpollCallbackInterface() {}
-};
-
-////////////////////////////////////////////////////////////////////////////////
-////////////////////////////////////////////////////////////////////////////////
-
-class SimpleEpollServer {
- public:
- typedef EpollAlarmCallbackInterface AlarmCB;
- typedef EpollCallbackInterface CB;
-
- typedef std::multimap<int64_t, AlarmCB*> TimeToAlarmCBMap;
- typedef TimeToAlarmCBMap::iterator AlarmRegToken;
-
- // Summary:
- // Constructor:
- // By default, we don't wait any amount of time for events, and
- // we suggest to the epoll-system that we're going to use on-the-order
- // of 1024 FDs.
- SimpleEpollServer();
-
- SimpleEpollServer(const SimpleEpollServer&) = delete;
- SimpleEpollServer operator=(const SimpleEpollServer&) = delete;
-
- ////////////////////////////////////////
-
- // Destructor
- virtual ~SimpleEpollServer();
-
- ////////////////////////////////////////
-
- // Summary
- // Register a callback to be called whenever an event contained
- // in the set of events included in event_mask occurs on the
- // file-descriptor 'fd'
- //
- // Note that only one callback is allowed to be registered for
- // any specific file-decriptor.
- //
- // If a callback is registered for a file-descriptor which has already
- // been registered, then the previous callback is unregistered with
- // the 'replaced' flag set to true. I.e. the previous callback's
- // OnUnregistration() function is called like so:
- // OnUnregistration(fd, true);
- //
- // The epoll server does NOT take on ownership of the callback: the callback
- // creator is responsible for managing that memory.
- //
- // Args:
- // fd - a valid file-descriptor
- // cb - an instance of a subclass of EpollCallbackInterface
- // event_mask - a combination of (EPOLLOUT, EPOLLIN.. etc) indicating
- // the events for which the callback would like to be
- // called.
- virtual void RegisterFD(int fd, CB* cb, int event_mask);
-
- ////////////////////////////////////////
-
- // Summary:
- // A shortcut for RegisterFD which sets things up such that the
- // callback is called when 'fd' is available for writing.
- // Args:
- // fd - a valid file-descriptor
- // cb - an instance of a subclass of EpollCallbackInterface
- virtual void RegisterFDForWrite(int fd, CB* cb);
-
- ////////////////////////////////////////
-
- // Summary:
- // A shortcut for RegisterFD which sets things up such that the
- // callback is called when 'fd' is available for reading or writing.
- // Args:
- // fd - a valid file-descriptor
- // cb - an instance of a subclass of EpollCallbackInterface
- virtual void RegisterFDForReadWrite(int fd, CB* cb);
-
- ////////////////////////////////////////
-
- // Summary:
- // A shortcut for RegisterFD which sets things up such that the
- // callback is called when 'fd' is available for reading.
- // Args:
- // fd - a valid file-descriptor
- // cb - an instance of a subclass of EpollCallbackInterface
- virtual void RegisterFDForRead(int fd, CB* cb);
-
- ////////////////////////////////////////
-
- // Summary:
- // Removes the FD and the associated callback from the pollserver.
- // If the callback is registered with other FDs, they will continue
- // to be processed using the callback without modification.
- // If the file-descriptor specified is not registered in the
- // epoll_server, then nothing happens as a result of this call.
- // Args:
- // fd - the file-descriptor which should no-longer be monitored.
- virtual void UnregisterFD(int fd);
-
- ////////////////////////////////////////
-
- // Summary:
- // Modifies the event mask for the file-descriptor, replacing
- // the old event_mask with the new one specified here.
- // If the file-descriptor specified is not registered in the
- // epoll_server, then nothing happens as a result of this call.
- // Args:
- // fd - the fd whose event mask should be modified.
- // event_mask - the new event mask.
- virtual void ModifyCallback(int fd, int event_mask);
-
- ////////////////////////////////////////
-
- // Summary:
- // Modifies the event mask for the file-descriptor such that we
- // no longer request events when 'fd' is readable.
- // If the file-descriptor specified is not registered in the
- // epoll_server, then nothing happens as a result of this call.
- // Args:
- // fd - the fd whose event mask should be modified.
- virtual void StopRead(int fd);
-
- ////////////////////////////////////////
-
- // Summary:
- // Modifies the event mask for the file-descriptor such that we
- // request events when 'fd' is readable.
- // If the file-descriptor specified is not registered in the
- // epoll_server, then nothing happens as a result of this call.
- // Args:
- // fd - the fd whose event mask should be modified.
- virtual void StartRead(int fd);
-
- ////////////////////////////////////////
-
- // Summary:
- // Modifies the event mask for the file-descriptor such that we
- // no longer request events when 'fd' is writable.
- // If the file-descriptor specified is not registered in the
- // epoll_server, then nothing happens as a result of this call.
- // Args:
- // fd - the fd whose event mask should be modified.
- virtual void StopWrite(int fd);
-
- ////////////////////////////////////////
-
- // Summary:
- // Modifies the event mask for the file-descriptor such that we
- // request events when 'fd' is writable.
- // If the file-descriptor specified is not registered in the
- // epoll_server, then nothing happens as a result of this call.
- // Args:
- // fd - the fd whose event mask should be modified.
- virtual void StartWrite(int fd);
-
- ////////////////////////////////////////
-
- // Summary:
- // Looks up the callback associated with the file-descriptor 'fd'.
- // If a callback is associated with this file-descriptor, then
- // it's OnEvent() method is called with the file-descriptor 'fd',
- // and event_mask 'event_mask'
- //
- // If no callback is registered for this file-descriptor, nothing
- // will happen as a result of this call.
- //
- // This function is used internally by the SimpleEpollServer, but is
- // available publicly so that events might be 'faked'. Calling
- // this function with an fd and event_mask is equivalent (as far
- // as the callback is concerned) to having a real event generated
- // by epoll (except, of course, that read(), etc won't necessarily
- // be able to read anything)
- // Args:
- // fd - the file-descriptor on which an event has occurred.
- // event_mask - a bitmask representing the events which have occurred
- // on/for this fd. This bitmask is composed of
- // POLLIN, POLLOUT, etc.
- //
- void HandleEvent(int fd, int event_mask);
-
- // Summary:
- // Call this when you want the pollserver to
- // wait for events and execute the callbacks associated with
- // the file-descriptors on which those events have occurred.
- // Depending on the value of timeout_in_us_, this may or may
- // not return immediately. Please reference the set_timeout()
- // function for the specific behaviour.
- virtual void WaitForEventsAndExecuteCallbacks();
-
- // Summary:
- // When an fd is registered to use edge trigger notification, the ready
- // list can be used to simulate level trigger semantics. Edge trigger
- // registration doesn't send an initial event, and only rising edge (going
- // from blocked to unblocked) events are sent. A callback can put itself on
- // the ready list by calling SetFDReady() after calling RegisterFD(). The
- // OnEvent method of all callbacks associated with the fds on the ready
- // list will be called immediately after processing the events returned by
- // epoll_wait(). The fd is removed from the ready list before the
- // callback's OnEvent() method is invoked. To stay on the ready list, the
- // OnEvent() (or some function in that call chain) must call SetFDReady
- // again. When a fd is unregistered using UnregisterFD(), the fd is
- // automatically removed from the ready list.
- //
- // When the callback for a edge triggered fd hits the falling edge (about
- // to block, either because of it got an EAGAIN, or had a short read/write
- // operation), it should remove itself from the ready list using
- // SetFDNotReady() (since OnEvent cannot distinguish between invocation
- // from the ready list vs from a normal epoll event). All four ready list
- // methods are safe to be called within the context of the callbacks.
- //
- // Since the ready list invokes EpollCallbackInterface::OnEvent, only fds
- // that are registered with the SimpleEpollServer will be put on the ready
- // list. SetFDReady() and SetFDNotReady() will do nothing if the
- // SimpleEpollServer doesn't know about the fd passed in.
- //
- // Since the ready list cannot reliably determine proper set of events
- // which should be sent to the callback, SetFDReady() requests the caller
- // to provide the ready list with the event mask, which will be used later
- // when OnEvent() is invoked by the ready list. Hence, the event_mask
- // passedto SetFDReady() does not affect the actual epoll registration of
- // the fd with the kernel. If a fd is already put on the ready list, and
- // SetFDReady() is called again for that fd with a different event_mask,
- // the event_mask will be updated.
- virtual void SetFDReady(int fd, int events_to_fake);
-
- virtual void SetFDNotReady(int fd);
-
- // Summary:
- // IsFDReady(), ReadyListSize(), and VerifyReadyList are intended as
- // debugging tools and for writing unit tests.
- // ISFDReady() returns whether a fd is in the ready list.
- // ReadyListSize() returns the number of fds on the ready list.
- // VerifyReadyList() checks the consistency of internal data structure. It
- // will CHECK if it finds an error.
- virtual bool IsFDReady(int fd) const;
-
- size_t ReadyListSize() const { return ready_list_size_; }
-
- void VerifyReadyList() const;
-
- ////////////////////////////////////////
-
- // Summary:
- // Registers an alarm 'ac' to go off at time 'timeout_time_in_us'.
- // If the callback returns a positive number from its OnAlarm() function,
- // then the callback will be re-registered at that time, else the alarm
- // owner is responsible for freeing up memory.
- //
- // Important: A give AlarmCB* can not be registered again if it is already
- // registered. If a user wants to register a callback again it should first
- // unregister the previous callback before calling RegisterAlarm again.
- // Args:
- // timeout_time_in_us - the absolute time at which the alarm should go off
- // ac - the alarm which will be called.
- virtual void RegisterAlarm(int64_t timeout_time_in_us, AlarmCB* ac);
-
- // Summary:
- // Registers an alarm 'ac' to go off at time: (ApproximateNowInUs() +
- // delta_in_us). While this is somewhat less accurate (see the description
- // for ApproximateNowInUs() to see how 'approximate'), the error is never
- // worse than the amount of time it takes to process all events in one
- // WaitForEvents. As with 'RegisterAlarm()', if the callback returns a
- // positive number from its OnAlarm() function, then the callback will be
- // re-registered at that time, else the alarm owner is responsible for
- // freeing up memory.
- // Note that this function is purely a convienence. The
- // same thing may be accomplished by using RegisterAlarm with
- // ApproximateNowInUs() directly.
- //
- // Important: A give AlarmCB* can not be registered again if it is already
- // registered. If a user wants to register a callback again it should first
- // unregister the previous callback before calling RegisterAlarm again.
- // Args:
- // delta_in_us - the delta in microseconds from the ApproximateTimeInUs() at
- // which point the alarm should go off.
- // ac - the alarm which will be called.
- void RegisterAlarmApproximateDelta(int64_t delta_in_us, AlarmCB* ac) {
- RegisterAlarm(ApproximateNowInUsec() + delta_in_us, ac);
- }
-
- ////////////////////////////////////////
-
- // Summary:
- // Unregister the alarm referred to by iterator_token; Callers should
- // be warned that a token may have become already invalid when OnAlarm()
- // is called, was unregistered, or OnShutdown was called on that alarm.
- // Args:
- // iterator_token - iterator to the alarm callback to unregister.
- virtual void UnregisterAlarm(
- const SimpleEpollServer::AlarmRegToken& iterator_token);
-
- virtual SimpleEpollServer::AlarmRegToken ReregisterAlarm(
- SimpleEpollServer::AlarmRegToken iterator_token,
- int64_t timeout_time_in_us);
-
- ////////////////////////////////////////
-
- // Summary:
- // returns the number of file-descriptors registered in this
- // SimpleEpollServer.
- // Returns:
- // number of FDs registered (discounting the internal pipe used for Wake)
- virtual int NumFDsRegistered() const;
-
- // Summary:
- // Force the epoll server to wake up (by writing to an internal pipe).
- virtual void Wake();
-
- // Summary:
- // Wrapper around WallTimer's NowInUsec. We do this so that we can test
- // SimpleEpollServer without using the system clock (and can avoid the
- // flakiness that would ensue)
- // Returns:
- // the current time as number of microseconds since the Unix epoch.
- virtual int64_t NowInUsec() const;
-
- // Summary:
- // Since calling NowInUsec() many thousands of times per
- // WaitForEventsAndExecuteCallbacks function call is, to say the least,
- // inefficient, we allow users to use an approximate time instead. The
- // time returned from this function is as accurate as NowInUsec() when
- // WaitForEventsAndExecuteCallbacks is not an ancestor of the caller's
- // callstack.
- // However, when WaitForEventsAndExecuteCallbacks -is- an ancestor, then
- // this function returns the time at which the
- // WaitForEventsAndExecuteCallbacks function started to process events or
- // alarms.
- //
- // Essentially, this function makes available a fast and mostly accurate
- // mechanism for getting the time for any function handling an event or
- // alarm. When functions which are not handling callbacks or alarms call
- // this function, they get the slow and "absolutely" accurate time.
- //
- // Users should be encouraged to use this function.
- // Returns:
- // the "approximate" current time as number of microseconds since the Unix
- // epoch.
- virtual int64_t ApproximateNowInUsec() const;
-
- static std::string EventMaskToString(int event_mask);
-
- // Summary:
- // Logs the state of the epoll server with EPOLL_LOG(ERROR).
- void LogStateOnCrash();
-
- // Summary:
- // Set the timeout to the value specified.
- // If the timeout is set to a negative number,
- // WaitForEventsAndExecuteCallbacks() will only return when an event has
- // occurred
- // If the timeout is set to zero,
- // WaitForEventsAndExecuteCallbacks() will return immediately
- // If the timeout is set to a positive number,
- // WaitForEventsAndExecuteCallbacks() will return when an event has
- // occurred, or when timeout_in_us microseconds has elapsed, whichever
- // is first.
- // Args:
- // timeout_in_us - value specified depending on behaviour desired.
- // See above.
- void set_timeout_in_us(int64_t timeout_in_us) {
- timeout_in_us_ = timeout_in_us;
- }
-
- ////////////////////////////////////////
-
- // Summary:
- // Accessor for the current value of timeout_in_us.
- int timeout_in_us_for_test() const { return timeout_in_us_; }
-
- // Summary:
- // Returns true when the SimpleEpollServer() is being destroyed.
- bool in_shutdown() const { return in_shutdown_; }
- bool ShutdownCalled() const { return in_shutdown(); }
-
- // Compatibility stub.
- void Shutdown() {}
-
- // Summary:
- // A function for implementing the ready list. It invokes OnEvent for each
- // of the fd in the ready list, and takes care of adding them back to the
- // ready list if the callback requests it (by checking that out_ready_mask
- // is non-zero).
- void CallReadyListCallbacks();
-
- int64_t LastDelayInUsec() const { return last_delay_in_usec_; }
-
- protected:
- virtual void SetNonblocking(int fd);
-
- // This exists here so that we can override this function in unittests
- // in order to make effective mock SimpleEpollServer objects.
- virtual int epoll_wait_impl(int epfd, struct epoll_event* events,
- int max_events, int timeout_in_ms);
-
- // this struct is used internally, and is never used by anything external
- // to this class. Some of its members are declared mutable to get around the
- // restriction imposed by hash_set. Since hash_set knows nothing about the
- // objects it stores, it has to assume that every bit of the object is used
- // in the hash function and equal_to comparison. Thus hash_set::iterator is a
- // const iterator. In this case, the only thing that must stay constant is
- // fd. Everything else are just along for the ride and changing them doesn't
- // compromise the hash_set integrity.
- struct CBAndEventMask {
- CBAndEventMask()
- : cb(NULL),
- fd(-1),
- event_mask(0),
- events_asserted(0),
- events_to_fake(0),
- in_use(false) {
- entry.le_next = NULL;
- entry.le_prev = NULL;
- }
-
- CBAndEventMask(EpollCallbackInterface* cb, int event_mask, int fd)
- : cb(cb),
- fd(fd),
- event_mask(event_mask),
- events_asserted(0),
- events_to_fake(0),
- in_use(false) {
- entry.le_next = NULL;
- entry.le_prev = NULL;
- }
-
- // Required operator for hash_set. Normally operator== should be a free
- // standing function. However, since CBAndEventMask is a protected type and
- // it will never be a base class, it makes no difference.
- bool operator==(const CBAndEventMask& cb_and_mask) const {
- return fd == cb_and_mask.fd;
- }
- // A callback. If the fd is unregistered inside the callchain of OnEvent,
- // the cb will be set to NULL.
- mutable EpollCallbackInterface* cb;
-
- mutable LIST_ENTRY(CBAndEventMask) entry;
- // file descriptor registered with the epoll server.
- int fd;
- // the current event_mask registered for this callback.
- mutable int event_mask;
- // the event_mask that was returned by epoll
- mutable int events_asserted;
- // the event_mask for the ready list to use to call OnEvent.
- mutable int events_to_fake;
- // toggle around calls to OnEvent to tell UnregisterFD to not erase the
- // iterator because HandleEvent is using it.
- mutable bool in_use;
- };
-
- // Custom hash function to be used by hash_set.
- struct CBAndEventMaskHash {
- size_t operator()(const CBAndEventMask& cb_and_eventmask) const {
- return static_cast<size_t>(cb_and_eventmask.fd);
- }
- };
-
- using FDToCBMap = std::unordered_set<CBAndEventMask, CBAndEventMaskHash>;
-
- // the following four functions are OS-specific, and are likely
- // to be changed in a subclass if the poll/select method is changed
- // from epoll.
-
- // Summary:
- // Deletes a file-descriptor from the set of FDs that should be
- // monitored with epoll.
- // Note that this only deals with modifying data relating -directly-
- // with the epoll call-- it does not modify any data within the
- // epoll_server.
- // Args:
- // fd - the file descriptor to-be-removed from the monitoring set
- virtual void DelFD(int fd) const;
-
- ////////////////////////////////////////
-
- // Summary:
- // Adds a file-descriptor to the set of FDs that should be
- // monitored with epoll.
- // Note that this only deals with modifying data relating -directly-
- // with the epoll call.
- // Args:
- // fd - the file descriptor to-be-added to the monitoring set
- // event_mask - the event mask (consisting of EPOLLIN, EPOLLOUT, etc
- // OR'd together) which will be associated with this
- // FD initially.
- virtual void AddFD(int fd, int event_mask) const;
-
- ////////////////////////////////////////
-
- // Summary:
- // Modifies a file-descriptor in the set of FDs that should be
- // monitored with epoll.
- // Note that this only deals with modifying data relating -directly-
- // with the epoll call.
- // Args:
- // fd - the file descriptor to-be-added to the monitoring set
- // event_mask - the event mask (consisting of EPOLLIN, EPOLLOUT, etc
- // OR'd together) which will be associated with this
- // FD after this call.
- virtual void ModFD(int fd, int event_mask) const;
-
- ////////////////////////////////////////
-
- // Summary:
- // Modified the event mask associated with an FD in the set of
- // data needed by epoll.
- // Events are removed before they are added, thus, if ~0 is put
- // in 'remove_event', whatever is put in 'add_event' will be
- // the new event mask.
- // If the file-descriptor specified is not registered in the
- // epoll_server, then nothing happens as a result of this call.
- // Args:
- // fd - the file descriptor whose event mask is to be modified
- // remove_event - the events which are to be removed from the current
- // event_mask
- // add_event - the events which are to be added to the current event_mask
- //
- //
- virtual void ModifyFD(int fd, int remove_event, int add_event);
-
- ////////////////////////////////////////
-
- // Summary:
- // Waits for events, and calls HandleEvents() for each
- // fd, event pair discovered to possibly have an event.
- // Note that a callback (B) may get a spurious event if
- // another callback (A) has closed a file-descriptor N, and
- // the callback (B) has a newly opened file-descriptor, which
- // also happens to be N.
- virtual void WaitForEventsAndCallHandleEvents(int64_t timeout_in_us,
- struct epoll_event events[],
- int events_size);
-
- // Summary:
- // An internal function for implementing the ready list. It adds a fd's
- // CBAndEventMask to the ready list. If the fd is already on the ready
- // list, it is a no-op.
- void AddToReadyList(CBAndEventMask* cb_and_mask);
-
- // Summary:
- // An internal function for implementing the ready list. It remove a fd's
- // CBAndEventMask from the ready list. If the fd is not on the ready list,
- // it is a no-op.
- void RemoveFromReadyList(const CBAndEventMask& cb_and_mask);
-
- // Summary:
- // Calls any pending alarms that should go off and reregisters them if they
- // were recurring.
- virtual void CallAndReregisterAlarmEvents();
-
- // The file-descriptor created for epolling
- int epoll_fd_;
-
- // The mapping of file-descriptor to CBAndEventMasks
- FDToCBMap cb_map_;
-
- // Custom hash function to be used by hash_set.
- struct AlarmCBHash {
- size_t operator()(AlarmCB* const& p) const {
- return reinterpret_cast<size_t>(p);
- }
- };
-
- // TODO(sushantj): Having this hash_set is avoidable. We currently have it
- // only so that we can enforce stringent checks that a caller can not register
- // the same alarm twice. One option is to have an implementation in which
- // this hash_set is used only in the debug mode.
- using AlarmCBMap = std::unordered_set<AlarmCB*, AlarmCBHash>;
- AlarmCBMap all_alarms_;
-
- TimeToAlarmCBMap alarm_map_;
-
- // The amount of time in microseconds that we'll wait before returning
- // from the WaitForEventsAndExecuteCallbacks() function.
- // If this is positive, wait that many microseconds.
- // If this is negative, wait forever, or for the first event that occurs
- // If this is zero, never wait for an event.
- int64_t timeout_in_us_;
-
- // This is nonzero only after the invocation of epoll_wait_impl within
- // WaitForEventsAndCallHandleEvents and before the function
- // WaitForEventsAndExecuteCallbacks returns. At all other times, this is
- // zero. This enables us to have relatively accurate time returned from the
- // ApproximateNowInUs() function. See that function for more details.
- int64_t recorded_now_in_us_;
-
- // This is used to implement CallAndReregisterAlarmEvents. This stores
- // all alarms that were reregistered because OnAlarm() returned a
- // value > 0 and the time at which they should be executed is less that
- // the current time. By storing such alarms in this map we ensure
- // that while calling CallAndReregisterAlarmEvents we do not call
- // OnAlarm on any alarm in this set. This ensures that we do not
- // go in an infinite loop.
- AlarmCBMap alarms_reregistered_and_should_be_skipped_;
-
- LIST_HEAD(ReadyList, CBAndEventMask) ready_list_;
- LIST_HEAD(TmpList, CBAndEventMask) tmp_list_;
- int ready_list_size_;
- // TODO(alyssar): make this into something that scales up.
- static const int events_size_ = 256;
- struct epoll_event events_[256];
-
-#ifdef EPOLL_SERVER_EVENT_TRACING
- struct EventRecorder {
- public:
- EventRecorder() : num_records_(0), record_threshold_(10000) {}
-
- ~EventRecorder() { Clear(); }
-
- // When a number of events equals the record threshold,
- // the collected data summary for all FDs will be written
- // to EPOLL_LOG(INFO). Note that this does not include the
- // individual events (if you'reinterested in those, you'll
- // have to get at them programmatically).
- // After any such flushing to EPOLL_LOG(INFO) all events will
- // be cleared.
- // Note that the definition of an 'event' is a bit 'hazy',
- // as it includes the 'Unregistration' event, and perhaps
- // others.
- void set_record_threshold(int64_t new_threshold) {
- record_threshold_ = new_threshold;
- }
-
- void Clear() {
- for (int i = 0; i < debug_events_.size(); ++i) {
- delete debug_events_[i];
- }
- debug_events_.clear();
- unregistered_fds_.clear();
- event_counts_.clear();
- }
-
- void MaybeRecordAndClear() {
- ++num_records_;
- if ((num_records_ > record_threshold_) && (record_threshold_ > 0)) {
- EPOLL_LOG(INFO) << "\n" << *this;
- num_records_ = 0;
- Clear();
- }
- }
-
- void RecordFDMaskEvent(int fd, int mask, const char* function) {
- FDMaskOutput* fdmo = new FDMaskOutput(fd, mask, function);
- debug_events_.push_back(fdmo);
- MaybeRecordAndClear();
- }
-
- void RecordEpollWaitEvent(int timeout_in_ms, int num_events_generated) {
- EpollWaitOutput* ewo =
- new EpollWaitOutput(timeout_in_ms, num_events_generated);
- debug_events_.push_back(ewo);
- MaybeRecordAndClear();
- }
-
- void RecordEpollEvent(int fd, int event_mask) {
- Events& events_for_fd = event_counts_[fd];
- events_for_fd.AssignFromMask(event_mask);
- MaybeRecordAndClear();
- }
-
- friend ostream& operator<<(ostream& os, const EventRecorder& er) {
- for (int i = 0; i < er.unregistered_fds_.size(); ++i) {
- os << "fd: " << er.unregistered_fds_[i] << "\n";
- os << er.unregistered_fds_[i];
- }
- for (EventCountsMap::const_iterator i = er.event_counts_.begin();
- i != er.event_counts_.end(); ++i) {
- os << "fd: " << i->first << "\n";
- os << i->second;
- }
- for (int i = 0; i < er.debug_events_.size(); ++i) {
- os << *(er.debug_events_[i]) << "\n";
- }
- return os;
- }
-
- void RecordUnregistration(int fd) {
- EventCountsMap::iterator i = event_counts_.find(fd);
- if (i != event_counts_.end()) {
- unregistered_fds_.push_back(i->second);
- event_counts_.erase(i);
- }
- MaybeRecordAndClear();
- }
-
- protected:
- class DebugOutput {
- public:
- friend ostream& operator<<(ostream& os, const DebugOutput& debug_output) {
- debug_output.OutputToStream(os);
- return os;
- }
- virtual void OutputToStream(ostream* os) const = 0;
- virtual ~DebugOutput() {}
- };
-
- class FDMaskOutput : public DebugOutput {
- public:
- FDMaskOutput(int fd, int mask, const char* function)
- : fd_(fd), mask_(mask), function_(function) {}
- virtual void OutputToStream(ostream* os) const {
- (*os) << "func: " << function_ << "\tfd: " << fd_;
- if (mask_ != 0) {
- (*os) << "\tmask: " << EventMaskToString(mask_);
- }
- }
- int fd_;
- int mask_;
- const char* function_;
- };
-
- class EpollWaitOutput : public DebugOutput {
- public:
- EpollWaitOutput(int timeout_in_ms, int num_events_generated)
- : timeout_in_ms_(timeout_in_ms),
- num_events_generated_(num_events_generated) {}
- virtual void OutputToStream(ostream* os) const {
- (*os) << "timeout_in_ms: " << timeout_in_ms_
- << "\tnum_events_generated: " << num_events_generated_;
- }
-
- protected:
- int timeout_in_ms_;
- int num_events_generated_;
- };
-
- struct Events {
- Events()
- : epoll_in(0),
- epoll_pri(0),
- epoll_out(0),
- epoll_rdnorm(0),
- epoll_rdband(0),
- epoll_wrnorm(0),
- epoll_wrband(0),
- epoll_msg(0),
- epoll_err(0),
- epoll_hup(0),
- epoll_oneshot(0),
- epoll_et(0) {}
-
- void AssignFromMask(int event_mask) {
- if (event_mask & EPOLLIN) ++epoll_in;
- if (event_mask & EPOLLPRI) ++epoll_pri;
- if (event_mask & EPOLLOUT) ++epoll_out;
- if (event_mask & EPOLLRDNORM) ++epoll_rdnorm;
- if (event_mask & EPOLLRDBAND) ++epoll_rdband;
- if (event_mask & EPOLLWRNORM) ++epoll_wrnorm;
- if (event_mask & EPOLLWRBAND) ++epoll_wrband;
- if (event_mask & EPOLLMSG) ++epoll_msg;
- if (event_mask & EPOLLERR) ++epoll_err;
- if (event_mask & EPOLLHUP) ++epoll_hup;
- if (event_mask & EPOLLONESHOT) ++epoll_oneshot;
- if (event_mask & EPOLLET) ++epoll_et;
- }
-
- friend ostream& operator<<(ostream& os, const Events& ev) {
- if (ev.epoll_in) {
- os << "\t EPOLLIN: " << ev.epoll_in << "\n";
- }
- if (ev.epoll_pri) {
- os << "\t EPOLLPRI: " << ev.epoll_pri << "\n";
- }
- if (ev.epoll_out) {
- os << "\t EPOLLOUT: " << ev.epoll_out << "\n";
- }
- if (ev.epoll_rdnorm) {
- os << "\t EPOLLRDNORM: " << ev.epoll_rdnorm << "\n";
- }
- if (ev.epoll_rdband) {
- os << "\t EPOLLRDBAND: " << ev.epoll_rdband << "\n";
- }
- if (ev.epoll_wrnorm) {
- os << "\t EPOLLWRNORM: " << ev.epoll_wrnorm << "\n";
- }
- if (ev.epoll_wrband) {
- os << "\t EPOLLWRBAND: " << ev.epoll_wrband << "\n";
- }
- if (ev.epoll_msg) {
- os << "\t EPOLLMSG: " << ev.epoll_msg << "\n";
- }
- if (ev.epoll_err) {
- os << "\t EPOLLERR: " << ev.epoll_err << "\n";
- }
- if (ev.epoll_hup) {
- os << "\t EPOLLHUP: " << ev.epoll_hup << "\n";
- }
- if (ev.epoll_oneshot) {
- os << "\t EPOLLONESHOT: " << ev.epoll_oneshot << "\n";
- }
- if (ev.epoll_et) {
- os << "\t EPOLLET: " << ev.epoll_et << "\n";
- }
- return os;
- }
-
- unsigned int epoll_in;
- unsigned int epoll_pri;
- unsigned int epoll_out;
- unsigned int epoll_rdnorm;
- unsigned int epoll_rdband;
- unsigned int epoll_wrnorm;
- unsigned int epoll_wrband;
- unsigned int epoll_msg;
- unsigned int epoll_err;
- unsigned int epoll_hup;
- unsigned int epoll_oneshot;
- unsigned int epoll_et;
- };
-
- std::vector<DebugOutput*> debug_events_;
- std::vector<Events> unregistered_fds_;
- using EventCountsMap = std::unordered_map<int, Events>;
- EventCountsMap event_counts_;
- int64_t num_records_;
- int64_t record_threshold_;
- };
-
- void ClearEventRecords() { event_recorder_.Clear(); }
- void WriteEventRecords(ostream* os) const { (*os) << event_recorder_; }
-
- mutable EventRecorder event_recorder_;
-
-#endif
-
- private:
- // Helper functions used in the destructor.
- void CleanupFDToCBMap();
- void CleanupTimeToAlarmCBMap();
-
- // The callback registered to the fds below. As the purpose of their
- // registration is to wake the epoll server it just clears the pipe and
- // returns.
- std::unique_ptr<ReadPipeCallback> wake_cb_;
-
- // A pipe owned by the epoll server. The server will be registered to listen
- // on read_fd_ and can be woken by Wake() which writes to write_fd_.
- int read_fd_;
- int write_fd_;
-
- // This boolean is checked to see if it is false at the top of the
- // WaitForEventsAndExecuteCallbacks function. If not, then it either returns
- // without doing work, and logs to ERROR, or aborts the program (in
- // DEBUG mode). If so, then it sets the bool to true, does work, and
- // sets it back to false when done. This catches unwanted recursion.
- bool in_wait_for_events_and_execute_callbacks_;
-
- // Returns true when the SimpleEpollServer() is being destroyed.
- bool in_shutdown_;
- int64_t last_delay_in_usec_;
-};
-
-class EpollAlarmCallbackInterface {
- public:
- // Summary:
- // Called when an alarm times out. Invalidates an AlarmRegToken.
- // WARNING: If a token was saved to refer to an alarm callback, OnAlarm must
- // delete it, as the reference is no longer valid.
- // Returns:
- // the unix time (in microseconds) at which this alarm should be signaled
- // again, or 0 if the alarm should be removed.
- virtual int64_t OnAlarm() = 0;
-
- // Summary:
- // Called when the an alarm is registered. Invalidates an AlarmRegToken.
- // Args:
- // token: the iterator to the alarm registered in the alarm map.
- // WARNING: this token becomes invalid when the alarm fires, is
- // unregistered, or OnShutdown is called on that alarm.
- // eps: the epoll server the alarm is registered with.
- virtual void OnRegistration(const SimpleEpollServer::AlarmRegToken& token,
- SimpleEpollServer* eps) = 0;
-
- // Summary:
- // Called when the an alarm is unregistered.
- // WARNING: It is not valid to unregister a callback and then use the token
- // that was saved to refer to the callback.
- virtual void OnUnregistration() = 0;
-
- // Summary:
- // Called when the epoll server is shutting down.
- // Invalidates the AlarmRegToken that was given when this alarm was
- // registered.
- virtual void OnShutdown(SimpleEpollServer* eps) = 0;
-
- virtual ~EpollAlarmCallbackInterface() {}
-
- protected:
- EpollAlarmCallbackInterface() {}
-};
-
-// A simple alarm which unregisters itself on destruction.
-//
-// PLEASE NOTE:
-// Any classes overriding these functions must either call the implementation
-// of the parent class, or is must otherwise make sure that the 'registered_'
-// boolean and the token, 'token_', are updated appropriately.
-class EpollAlarm : public EpollAlarmCallbackInterface {
- public:
- EpollAlarm();
-
- ~EpollAlarm() override;
-
- // Marks the alarm as unregistered and returns 0. The return value may be
- // safely ignored by subclasses.
- int64_t OnAlarm() override;
-
- // Marks the alarm as registered, and stores the token.
- void OnRegistration(const SimpleEpollServer::AlarmRegToken& token,
- SimpleEpollServer* eps) override;
-
- // Marks the alarm as unregistered.
- void OnUnregistration() override;
-
- // Marks the alarm as unregistered.
- void OnShutdown(SimpleEpollServer* eps) override;
-
- // If the alarm was registered, unregister it.
- void UnregisterIfRegistered();
-
- // Reregisters the alarm at specified time.
- void ReregisterAlarm(int64_t timeout_time_in_us);
-
- bool registered() const { return registered_; }
-
- const SimpleEpollServer* eps() const { return eps_; }
-
- private:
- SimpleEpollServer::AlarmRegToken token_;
- SimpleEpollServer* eps_;
- bool registered_;
-};
-
-} // namespace epoll_server
-
-#endif // QUICHE_EPOLL_SERVER_SIMPLE_EPOLL_SERVER_H_
diff --git a/quiche/epoll_server/simple_epoll_server_test.cc b/quiche/epoll_server/simple_epoll_server_test.cc
deleted file mode 100644
index ba85038..0000000
--- a/quiche/epoll_server/simple_epoll_server_test.cc
+++ /dev/null
@@ -1,2494 +0,0 @@
-// Copyright 2013 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.
-
-// Epoll tests which determine that the right things happen in the right order.
-// Also lots of testing of individual functions.
-
-#include "quiche/epoll_server/simple_epoll_server.h"
-
-#include <errno.h>
-#include <fcntl.h>
-#include <netinet/in.h>
-#include <sys/epoll.h>
-#include <sys/socket.h>
-#include <unistd.h>
-
-#include <algorithm>
-#include <cstddef>
-#include <cstdlib>
-#include <cstring>
-#include <memory>
-#include <string>
-#include <unordered_map>
-#include <utility>
-#include <vector>
-
-#include "absl/time/clock.h"
-#include "quiche/epoll_server/fake_simple_epoll_server.h"
-#include "quiche/epoll_server/platform/api/epoll_address_test_utils.h"
-#include "quiche/epoll_server/platform/api/epoll_expect_bug.h"
-#include "quiche/epoll_server/platform/api/epoll_test.h"
-#include "quiche/epoll_server/platform/api/epoll_thread.h"
-
-namespace epoll_server {
-
-namespace test {
-
-namespace {
-
-const int kPageSize = 4096;
-const int kMaxBufLen = 10000;
-
-// These are used to record what is happening.
-enum {
- CREATION,
- REGISTRATION,
- MODIFICATION,
- EVENT,
- UNREGISTRATION,
- SHUTDOWN,
- DESTRUCTION
-};
-
-////////////////////////////////////////////////////////////////////////////////
-////////////////////////////////////////////////////////////////////////////////
-
-int64_t WallTimeNowInUsec() { return absl::GetCurrentTimeNanos() / 1000; }
-
-////////////////////////////////////////////////////////////////////////////////
-////////////////////////////////////////////////////////////////////////////////
-
-struct RecordEntry {
- RecordEntry() : time(0), instance(nullptr), event_type(0), fd(0), data(0) {}
-
- RecordEntry(int64_t time, void* instance, int event_type, int fd, int data)
- : time(time),
- instance(instance),
- event_type(event_type),
- fd(fd),
- data(data) {}
-
- int64_t time;
- void* instance;
- int event_type;
- int fd;
- int data;
-
- bool IsEqual(const RecordEntry* entry) const {
- bool retval = true;
-
- if (instance != entry->instance) {
- retval = false;
- EPOLL_LOG(INFO) << " instance (" << instance << ") != entry->instance("
- << entry->instance << ")";
- }
- if (event_type != entry->event_type) {
- retval = false;
- EPOLL_LOG(INFO) << " event_type (" << event_type
- << ") != entry->event_type(" << entry->event_type << ")";
- }
- if (fd != entry->fd) {
- retval = false;
- EPOLL_LOG(INFO) << " fd (" << fd << ") != entry->fd (" << entry->fd
- << ")";
- }
- if (data != entry->data) {
- retval = false;
- EPOLL_LOG(INFO) << " data (" << data << ") != entry->data(" << entry->data
- << ")";
- }
- return retval;
- }
-};
-
-////////////////////////////////////////////////////////////////////////////////
-////////////////////////////////////////////////////////////////////////////////
-
-class Recorder {
- public:
- void Record(void* instance, int event_type, int fd, int data) {
- records_.push_back(
- RecordEntry(WallTimeNowInUsec(), instance, event_type, fd, data));
- }
-
- const std::vector<RecordEntry>* records() const { return &records_; }
-
- bool IsEqual(const Recorder* recorder) const {
- const std::vector<RecordEntry>* records = recorder->records();
-
- if (records_.size() != records->size()) {
- EPOLL_LOG(INFO) << "records_.size() (" << records_.size()
- << ") != records->size() (" << records->size() << ")";
- return false;
- }
- for (size_t i = 0; i < std::min(records_.size(), records->size()); ++i) {
- if (!records_[i].IsEqual(&(*records)[i])) {
- EPOLL_LOG(INFO) << "entry in index: " << i << " differs from recorder.";
- return false;
- }
- }
- return true;
- }
-
- private:
- std::vector<RecordEntry> records_;
-};
-
-////////////////////////////////////////////////////////////////////////////////
-////////////////////////////////////////////////////////////////////////////////
-
-class RecordingCB : public EpollCallbackInterface {
- public:
- RecordingCB() : recorder_(new Recorder()) {
- recorder_->Record(this, CREATION, 0, 0);
- }
-
- ~RecordingCB() override {
- recorder_->Record(this, DESTRUCTION, 0, 0);
- delete recorder_;
- }
-
- void OnRegistration(SimpleEpollServer* /*eps*/, int fd,
- int event_mask) override {
- recorder_->Record(this, REGISTRATION, fd, event_mask);
- }
-
- void OnModification(int fd, int event_mask) override {
- recorder_->Record(this, MODIFICATION, fd, event_mask);
- }
-
- void OnEvent(int fd, EpollEvent* event) override {
- recorder_->Record(this, EVENT, fd, event->in_events);
- if (event->in_events & EPOLLIN) {
- const int kLength = 1024;
- char buf[kLength];
- int data_read;
- do {
- data_read = read(fd, &buf, kLength);
- } while (data_read > 0);
- }
- }
-
- void OnUnregistration(int fd, bool replaced) override {
- recorder_->Record(this, UNREGISTRATION, fd, replaced);
- }
-
- void OnShutdown(SimpleEpollServer* eps, int fd) override {
- if (fd >= 0) {
- eps->UnregisterFD(fd);
- }
- recorder_->Record(this, SHUTDOWN, fd, 0);
- }
-
- std::string Name() const override { return "RecordingCB"; }
-
- const Recorder* recorder() const { return recorder_; }
-
- protected:
- Recorder* recorder_;
-};
-
-////////////////////////////////////////////////////////////////////////////////
-////////////////////////////////////////////////////////////////////////////////
-
-// A simple test server that adds some test functions to SimpleEpollServer as
-// well as allowing access to protected functions.
-class EpollTestServer : public SimpleEpollServer {
- public:
- EpollTestServer() : SimpleEpollServer() {}
-
- ~EpollTestServer() override {}
-
- void CheckMapping(int fd, CB* cb) {
- CBAndEventMask tmp;
- tmp.fd = fd;
- FDToCBMap::iterator fd_i = cb_map_.find(tmp);
- CHECK(fd_i != cb_map_.end()); // Chokes CHECK_NE.
- CHECK(fd_i->cb == cb);
- }
-
- void CheckNotMapped(int fd) {
- CBAndEventMask tmp;
- tmp.fd = fd;
- FDToCBMap::iterator fd_i = cb_map_.find(tmp);
- CHECK(fd_i == cb_map_.end()); // Chokes CHECK_EQ.
- }
-
- void CheckEventMask(int fd, int event_mask) {
- CBAndEventMask tmp;
- tmp.fd = fd;
- FDToCBMap::iterator fd_i = cb_map_.find(tmp);
- CHECK(cb_map_.end() != fd_i); // Chokes CHECK_NE.
- CHECK_EQ(fd_i->event_mask, event_mask);
- }
-
- void CheckNotRegistered(int fd) {
- struct epoll_event ee;
- memset(&ee, 0, sizeof(ee));
- // If the fd is registered, the epoll_ctl call would succeed (return 0) and
- // the CHECK would fail.
- CHECK(epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, fd, &ee));
- }
-
- size_t GetNumPendingAlarmsForTest() const { return alarm_map_.size(); }
-
- bool ContainsAlarm(AlarmCB* ac) {
- return all_alarms_.find(ac) != all_alarms_.end();
- }
-
- using SimpleEpollServer::WaitForEventsAndCallHandleEvents;
-};
-
-class EpollFunctionTest : public EpollTest {
- public:
- EpollFunctionTest()
- : fd_(-1), fd2_(-1), recorder_(nullptr), cb_(nullptr), ep_(nullptr) {}
-
- ~EpollFunctionTest() override {
- delete ep_;
- delete cb_;
- }
-
- void SetUp() override {
- ep_ = new EpollTestServer();
- cb_ = new RecordingCB();
- // recorder_ is safe to use directly as we know it has the same scope as
- // cb_
- recorder_ = cb_->recorder();
-
- int pipe_fds[2];
- if (pipe(pipe_fds) < 0) {
- EPOLL_PLOG(FATAL) << "pipe() failed";
- }
- fd_ = pipe_fds[0];
- fd2_ = pipe_fds[1];
- }
-
- void TearDown() override {
- close(fd_);
- close(fd2_);
- }
-
- void DeleteSimpleEpollServer() {
- delete ep_;
- ep_ = nullptr;
- }
-
- int fd() { return fd_; }
- int fd2() { return fd2_; }
- EpollTestServer* ep() { return ep_; }
- EpollCallbackInterface* cb() { return cb_; }
- const Recorder* recorder() { return recorder_; }
-
- private:
- int fd_;
- int fd2_;
- const Recorder* recorder_;
- RecordingCB* cb_;
- EpollTestServer* ep_;
-};
-
-TEST_F(EpollFunctionTest, TestUnconnectedSocket) {
- int fd = socket(AddressFamilyUnderTest(), SOCK_STREAM, IPPROTO_TCP);
- ep()->RegisterFD(fd, cb(), EPOLLIN | EPOLLOUT);
- ep()->WaitForEventsAndExecuteCallbacks();
-
- Recorder tmp;
- tmp.Record(cb(), CREATION, 0, 0);
- tmp.Record(cb(), REGISTRATION, fd, EPOLLIN | EPOLLOUT);
- tmp.Record(cb(), EVENT, fd, EPOLLOUT | EPOLLHUP);
- EXPECT_TRUE(recorder()->IsEqual(&tmp));
-}
-
-TEST_F(EpollFunctionTest, TestRegisterFD) {
- // Check that the basic register works.
- ep()->RegisterFD(fd(), cb(), EPOLLIN);
-
- // Make sure that the fd-CB mapping is there.
- ep()->CheckMapping(fd(), cb());
- ep()->CheckEventMask(fd(), EPOLLIN);
-
- // Now make sure that if we register again, we stomp the old callback.
- // Also make sure we handle O_NONBLOCK correctly
- RecordingCB cb2;
- ep()->RegisterFD(fd(), &cb2, EPOLLOUT | O_NONBLOCK);
- ep()->CheckMapping(fd(), &cb2);
- ep()->CheckEventMask(fd(), EPOLLOUT | O_NONBLOCK);
-
- // Clean up.
- ep()->UnregisterFD(fd());
-}
-
-TEST_F(EpollFunctionTest, TestRegisterFDForWrite) {
- ep()->RegisterFDForWrite(fd(), cb());
- ep()->CheckMapping(fd(), cb());
- ep()->CheckEventMask(fd(), EPOLLOUT);
-
- // Clean up.
- ep()->UnregisterFD(fd());
-}
-
-TEST_F(EpollFunctionTest, TestRegisterFDForReadWrite) {
- ep()->RegisterFDForReadWrite(fd(), cb());
- ep()->CheckMapping(fd(), cb());
- ep()->CheckEventMask(fd(), EPOLLIN | EPOLLOUT);
-
- // Clean up.
- ep()->UnregisterFD(fd());
-}
-
-TEST_F(EpollFunctionTest, TestRegisterFDForRead) {
- ep()->RegisterFDForRead(fd(), cb());
- ep()->CheckMapping(fd(), cb());
- ep()->CheckEventMask(fd(), EPOLLIN);
-
- ep()->UnregisterFD(fd());
-}
-
-TEST_F(EpollFunctionTest, TestUnregisterFD) {
- ep()->RegisterFDForRead(fd(), cb());
- ep()->CheckMapping(fd(), cb());
- ep()->CheckEventMask(fd(), EPOLLIN);
-
- // Unregister and make sure that it's gone.
- ep()->UnregisterFD(fd());
- ep()->CheckNotMapped(fd());
- ep()->CheckNotRegistered(fd());
-
- // And make sure that unregistering something a second time doesn't cause
- // crashes.
- ep()->UnregisterFD(fd());
- ep()->CheckNotMapped(fd());
- ep()->CheckNotRegistered(fd());
-}
-
-TEST_F(EpollFunctionTest, TestModifyCallback) {
- // Check that nothing terrible happens if we modify an unregistered fd.
- ep()->ModifyCallback(fd(), EPOLLOUT);
- ep()->CheckNotMapped(fd());
- ep()->CheckNotRegistered(fd());
-
- // Check that the basic register works.
- ep()->RegisterFD(fd(), cb(), EPOLLIN);
- ep()->CheckMapping(fd(), cb());
- ep()->CheckEventMask(fd(), EPOLLIN);
-
- // Check that adding a signal swaps it out for the first.
- ep()->ModifyCallback(fd(), EPOLLOUT);
- ep()->CheckMapping(fd(), cb());
- ep()->CheckEventMask(fd(), EPOLLOUT);
-
- // Check that modifying from X to X works correctly.
- ep()->ModifyCallback(fd(), EPOLLOUT);
- ep()->CheckMapping(fd(), cb());
- ep()->CheckEventMask(fd(), EPOLLOUT);
-
- // Check that modifying from something to nothing works.
- ep()->ModifyCallback(fd(), 0);
- ep()->CheckMapping(fd(), cb());
- ep()->CheckEventMask(fd(), 0);
-
- ep()->UnregisterFD(fd());
-}
-
-TEST_F(EpollFunctionTest, TestStopRead) {
- ep()->RegisterFDForReadWrite(fd(), cb());
- ep()->CheckMapping(fd(), cb());
- ep()->CheckEventMask(fd(), EPOLLIN | EPOLLOUT);
-
- // Unregister and make sure you only lose the read event.
- ep()->StopRead(fd());
- ep()->CheckMapping(fd(), cb());
- ep()->CheckEventMask(fd(), EPOLLOUT);
-
- ep()->UnregisterFD(fd());
-}
-
-TEST_F(EpollFunctionTest, TestStartRead) {
- ep()->RegisterFDForWrite(fd(), cb());
- ep()->CheckMapping(fd(), cb());
- ep()->CheckEventMask(fd(), EPOLLOUT);
-
- // Make sure that StartRead adds EPOLLIN and doesn't remove other signals.
- ep()->StartRead(fd());
- ep()->CheckMapping(fd(), cb());
- ep()->CheckEventMask(fd(), EPOLLIN | EPOLLOUT);
-
- // Clean up.
- ep()->UnregisterFD(fd());
-}
-
-TEST_F(EpollFunctionTest, TestStopWrite) {
- ep()->RegisterFDForReadWrite(fd(), cb());
- ep()->CheckMapping(fd(), cb());
- ep()->CheckEventMask(fd(), EPOLLIN | EPOLLOUT);
-
- // Unregister write and make sure you only lose the write event.
- ep()->StopWrite(fd());
- ep()->CheckMapping(fd(), cb());
- ep()->CheckEventMask(fd(), EPOLLIN);
-
- ep()->UnregisterFD(fd());
-}
-
-TEST_F(EpollFunctionTest, TestStartWrite) {
- ep()->RegisterFDForRead(fd(), cb());
- ep()->CheckMapping(fd(), cb());
- ep()->CheckEventMask(fd(), EPOLLIN);
-
- // Make sure that StartWrite adds EPOLLOUT and doesn't remove other
- // signals.
- ep()->StartWrite(fd());
- ep()->CheckMapping(fd(), cb());
- ep()->CheckEventMask(fd(), EPOLLIN | EPOLLOUT);
-
- // Clean up.
- ep()->UnregisterFD(fd());
-}
-
-TEST_F(EpollFunctionTest, TestSet_timeout_in_us) {
- // Check that set works with various values. There's a separate test below
- // to make sure the values are used properly.
- ep()->set_timeout_in_us(10);
- EXPECT_EQ(10, ep()->timeout_in_us_for_test());
-
- ep()->set_timeout_in_us(-1);
- EXPECT_EQ(-1, ep()->timeout_in_us_for_test());
-}
-
-TEST_F(EpollFunctionTest, TestHandleEvent) {
- const std::vector<RecordEntry>* records = recorder()->records();
-
- // Test that nothing bad happens if the FD is not in the map.
- ep()->HandleEvent(fd(), EPOLLOUT);
- ep()->CallReadyListCallbacks();
-
- ep()->RegisterFD(fd(), cb(), 0);
- ep()->CheckMapping(fd(), cb());
- ep()->CheckEventMask(fd(), 0);
-
- // At this point we should have creation and registration recorded.
- EXPECT_EQ(2u, records->size());
-
- // Call handle event and make sure something was recorded.
- ep()->HandleEvent(fd(), EPOLLOUT);
- ep()->CallReadyListCallbacks();
- EXPECT_EQ(3u, records->size());
-
- // Call handle event and make sure something was recorded.
- ep()->HandleEvent(fd(), EPOLLIN | O_NONBLOCK);
- ep()->CallReadyListCallbacks();
- EXPECT_EQ(4u, records->size());
-
- Recorder tmp;
- tmp.Record(cb(), CREATION, 0, 0);
- tmp.Record(cb(), REGISTRATION, fd(), 0);
- tmp.Record(cb(), EVENT, fd(), EPOLLOUT);
- tmp.Record(cb(), EVENT, fd(), EPOLLIN | O_NONBLOCK);
-
- EXPECT_TRUE(recorder()->IsEqual(&tmp));
- ep()->UnregisterFD(fd());
-}
-
-TEST_F(EpollFunctionTest, TestNumFDsRegistered) {
- EXPECT_EQ(0, ep()->NumFDsRegistered());
-
- ep()->RegisterFD(fd(), cb(), 0);
- EXPECT_EQ(1, ep()->NumFDsRegistered());
-
- ep()->RegisterFD(fd2(), cb(), 0);
- EXPECT_EQ(2, ep()->NumFDsRegistered());
-
- ep()->RegisterFD(fd2(), cb(), 0);
- EXPECT_EQ(2, ep()->NumFDsRegistered());
-
- ep()->UnregisterFD(fd2());
- EXPECT_EQ(1, ep()->NumFDsRegistered());
-
- ep()->UnregisterFD(fd());
- EXPECT_EQ(0, ep()->NumFDsRegistered());
-}
-
-// Check all of the individual signals and 1-2 combinations.
-TEST_F(EpollFunctionTest, TestEventMaskToString) {
- std::string test;
-
- test = SimpleEpollServer::EventMaskToString(EPOLLIN);
- EXPECT_EQ(test, "EPOLLIN ");
-
- test = SimpleEpollServer::EventMaskToString(EPOLLOUT);
- EXPECT_EQ(test, "EPOLLOUT ");
-
- test = SimpleEpollServer::EventMaskToString(EPOLLPRI);
- EXPECT_EQ(test, "EPOLLPRI ");
-
- test = SimpleEpollServer::EventMaskToString(EPOLLERR);
- EXPECT_EQ(test, "EPOLLERR ");
-
- test = SimpleEpollServer::EventMaskToString(EPOLLHUP);
- EXPECT_EQ(test, "EPOLLHUP ");
-
- test = SimpleEpollServer::EventMaskToString(EPOLLHUP | EPOLLIN);
- EXPECT_EQ(test, "EPOLLIN EPOLLHUP ");
-
- test = SimpleEpollServer::EventMaskToString(EPOLLIN | EPOLLOUT);
- EXPECT_EQ(test, "EPOLLIN EPOLLOUT ");
-}
-
-class TestAlarm : public EpollAlarmCallbackInterface {
- public:
- TestAlarm()
- : time_before_next_alarm_(-1),
- was_called_(false),
- num_called_(0),
- absolute_time_(false),
- onshutdown_called_(false),
- has_token_(false),
- eps_(nullptr) {}
- ~TestAlarm() override {}
- int64_t OnAlarm() override {
- has_token_ = false;
- was_called_ = true;
- ++num_called_;
- if (time_before_next_alarm_ < 0) {
- return 0;
- }
- if (absolute_time_) {
- return time_before_next_alarm_;
- } else {
- return WallTimeNowInUsec() + time_before_next_alarm_;
- }
- }
-
- void OnShutdown(SimpleEpollServer* /*eps*/) override {
- onshutdown_called_ = true;
- has_token_ = false;
- }
- void OnRegistration(const SimpleEpollServer::AlarmRegToken& token,
- SimpleEpollServer* eps) override {
- has_token_ = true;
- last_token_ = token;
- eps_ = eps;
- }
- void OnUnregistration() override { has_token_ = false; }
-
- void UnregisterIfRegistered(SimpleEpollServer* eps) {
- if (has_token_) {
- eps->UnregisterAlarm(last_token_);
- }
- }
-
- void ReregisterAlarm(int64_t timeout_in_us) {
- CHECK(has_token_);
- eps_->ReregisterAlarm(last_token_, timeout_in_us);
- }
-
- void Reset() {
- time_before_next_alarm_ = -1;
- was_called_ = false;
- absolute_time_ = false;
- }
-
- bool was_called() const { return was_called_; }
- int num_called() const { return num_called_; }
-
- void set_time_before_next_alarm(int64_t time) {
- time_before_next_alarm_ = time;
- }
- void set_absolute_time(bool absolute) { absolute_time_ = absolute; }
- bool onshutdown_called() { return onshutdown_called_; }
-
- protected:
- int64_t time_before_next_alarm_;
- bool was_called_;
- int num_called_;
- // Is time_before_next_alarm relative to the current time or absolute?
- bool absolute_time_;
- bool onshutdown_called_;
- bool has_token_;
- SimpleEpollServer::AlarmRegToken last_token_;
- SimpleEpollServer* eps_;
-};
-
-class TestChildAlarm;
-
-// This node unregister all other alarms when it receives
-// OnShutdown() from any one child.
-class TestParentAlarm {
- public:
- void OnShutdown(TestChildAlarm* child, SimpleEpollServer* eps) {
- // Unregister
- for (ChildTokenMap::const_iterator it = child_tokens_.begin();
- it != child_tokens_.end(); ++it) {
- if (it->first != child) {
- eps->UnregisterAlarm(it->second);
- }
- }
- child_tokens_.clear();
- }
-
- void OnRegistration(TestChildAlarm* child,
- const SimpleEpollServer::AlarmRegToken& token) {
- child_tokens_[child] = token;
- }
-
- protected:
- typedef std::unordered_map<TestChildAlarm*, SimpleEpollServer::AlarmRegToken>
- ChildTokenMap;
-
- ChildTokenMap child_tokens_;
-};
-
-class TestChildAlarm : public TestAlarm {
- public:
- void set_parent(TestParentAlarm* tp) { parent_ = tp; }
- void OnShutdown(SimpleEpollServer* eps) override {
- onshutdown_called_ = true;
- // Inform parent of shutdown
- parent_->OnShutdown(this, eps);
- }
- void OnRegistration(const SimpleEpollServer::AlarmRegToken& token,
- SimpleEpollServer* /*eps*/) override {
- parent_->OnRegistration(this, token);
- }
-
- protected:
- TestParentAlarm* parent_;
-};
-
-class TestAlarmThatRegistersAnotherAlarm : public TestAlarm {
- public:
- TestAlarmThatRegistersAnotherAlarm()
- : alarm_(nullptr),
- reg_time_delta_usec_(0),
- eps_to_register_(nullptr),
- has_reg_alarm_(false) {}
- void SetRegisterAlarm(TestAlarm* alarm, int64_t time_delta_usec,
- SimpleEpollServer* eps) {
- alarm_ = alarm;
- reg_time_delta_usec_ = time_delta_usec;
- has_reg_alarm_ = true;
- eps_to_register_ = eps;
- }
- int64_t OnAlarm() override {
- if (has_reg_alarm_) {
- eps_to_register_->RegisterAlarm(
- eps_to_register_->ApproximateNowInUsec() + reg_time_delta_usec_,
- alarm_);
- has_reg_alarm_ = false;
- }
- return TestAlarm::OnAlarm();
- }
-
- protected:
- TestAlarm* alarm_;
- int64_t reg_time_delta_usec_;
- SimpleEpollServer* eps_to_register_;
- bool has_reg_alarm_;
-};
-
-class TestAlarmThatRegistersAndReregistersAnotherAlarm : public TestAlarm {
- public:
- TestAlarmThatRegistersAndReregistersAnotherAlarm()
- : alarm_(nullptr),
- reg_time_delta_usec_(0),
- reregister_time_delta_usec_(0),
- eps_to_register_(nullptr),
- has_reg_alarm_(false) {}
- void SetRegisterAndReregisterAlarm(TestAlarm* alarm, int64_t time_delta_usec,
- int64_t reregister_delta_usec,
- SimpleEpollServer* eps) {
- alarm_ = alarm;
- reg_time_delta_usec_ = time_delta_usec;
- reregister_time_delta_usec_ = reregister_delta_usec;
- has_reg_alarm_ = true;
- eps_to_register_ = eps;
- }
- int64_t OnAlarm() override {
- if (has_reg_alarm_) {
- eps_to_register_->RegisterAlarm(
- eps_to_register_->ApproximateNowInUsec() + reg_time_delta_usec_,
- alarm_);
- alarm_->ReregisterAlarm(eps_to_register_->ApproximateNowInUsec() +
- reregister_time_delta_usec_);
- has_reg_alarm_ = false;
- }
- return TestAlarm::OnAlarm();
- }
-
- protected:
- TestAlarm* alarm_;
- int64_t reg_time_delta_usec_;
- int64_t reregister_time_delta_usec_;
- SimpleEpollServer* eps_to_register_;
- bool has_reg_alarm_;
-};
-
-class TestAlarmThatUnregistersAnotherAlarm : public TestAlarm {
- public:
- TestAlarmThatUnregistersAnotherAlarm()
- : alarm_(nullptr), eps_to_register_(nullptr), has_unreg_alarm_(false) {}
- void SetUnregisterAlarm(TestAlarm* alarm, SimpleEpollServer* eps) {
- alarm_ = alarm;
- has_unreg_alarm_ = true;
- eps_to_register_ = eps;
- }
- int64_t OnAlarm() override {
- if (has_unreg_alarm_) {
- has_unreg_alarm_ = false;
- alarm_->UnregisterIfRegistered(eps_to_register_);
- }
- return TestAlarm::OnAlarm();
- }
-
- protected:
- TestAlarm* alarm_;
- SimpleEpollServer* eps_to_register_;
- bool has_unreg_alarm_;
-};
-
-class TestAlarmUnregister : public TestAlarm {
- public:
- TestAlarmUnregister()
- : onunregistration_called_(false), iterator_token_(nullptr) {}
- ~TestAlarmUnregister() override { delete iterator_token_; }
-
- void OnShutdown(SimpleEpollServer* /*eps*/) override {
- onshutdown_called_ = true;
- }
-
- int64_t OnAlarm() override {
- delete iterator_token_;
- iterator_token_ = nullptr;
-
- return TestAlarm::OnAlarm();
- }
-
- void OnRegistration(const SimpleEpollServer::AlarmRegToken& token,
- SimpleEpollServer* /*eps*/) override {
- // Multiple iterator tokens are not maintained by this code,
- // so we should have reset the iterator_token in OnAlarm or
- // OnUnregistration.
- CHECK(iterator_token_ == nullptr);
- iterator_token_ = new SimpleEpollServer::AlarmRegToken(token);
- }
- void OnUnregistration() override {
- delete iterator_token_;
- iterator_token_ = nullptr;
- // Make sure that this alarm was not already unregistered.
- CHECK(onunregistration_called_ == false);
- onunregistration_called_ = true;
- }
-
- bool onunregistration_called() { return onunregistration_called_; }
- // Returns true if the token has been filled in with the saved iterator
- // and false if it has not.
- bool get_token(SimpleEpollServer::AlarmRegToken* token) {
- if (iterator_token_ != nullptr) {
- *token = *iterator_token_;
- return true;
- } else {
- return false;
- }
- }
-
- protected:
- bool onunregistration_called_;
- SimpleEpollServer::AlarmRegToken* iterator_token_;
-};
-
-void WaitForAlarm(SimpleEpollServer* eps, const TestAlarm& alarm) {
- for (int i = 0; i < 5; ++i) {
- // Ideally we would only have to call this once but it could wake up a bit
- // early and so not call the alarm. If it wakes up early several times
- // there is something wrong.
- eps->WaitForEventsAndExecuteCallbacks();
- if (alarm.was_called()) {
- break;
- }
- }
-}
-
-// Check a couple of alarm times to make sure they're falling within a
-// reasonable range.
-TEST(SimpleEpollServerTest, TestAlarms) {
- EpollTestServer ep;
- TestAlarm alarm;
-
- int alarm_time = 10;
-
- // Register an alarm and make sure we wait long enough to hit it.
- ep.set_timeout_in_us(alarm_time * 1000 * 2);
- ep.RegisterAlarm(WallTimeNowInUsec() + alarm_time, &alarm);
- EXPECT_EQ(1u, ep.GetNumPendingAlarmsForTest());
- WaitForAlarm(&ep, alarm);
- EXPECT_TRUE(alarm.was_called());
- EXPECT_EQ(0u, ep.GetNumPendingAlarmsForTest());
- alarm.Reset();
-
- // Test a different time just to be careful.
- alarm_time = 20;
- ep.set_timeout_in_us(alarm_time * 1000 * 2);
- ep.RegisterAlarm(WallTimeNowInUsec() + alarm_time, &alarm);
- EXPECT_EQ(1u, ep.GetNumPendingAlarmsForTest());
- WaitForAlarm(&ep, alarm);
- EXPECT_TRUE(alarm.was_called());
- alarm.Reset();
-
- // The alarm was a one-time thing. Make sure that we don't hit it again.
- EXPECT_EQ(0u, ep.GetNumPendingAlarmsForTest());
- ep.WaitForEventsAndExecuteCallbacks();
- EXPECT_FALSE(alarm.was_called());
- alarm.Reset();
-}
-
-// Same as above, but using RegisterAlarmApproximateDelta.
-TEST(SimpleEpollServerTest, TestRegisterAlarmApproximateDelta) {
- EpollTestServer ep;
- TestAlarm alarm;
-
- int alarm_time = 10;
-
- // Register an alarm and make sure we wait long enough to hit it.
- ep.set_timeout_in_us(alarm_time * 1000 * 2);
- ep.RegisterAlarmApproximateDelta(alarm_time * 1000, &alarm);
- EXPECT_EQ(1u, ep.GetNumPendingAlarmsForTest());
- WaitForAlarm(&ep, alarm);
- EXPECT_TRUE(alarm.was_called());
- EXPECT_EQ(0u, ep.GetNumPendingAlarmsForTest());
- alarm.Reset();
- int64_t first_now = ep.ApproximateNowInUsec();
- EXPECT_LT(0u, first_now);
-
- // Test a different time just to be careful.
- alarm_time = 20;
- ep.set_timeout_in_us(alarm_time * 1000 * 2);
- ep.RegisterAlarmApproximateDelta(alarm_time * 1000, &alarm);
- EXPECT_EQ(1u, ep.GetNumPendingAlarmsForTest());
- WaitForAlarm(&ep, alarm);
- EXPECT_TRUE(alarm.was_called());
- alarm.Reset();
- int64_t second_now = ep.ApproximateNowInUsec();
-
- EXPECT_LT(first_now, second_now);
-
- // The alarm was a one-time thing. Make sure that we don't hit it again.
- EXPECT_EQ(0u, ep.GetNumPendingAlarmsForTest());
- ep.WaitForEventsAndExecuteCallbacks();
- EXPECT_FALSE(alarm.was_called());
- alarm.Reset();
-}
-
-TEST(SimpleEpollServerTest, TestAlarmsWithInfiniteWait) {
- EpollTestServer ep;
- TestAlarm alarm;
-
- int alarm_time = 10;
-
- // Register an alarm and make sure we wait long enough to hit it.
- ep.set_timeout_in_us(-1);
- ep.RegisterAlarm(WallTimeNowInUsec() + alarm_time, &alarm);
- EXPECT_EQ(1u, ep.GetNumPendingAlarmsForTest());
- WaitForAlarm(&ep, alarm);
- EXPECT_TRUE(alarm.was_called());
- EXPECT_EQ(0u, ep.GetNumPendingAlarmsForTest());
- alarm.Reset();
-}
-
-// In this test we have an alarm that when fires gets re-registered
-// at almost the same time at which it fires. Here, we want to make
-// sure that when the alarm gets re-registered we do not call OnAlarm()
-// on the same Alarm object again, until we have called
-// WaitForEventsAndExecuteCallbacks(). A poor implementation of epoll
-// server alarm handling can potentially cause OnAlarm() to be called
-// multiple times. We make sure that the epoll server is not going in
-// an infinite loop by checking that OnAlarm() is called exactly once
-// on the alarm object that got registered again.
-TEST(SimpleEpollServerTest, TestAlarmsThatGetReRegisteredAreNotCalledTwice) {
- // This alarm would get registered again
- TestAlarm alarm;
- TestAlarm alarm2;
- EpollTestServer ep;
- ep.set_timeout_in_us(-1);
-
- int64_t alarm_time = 10;
- int64_t abs_time = WallTimeNowInUsec() + alarm_time * 1000;
-
- // This will make the alarm re-register when OnAlarm is called.
- alarm.set_absolute_time(true);
- alarm.set_time_before_next_alarm(abs_time + 2);
-
- // Register two alarms and make sure we wait long enough to hit it.
- ep.RegisterAlarm(abs_time, &alarm);
- ep.RegisterAlarm(abs_time, &alarm2);
- EXPECT_EQ(2u, ep.GetNumPendingAlarmsForTest());
-
- WaitForAlarm(&ep, alarm);
-
- EXPECT_TRUE(alarm.was_called());
- // Make sure that alarm is called only once.
- EXPECT_EQ(1, alarm.num_called());
- EXPECT_EQ(1u, ep.GetNumPendingAlarmsForTest());
- alarm.Reset();
-}
-
-// Here we make sure that when one alarm unregisters another alarm
-// (that is supposed to be registered again because its OnAlarm
-// returned > 0), the alarm thats supposed to be unregistered does
-// actually gets unregistered.
-TEST(SimpleEpollServerTest, TestAlarmsOneOnAlarmUnRegistersAnotherAlarm) {
- TestAlarm alarm;
- TestAlarmThatUnregistersAnotherAlarm alarm2;
- EpollTestServer ep;
- ep.set_timeout_in_us(-1);
-
- int64_t alarm_time = 1;
- int64_t abs_time = WallTimeNowInUsec() + alarm_time * 1000;
-
- // This will make the alarm re-register when OnAlarm is called.
- alarm.set_absolute_time(true);
- alarm.set_time_before_next_alarm(abs_time + 2);
-
- // Register two alarms and make sure we wait long enough to hit it.
- ep.RegisterAlarm(abs_time, &alarm);
- // This would cause us to unregister alarm when OnAlarm is called
- // on alarm2.
- alarm2.SetUnregisterAlarm(&alarm, &ep);
- ep.RegisterAlarm(abs_time + 1, &alarm2);
- EXPECT_EQ(2u, ep.GetNumPendingAlarmsForTest());
-
- WaitForAlarm(&ep, alarm);
-
- EXPECT_TRUE(alarm.was_called());
- // Make sure that alarm is called only once.
- EXPECT_EQ(1, alarm.num_called());
- EXPECT_EQ(0u, ep.GetNumPendingAlarmsForTest());
- alarm.Reset();
-}
-
-// Check a couple of alarm times to make sure they're falling within a
-// reasonable range.
-TEST(SimpleEpollServerTest, TestRepeatAlarms) {
- EpollTestServer ep;
- TestAlarm alarm;
-
- int alarm_time = 20;
-
- // Register an alarm and make sure we wait long enough to hit it.
- ep.set_timeout_in_us(alarm_time * 1000 * 2);
- alarm.set_time_before_next_alarm(1000 * alarm_time);
- ep.RegisterAlarm(WallTimeNowInUsec() + alarm_time, &alarm);
- EXPECT_EQ(1u, ep.GetNumPendingAlarmsForTest());
-
- WaitForAlarm(&ep, alarm);
- // When we wake up it should be because the Alarm has been called, and has
- // registered itself to be called again.
-
- // Make sure the first alarm was called properly.
- EXPECT_TRUE(alarm.was_called());
-
- // Resetting means that the alarm is no longer a recurring alarm. It will be
- // called once more and then stop.
- alarm.Reset();
-
- // Make sure the alarm is called one final time.
- EXPECT_EQ(1u, ep.GetNumPendingAlarmsForTest());
- ep.set_timeout_in_us(alarm_time * 1000 * 2);
- WaitForAlarm(&ep, alarm);
-
- EXPECT_TRUE(alarm.was_called());
- alarm.Reset();
-
- // The alarm was a one-time thing. Make sure that we don't hit it again.
- EXPECT_EQ(0u, ep.GetNumPendingAlarmsForTest());
- ep.WaitForEventsAndExecuteCallbacks();
- EXPECT_FALSE(alarm.was_called());
-}
-
-// Verify that an alarm that repeats itself in the past works properly.
-TEST(SimpleEpollServerTest, TestRepeatAlarmInPast) {
- EpollTestServer ep;
- TestAlarm alarm;
-
- int64_t alarm_time = 20;
- int64_t abs_time = WallTimeNowInUsec() + alarm_time * 1000;
-
- // Make the alarm re-register in the past when OnAlarm is called.
- alarm.set_absolute_time(true);
- alarm.set_time_before_next_alarm(abs_time - 1000);
-
- // Register the alarm and make sure we wait long enough to hit it.
- ep.set_timeout_in_us(alarm_time * 1000 * 2);
- ep.RegisterAlarm(abs_time, &alarm);
- EXPECT_EQ(1u, ep.GetNumPendingAlarmsForTest());
-
- WaitForAlarm(&ep, alarm);
- // When we wake up it should be because the Alarm has been called, and has
- // registered itself to be called again.
-
- // Make sure the first alarm was called properly.
- EXPECT_TRUE(alarm.was_called());
-
- // Resetting means that the alarm is no longer a recurring alarm. It will be
- // called once more and then stop.
- alarm.Reset();
-
- // Make sure the alarm is called one final time.
- EXPECT_EQ(1u, ep.GetNumPendingAlarmsForTest());
- ep.set_timeout_in_us(alarm_time * 1000 * 2);
- WaitForAlarm(&ep, alarm);
-
- EXPECT_TRUE(alarm.was_called());
- alarm.Reset();
-
- // The alarm was a one-time thing. Make sure that we don't hit it again.
- EXPECT_EQ(0u, ep.GetNumPendingAlarmsForTest());
- ep.WaitForEventsAndExecuteCallbacks();
- EXPECT_FALSE(alarm.was_called());
-}
-
-class EpollTestAlarms : public SimpleEpollServer {
- public:
- EpollTestAlarms() : SimpleEpollServer() {}
-
- inline int64_t NowInUsec() const override { return time_; }
-
- void CallAndReregisterAlarmEvents() override {
- recorded_now_in_us_ = NowInUsec();
- SimpleEpollServer::CallAndReregisterAlarmEvents();
- }
-
- void set_time(int64_t time) { time_ = time; }
-
- size_t GetNumPendingAlarmsForTest() const { return alarm_map_.size(); }
-
- private:
- int64_t time_;
-};
-
-// Test multiple interleaving alarms to make sure they work right.
-// Pattern is roughly:
-// time: 15 20 30 40
-// alarm: A B A' C
-TEST(SimpleEpollServerTest, TestMultipleAlarms) {
- EpollTestAlarms ep;
- TestAlarm alarmA;
- TestAlarm alarmB;
- TestAlarm alarmC;
-
- ep.set_timeout_in_us(50 * 1000 * 2);
- alarmA.set_time_before_next_alarm(1000 * 30);
- alarmA.set_absolute_time(true);
- ep.RegisterAlarm(15 * 1000, &alarmA);
- ep.RegisterAlarm(20 * 1000, &alarmB);
- ep.RegisterAlarm(40 * 1000, &alarmC);
-
- ep.set_time(15 * 1000);
- ep.CallAndReregisterAlarmEvents(); // A
- EXPECT_TRUE(alarmA.was_called());
- EXPECT_FALSE(alarmB.was_called());
- EXPECT_FALSE(alarmC.was_called());
- alarmA.Reset(); // Unregister A in the future.
-
- ep.set_time(20 * 1000);
- ep.CallAndReregisterAlarmEvents(); // B
- EXPECT_FALSE(alarmA.was_called());
- EXPECT_TRUE(alarmB.was_called());
- EXPECT_FALSE(alarmC.was_called());
- alarmB.Reset();
-
- ep.set_time(30 * 1000);
- ep.CallAndReregisterAlarmEvents(); // A
- EXPECT_TRUE(alarmA.was_called());
- EXPECT_FALSE(alarmB.was_called());
- EXPECT_FALSE(alarmC.was_called());
- alarmA.Reset();
-
- ep.set_time(40 * 1000);
- ep.CallAndReregisterAlarmEvents(); // C
- EXPECT_FALSE(alarmA.was_called());
- EXPECT_FALSE(alarmB.was_called());
- EXPECT_TRUE(alarmC.was_called());
- alarmC.Reset();
-
- ep.CallAndReregisterAlarmEvents(); // None.
- EXPECT_FALSE(alarmA.was_called());
- EXPECT_FALSE(alarmB.was_called());
- EXPECT_FALSE(alarmC.was_called());
-}
-
-TEST(SimpleEpollServerTest, TestAlarmOnShutdown) {
- TestAlarm alarm1;
- {
- EpollTestServer ep;
- const int64_t now = WallTimeNowInUsec();
- ep.RegisterAlarm(now + 5000, &alarm1);
- }
-
- EXPECT_TRUE(alarm1.onshutdown_called());
-}
-
-// Tests that if we have multiple alarms
-// OnShutdown then we handle them properly.
-TEST(SimpleEpollServerTest, TestMultipleAlarmOnShutdown) {
- TestAlarm alarm1;
- TestAlarm alarm2;
- TestAlarm alarm3;
- {
- EpollTestServer ep;
- const int64_t now = WallTimeNowInUsec();
- ep.RegisterAlarm(now + 5000, &alarm1);
- ep.RegisterAlarm(now + 9000, &alarm2);
- ep.RegisterAlarm(now + 9000, &alarm3);
- }
-
- EXPECT_TRUE(alarm1.onshutdown_called());
- EXPECT_TRUE(alarm2.onshutdown_called());
- EXPECT_TRUE(alarm3.onshutdown_called());
-}
-TEST(SimpleEpollServerTest, TestMultipleAlarmUnregistrationOnShutdown) {
- TestParentAlarm tp;
- TestChildAlarm alarm1;
- TestChildAlarm alarm2;
- alarm1.set_parent(&tp);
- alarm2.set_parent(&tp);
- {
- EpollTestServer ep;
- const int64_t now = WallTimeNowInUsec();
- ep.RegisterAlarm(now + 5000, &alarm1);
- ep.RegisterAlarm(now + 9000, &alarm2);
- }
-
- EXPECT_TRUE(alarm1.onshutdown_called());
- EXPECT_FALSE(alarm2.onshutdown_called());
-}
-
-// Check an alarm set in the past runs right away.
-TEST(SimpleEpollServerTest, TestPastAlarm) {
- EpollTestServer ep;
- TestAlarm alarm;
-
- // Register the alarm and make sure we wait long enough to hit it.
- ep.set_timeout_in_us(1000 * 2);
- ep.RegisterAlarm(WallTimeNowInUsec() - 1000, &alarm);
- EXPECT_EQ(1u, ep.GetNumPendingAlarmsForTest());
- ep.WaitForEventsAndExecuteCallbacks();
- EXPECT_TRUE(alarm.was_called());
- EXPECT_EQ(0u, ep.GetNumPendingAlarmsForTest());
- alarm.Reset();
-}
-
-// Test Unregistering of Alarms
-TEST(SimpleEpollServerTest, TestUnregisterAlarm) {
- EpollTestServer ep;
- SimpleEpollServer::AlarmRegToken temptok;
-
- TestAlarmUnregister alarm1;
- TestAlarmUnregister alarm2;
-
- ep.RegisterAlarm(WallTimeNowInUsec() + 5 * 1000, &alarm1);
- ep.RegisterAlarm(WallTimeNowInUsec() + 13 * 1000, &alarm2);
-
- // Unregister an alarm.
- if (alarm2.get_token(&temptok)) {
- ep.UnregisterAlarm(temptok);
- }
- EXPECT_EQ(1u, ep.GetNumPendingAlarmsForTest());
- EXPECT_TRUE(alarm2.onunregistration_called());
-
- if (alarm1.get_token(&temptok)) {
- ep.UnregisterAlarm(temptok);
- }
- EXPECT_EQ(0u, ep.GetNumPendingAlarmsForTest());
- EXPECT_TRUE(alarm1.onunregistration_called());
-}
-
-// Test Reregistering of Alarms
-TEST(SimpleEpollServerTest, TestReregisterAlarm) {
- EpollTestAlarms ep;
- SimpleEpollServer::AlarmRegToken token;
-
- TestAlarmUnregister alarm;
- ep.set_time(1000);
- ep.RegisterAlarm(5000, &alarm);
-
- EXPECT_EQ(1u, ep.GetNumPendingAlarmsForTest());
- ASSERT_TRUE(alarm.get_token(&token));
- ep.ReregisterAlarm(token, 6000);
- EXPECT_EQ(1u, ep.GetNumPendingAlarmsForTest());
-
- ep.set_time(5000);
- ep.set_timeout_in_us(0);
- ep.CallAndReregisterAlarmEvents();
- EXPECT_FALSE(alarm.was_called());
-
- ep.set_time(6000);
- ep.CallAndReregisterAlarmEvents();
- EXPECT_TRUE(alarm.was_called());
-}
-
-TEST(SimpleEpollServerTest, TestReregisterDeferredAlarm) {
- EpollTestAlarms ep;
- ep.set_timeout_in_us(0);
-
- TestAlarm alarm;
- TestAlarmThatRegistersAndReregistersAnotherAlarm register_alarm;
- // Register the alarm in the past so it is added as a deferred alarm.
- register_alarm.SetRegisterAndReregisterAlarm(&alarm, -500, 500, &ep);
- ep.set_time(1000);
- ep.RegisterAlarm(1000, ®ister_alarm);
- // Call reregister twice, first to run register_alarm and second to run any
- // scheduled deferred alarms.
- ep.CallAndReregisterAlarmEvents();
- ep.CallAndReregisterAlarmEvents();
-
- EXPECT_EQ(1u, ep.GetNumPendingAlarmsForTest());
- EXPECT_FALSE(alarm.was_called());
-
- ep.set_time(1500);
- ep.CallAndReregisterAlarmEvents();
- EXPECT_TRUE(alarm.was_called());
-}
-
-// Check if an alarm fired and got reregistered, you are able to
-// unregister the second registration.
-TEST(SimpleEpollServerTest, TestFiredReregisteredAlarm) {
- EpollTestAlarms ep;
- TestAlarmUnregister alarmA;
-
- SimpleEpollServer::AlarmRegToken first_token;
- SimpleEpollServer::AlarmRegToken second_token;
- bool found;
-
- ep.set_timeout_in_us(50 * 1000 * 2);
- alarmA.set_time_before_next_alarm(1000 * 30);
- alarmA.set_absolute_time(true);
-
- // Alarm A first fires at 15, then 30
- ep.RegisterAlarm(15 * 1000, &alarmA);
-
- found = alarmA.get_token(&first_token);
- EXPECT_TRUE(found);
-
- ep.set_time(15 * 1000);
- ep.CallAndReregisterAlarmEvents(); // A
- EXPECT_TRUE(alarmA.was_called());
-
- alarmA.Reset();
-
- found = alarmA.get_token(&second_token);
- EXPECT_TRUE(found);
- if (found) {
- ep.UnregisterAlarm(second_token);
- }
-
- ep.set_time(30 * 1000);
- ep.CallAndReregisterAlarmEvents(); // A
-
- alarmA.Reset();
-}
-
-// Here we make sure that one alarm can unregister another alarm
-// in OnShutdown().
-TEST(SimpleEpollServerTest, TestAlarmCanUnregistersAnotherAlarmOnShutdown) {
- TestAlarmThatUnregistersAnotherAlarm alarm1;
- TestAlarm alarm2;
- {
- EpollTestServer ep;
- // Register two alarms and make alarm1 is placed in queue in front of alarm2
- // so that when the queue is cleared, alarm1 is processed first.
- const int64_t now = WallTimeNowInUsec();
- ep.RegisterAlarm(now + 5000, &alarm1);
- ep.RegisterAlarm(now + 9000, &alarm2);
- alarm1.SetUnregisterAlarm(&alarm2, &ep);
- EXPECT_EQ(2u, ep.GetNumPendingAlarmsForTest());
- }
-}
-
-class TestAlarmRegisterAnotherAlarmShutdown : public TestAlarmUnregister {
- public:
- TestAlarmRegisterAnotherAlarmShutdown(EpollAlarmCallbackInterface* alarm2,
- int64_t when)
- : alarm2_(alarm2), when_(when) {}
- void OnShutdown(SimpleEpollServer* eps) override {
- TestAlarmUnregister::OnShutdown(eps);
- eps->RegisterAlarm(when_, alarm2_);
- }
-
- private:
- EpollAlarmCallbackInterface* alarm2_;
- int64_t when_;
-};
-
-// This tests that alarm registers another alarm when shutting down.
-// The two cases are: new alarm comes before and after the alarm being
-// notified by OnShutdown()
-TEST(SimpleEpollServerTest, AlarmRegistersAnotherAlarmOnShutdownBeforeSelf) {
- TestAlarm alarm2;
- int64_t alarm_time = WallTimeNowInUsec() + 5000;
- TestAlarmRegisterAnotherAlarmShutdown alarm1(&alarm2, alarm_time - 1000);
- {
- EpollTestAlarms ep;
- ep.RegisterAlarm(alarm_time, &alarm1);
- }
- EXPECT_TRUE(alarm1.onshutdown_called());
- EXPECT_FALSE(alarm2.onshutdown_called());
-}
-
-TEST(SimpleEpollServerTest, AlarmRegistersAnotherAlarmOnShutdownAfterSelf) {
- TestAlarm alarm2;
- int64_t alarm_time = WallTimeNowInUsec() + 5000;
- TestAlarmRegisterAnotherAlarmShutdown alarm1(&alarm2, alarm_time + 1000);
- {
- EpollTestAlarms ep;
- ep.RegisterAlarm(alarm_time, &alarm1);
- }
- EXPECT_TRUE(alarm1.onshutdown_called());
- EXPECT_TRUE(alarm2.onshutdown_called());
-}
-
-TEST(SimpleEpollServerTest, TestWrite) {
- SimpleEpollServer ep;
- ep.set_timeout_in_us(1);
- char data[kPageSize] = {0};
-
- int pipe_fds[2];
- if (pipe(pipe_fds) < 0) {
- EPOLL_PLOG(FATAL) << "pipe() failed";
- }
- int read_fd = pipe_fds[0];
- int write_fd = pipe_fds[1];
-
- RecordingCB recording_cb;
- const Recorder* recorder = recording_cb.recorder();
- const std::vector<RecordEntry>* records = recorder->records();
-
- // Register to listen to write events.
- ep.RegisterFD(write_fd, &recording_cb, EPOLLOUT | O_NONBLOCK);
- // At this point the recorder should have the creation and registration
- // events.
- EXPECT_EQ(2u, records->size());
-
- // Fill up the pipe.
- int written = 1;
- for (int i = 0; i < 17 && written > 0; ++i) {
- written = write(write_fd, &data, kPageSize);
- }
- EXPECT_LT(written, 0);
-
- // There should be no new events as the pipe is not available for writing.
- ep.WaitForEventsAndExecuteCallbacks();
- EXPECT_EQ(2u, records->size());
-
- // Now read data from the pipe to make it writable again. This time the
- // we should get an EPOLLOUT event.
- int size = read(read_fd, &data, kPageSize);
- EXPECT_EQ(kPageSize, size);
- ep.WaitForEventsAndExecuteCallbacks();
- EXPECT_EQ(3u, records->size());
-
- // Now unsubscribe from writable events (which adds a modification record)
- // and wait to verify that no event records are added.
- ep.StopWrite(write_fd);
- ep.WaitForEventsAndExecuteCallbacks();
- EXPECT_EQ(4u, records->size());
-
- // We had the right number of events all along. Make sure they were actually
- // the right events.
- Recorder tmp;
- tmp.Record(&recording_cb, CREATION, 0, 0);
- tmp.Record(&recording_cb, REGISTRATION, write_fd, EPOLLOUT | O_NONBLOCK);
- tmp.Record(&recording_cb, EVENT, write_fd, EPOLLOUT);
- tmp.Record(&recording_cb, MODIFICATION, write_fd, O_NONBLOCK);
-
- EXPECT_TRUE(recorder->IsEqual(&tmp));
- ep.UnregisterFD(write_fd);
-
- close(read_fd);
- close(write_fd);
-}
-
-TEST(SimpleEpollServerTest, TestReadWrite) {
- SimpleEpollServer ep;
- ep.set_timeout_in_us(1);
- char data[kPageSize] = {0};
-
- int pipe_fds[2];
- if (pipe(pipe_fds) < 0) {
- EPOLL_PLOG(FATAL) << "pipe() failed";
- }
- int read_fd = pipe_fds[0];
- int write_fd = pipe_fds[1];
-
- RecordingCB recording_cb;
- const Recorder* recorder = recording_cb.recorder();
- const std::vector<RecordEntry>* records = recorder->records();
-
- // Register to listen to read and write events.
- ep.RegisterFDForReadWrite(read_fd, &recording_cb);
- // At this point the recorder should have the creation and registration
- // events.
- EXPECT_EQ(2u, records->size());
-
- int written = write(write_fd, &data, kPageSize);
- EXPECT_EQ(kPageSize, written);
-
- ep.WaitForEventsAndExecuteCallbacks();
- ep.UnregisterFD(read_fd);
-
- close(read_fd);
- close(write_fd);
-}
-
-TEST(SimpleEpollServerTest, TestMultipleFDs) {
- SimpleEpollServer ep;
- ep.set_timeout_in_us(1);
- char data = 'x';
-
- int pipe_one[2];
- if (pipe(pipe_one) < 0) {
- EPOLL_PLOG(FATAL) << "pipe() failed";
- }
- int pipe_two[2];
- if (pipe(pipe_two) < 0) {
- EPOLL_PLOG(FATAL) << "pipe() failed";
- }
-
- RecordingCB recording_cb_one;
- const Recorder* recorder_one = recording_cb_one.recorder();
- const std::vector<RecordEntry>* records_one = recorder_one->records();
-
- RecordingCB recording_cb_two;
- const Recorder* recorder_two = recording_cb_two.recorder();
- const std::vector<RecordEntry>* records_two = recorder_two->records();
-
- // Register to listen to read events for both pipes
- ep.RegisterFDForRead(pipe_one[0], &recording_cb_one);
- ep.RegisterFDForRead(pipe_two[0], &recording_cb_two);
-
- EXPECT_EQ(2u, records_one->size());
- EXPECT_EQ(2u, records_two->size());
-
- EXPECT_EQ(1, write(pipe_one[1], &data, 1));
- ep.WaitForEventsAndExecuteCallbacks();
- EXPECT_EQ(3u, records_one->size());
- EXPECT_EQ(2u, records_two->size());
-
- EXPECT_EQ(1, write(pipe_two[1], &data, 1));
- ep.WaitForEventsAndExecuteCallbacks();
- EXPECT_EQ(3u, records_one->size());
- EXPECT_EQ(3u, records_two->size());
-
- EXPECT_EQ(1, write(pipe_one[1], &data, 1));
- EXPECT_EQ(1, write(pipe_two[1], &data, 1));
- ep.WaitForEventsAndExecuteCallbacks();
- EXPECT_EQ(4u, records_one->size());
- EXPECT_EQ(4u, records_two->size());
-
- ep.WaitForEventsAndExecuteCallbacks();
- ep.UnregisterFD(pipe_one[0]);
- ep.UnregisterFD(pipe_two[0]);
- close(pipe_one[0]);
- close(pipe_one[1]);
- close(pipe_two[0]);
- close(pipe_two[1]);
-}
-
-// Check that the SimpleEpollServer calls OnShutdown for any registered FDs.
-TEST(SimpleEpollServerTest, TestFDOnShutdown) {
- int pipe_fds[2];
- if (pipe(pipe_fds) < 0) {
- EPOLL_PLOG(FATAL) << "pipe() failed";
- }
- int read_fd = pipe_fds[0];
- int write_fd = pipe_fds[1];
-
- RecordingCB recording_cb1;
- RecordingCB recording_cb2;
- const Recorder* recorder1 = recording_cb1.recorder();
- const Recorder* recorder2 = recording_cb2.recorder();
-
- {
- SimpleEpollServer ep;
- ep.set_timeout_in_us(1);
-
- // Register to listen to write events.
- ep.RegisterFD(write_fd, &recording_cb1, EPOLLOUT | O_NONBLOCK);
- ep.RegisterFD(read_fd, &recording_cb2, EPOLLIN | O_NONBLOCK);
- }
-
- // Make sure OnShutdown was called for both callbacks.
- Recorder write_recorder;
- write_recorder.Record(&recording_cb1, CREATION, 0, 0);
- write_recorder.Record(&recording_cb1, REGISTRATION, write_fd,
- EPOLLOUT | O_NONBLOCK);
- write_recorder.Record(&recording_cb1, UNREGISTRATION, write_fd, false);
- write_recorder.Record(&recording_cb1, SHUTDOWN, write_fd, 0);
- EXPECT_TRUE(recorder1->IsEqual(&write_recorder));
-
- Recorder read_recorder;
- read_recorder.Record(&recording_cb2, CREATION, 0, 0);
- read_recorder.Record(&recording_cb2, REGISTRATION, read_fd,
- EPOLLIN | O_NONBLOCK);
- read_recorder.Record(&recording_cb2, UNREGISTRATION, read_fd, false);
- read_recorder.Record(&recording_cb2, SHUTDOWN, read_fd, 0);
- EXPECT_TRUE(recorder2->IsEqual(&read_recorder));
-
- close(read_fd);
- close(write_fd);
-}
-
-class UnregisterCB : public EpollCallbackInterface {
- public:
- explicit UnregisterCB(int fd)
- : eps_(nullptr), fd_(fd), onshutdown_called_(false) {}
-
- ~UnregisterCB() override {}
-
- void OnShutdown(SimpleEpollServer* /*eps*/, int fd) override {
- eps_->UnregisterFD(fd_);
- eps_->UnregisterFD(fd);
- onshutdown_called_ = true;
- eps_ = nullptr;
- }
-
- void set_epollserver(SimpleEpollServer* eps) { eps_ = eps; }
- bool onshutdown_called() { return onshutdown_called_; }
-
- void OnRegistration(SimpleEpollServer* /*eps*/, int /*fd*/,
- int /*event_mask*/) override {}
- void OnModification(int /*fd*/, int /*event_mask*/) override {}
- void OnEvent(int /*fd*/, EpollEvent* /*event*/) override {}
- void OnUnregistration(int /*fd*/, bool /*replaced*/) override {}
-
- std::string Name() const override { return "UnregisterCB"; }
-
- protected:
- SimpleEpollServer* eps_;
- int fd_;
- bool onshutdown_called_;
-};
-
-// Check that unregistering fds in OnShutdown works cleanly.
-TEST(SimpleEpollServerTest, TestUnregisteringFDsOnShutdown) {
- int pipe_fds[2];
- if (pipe(pipe_fds) < 0) {
- EPOLL_PLOG(FATAL) << "pipe() failed";
- }
- int read_fd = pipe_fds[0];
- int write_fd = pipe_fds[1];
-
- UnregisterCB unreg_cb1(read_fd);
- UnregisterCB unreg_cb2(write_fd);
-
- {
- SimpleEpollServer ep;
- ep.set_timeout_in_us(1);
-
- unreg_cb1.set_epollserver(&ep);
- unreg_cb2.set_epollserver(&ep);
-
- // Register to listen to write events.
- ep.RegisterFD(write_fd, &unreg_cb1, EPOLLOUT | O_NONBLOCK);
- ep.RegisterFD(read_fd, &unreg_cb2, EPOLLIN | O_NONBLOCK);
- }
-
- // Make sure at least one onshutdown was called.
- EXPECT_TRUE(unreg_cb1.onshutdown_called() || unreg_cb2.onshutdown_called());
- // Make sure that both onshutdowns were not called.
- EXPECT_TRUE(
- !(unreg_cb1.onshutdown_called() && unreg_cb2.onshutdown_called()));
-
- close(read_fd);
- close(write_fd);
-}
-
-TEST(SimpleEpollServerTest, TestFDsAndAlarms) {
- SimpleEpollServer ep;
- ep.set_timeout_in_us(5);
- char data = 'x';
-
- int pipe_fds[2];
- if (pipe(pipe_fds) < 0) {
- EPOLL_PLOG(FATAL) << "pipe() failed";
- }
-
- RecordingCB recording_cb;
- const Recorder* recorder = recording_cb.recorder();
- const std::vector<RecordEntry>* records = recorder->records();
-
- TestAlarm alarm;
-
- ep.RegisterFDForRead(pipe_fds[0], &recording_cb);
-
- EXPECT_EQ(2u, records->size());
- EXPECT_FALSE(alarm.was_called());
-
- // Write to the pipe and set a longish alarm so we get a read event followed
- // by an alarm event.
- int written = write(pipe_fds[1], &data, 1);
- EXPECT_EQ(1, written);
- ep.WaitForEventsAndExecuteCallbacks();
- EXPECT_EQ(3u, records->size());
- EXPECT_FALSE(alarm.was_called());
- ep.RegisterAlarm(WallTimeNowInUsec() + 1000, &alarm);
- WaitForAlarm(&ep, alarm);
- EXPECT_EQ(3u, records->size());
- EXPECT_TRUE(alarm.was_called());
- alarm.Reset();
-
- // Now set a short alarm so the alarm and the read event are called together.
- ep.RegisterAlarm(WallTimeNowInUsec(), &alarm);
- written = write(pipe_fds[1], &data, 1);
- EXPECT_EQ(1, written);
- ep.WaitForEventsAndExecuteCallbacks();
- EXPECT_TRUE(alarm.was_called());
- EXPECT_EQ(4u, records->size());
-
- ep.UnregisterFD(pipe_fds[0]);
-
- close(pipe_fds[0]);
- close(pipe_fds[1]);
-}
-
-class EpollReader : public EpollCallbackInterface {
- public:
- explicit EpollReader(int len)
- : len_(0), expected_len_(len), done_reading_(false) {
- memset(&buf_, 0, kMaxBufLen);
- }
-
- ~EpollReader() override {}
-
- void OnRegistration(SimpleEpollServer* /*eps*/, int /*fd*/,
- int /*event_mask*/) override {}
-
- void OnModification(int /*fd*/, int /*event_mask*/) override {}
-
- void OnEvent(int fd, EpollEvent* event) override {
- if (event->in_events & EPOLLIN) {
- len_ += read(fd, &buf_ + len_, kMaxBufLen - len_);
- }
-
- // If we have finished reading...
- if (event->in_events & EPOLLHUP) {
- CHECK_EQ(len_, expected_len_);
- done_reading_ = true;
- }
- }
-
- void OnUnregistration(int /*fd*/, bool /*replaced*/) override {}
-
- void OnShutdown(SimpleEpollServer* /*eps*/, int /*fd*/) override {
- // None of the current tests involve having active callbacks when the
- // server shuts down.
- EPOLL_LOG(FATAL);
- }
-
- std::string Name() const override { return "EpollReader"; }
-
- // Returns true if the data in buf is the same as buf_, false otherwise.
- bool CheckOutput(char* buf, int len) {
- if (len != len_) {
- return false;
- }
- return !memcmp(buf, buf_, len);
- }
-
- bool done_reading() { return done_reading_; }
-
- protected:
- int len_;
- int expected_len_;
- char buf_[kMaxBufLen];
- bool done_reading_;
-};
-
-void TestPipe(char* test_message, int len) {
- int pipe_fds[2];
- if (pipe(pipe_fds) < 0) {
- EPOLL_PLOG(FATAL) << "pipe failed()";
- }
- int reader_pipe = pipe_fds[0];
- int writer_pipe = pipe_fds[1];
- int child_pid;
- memset(test_message, 'x', len);
-
- switch (child_pid = fork()) {
- case 0: { // Child will send message.
- const char* message = test_message;
- int size;
- close(reader_pipe);
- while ((size = write(writer_pipe, message, len)) > 0) {
- message += size;
- len -= size;
- if (len == 0) {
- break;
- }
- }
- if (len > 0) {
- EPOLL_PLOG(FATAL) << "write() failed";
- }
- close(writer_pipe);
-
- _exit(0);
- }
- case -1:
- EPOLL_PLOG(FATAL) << "fork() failed";
- break;
- default: { // Parent will receive message.
- close(writer_pipe);
- auto ep = std::make_unique<SimpleEpollServer>();
- ep->set_timeout_in_us(1);
- EpollReader reader(len);
- ep->RegisterFD(reader_pipe, &reader, EPOLLIN);
-
- int64_t start_ms = WallTimeNowInUsec() / 1000;
- // Loop until we're either done reading, or have waited ~10 us.
- while (!reader.done_reading() &&
- (WallTimeNowInUsec() / 1000 - start_ms) < 10000) {
- ep->WaitForEventsAndExecuteCallbacks();
- }
- ep->UnregisterFD(reader_pipe);
- CHECK(reader.CheckOutput(test_message, len));
- break;
- }
- }
-
- close(reader_pipe);
- close(writer_pipe);
-}
-
-TEST(SimpleEpollServerTest, TestSmallPipe) {
- char buf[kMaxBufLen];
- TestPipe(buf, 10);
-}
-
-TEST(SimpleEpollServerTest, TestLargePipe) {
- char buf[kMaxBufLen];
- TestPipe(buf, kMaxBufLen);
-}
-
-// Tests RegisterFDForRead as well as StopRead.
-TEST(SimpleEpollServerTest, TestRead) {
- SimpleEpollServer ep;
- ep.set_timeout_in_us(1);
- int len = 1;
-
- int pipe_fds[2];
- if (pipe(pipe_fds) < 0) {
- EPOLL_PLOG(FATAL) << "pipe() failed";
- }
- int read_fd = pipe_fds[0];
- int write_fd = pipe_fds[1];
-
- auto reader = std::make_unique<EpollReader>(len);
-
- // Check that registering a FD for read alerts us when there is data to be
- // read.
- ep.RegisterFDForRead(read_fd, reader.get());
- char data = 'a';
- int size = write(write_fd, &data, 1);
- EXPECT_EQ(1, size);
- ep.WaitForEventsAndExecuteCallbacks();
- EXPECT_TRUE(reader->CheckOutput(&data, len));
-
- // Remove the callback for read events, write to the pipe and make sure that
- // we did not read more data.
- ep.StopRead(read_fd);
- size = write(write_fd, &data, len);
- EXPECT_EQ(1, size);
- // The wait will return after timeout.
- ep.WaitForEventsAndExecuteCallbacks();
- EXPECT_TRUE(reader->CheckOutput(&data, len));
- ep.UnregisterFD(read_fd);
-
- close(read_fd);
- close(write_fd);
-}
-
-class EdgeTriggerCB : public EpollCallbackInterface {
- public:
- EdgeTriggerCB(int read_size, int write_size, char write_char, char peer_char)
- : eps_(nullptr),
- read_buf_(read_size),
- write_buf_(write_size, write_char),
- peer_char_(peer_char) {
- Reset();
- }
-
- ~EdgeTriggerCB() override {}
-
- void Reset() {
- CHECK(eps_ == nullptr);
- bytes_read_ = 0;
- bytes_written_ = 0;
- can_read_ = false;
- will_read_ = false;
- can_write_ = false;
- will_write_ = false;
- read_closed_ = false;
- write_closed_ = false;
- }
-
- void ResetByteCounts() { bytes_read_ = bytes_written_ = 0; }
-
- void set_will_read(bool will_read) { will_read_ = will_read; }
-
- void set_will_write(bool will_write) { will_write_ = will_write; }
-
- bool can_write() const { return can_write_; }
-
- int bytes_read() const { return bytes_read_; }
-
- int bytes_written() const { return bytes_written_; }
-
- void OnRegistration(SimpleEpollServer* eps, int fd, int event_mask) override {
- EXPECT_TRUE(eps_ == nullptr);
- eps_ = eps;
- Initialize(fd, event_mask);
- }
-
- void OnModification(int fd, int event_mask) override {
- EXPECT_TRUE(eps_ != nullptr);
- if (event_mask & EPOLLET) {
- Initialize(fd, event_mask);
- } else {
- eps_->SetFDNotReady(fd);
- }
- }
-
- void OnEvent(int fd, EpollEvent* event) override {
- const int event_mask = event->in_events;
- if (event_mask & (EPOLLHUP | EPOLLERR)) {
- write_closed_ = true;
- return;
- }
- if (will_read_ && event_mask & EPOLLIN) {
- EXPECT_FALSE(read_closed_);
- int read_size = read_buf_.size();
- memset(&read_buf_[0], 0, read_size);
- int len = recv(fd, &read_buf_[0], read_size, MSG_DONTWAIT);
- // Update the readiness states
- can_read_ = (len == read_size);
-
- if (len > 0) {
- bytes_read_ += len;
- EPOLL_VLOG(1) << "fd: " << fd << ", read " << len
- << ", total: " << bytes_read_;
- // Now check the bytes read
- EXPECT_TRUE(CheckReadBuffer(len));
- } else if (len < 0) {
- EPOLL_VLOG(1) << "fd: " << fd << " read hit EAGAIN";
- EXPECT_EQ(EAGAIN, errno) << strerror(errno);
- can_read_ = false;
- } else {
- read_closed_ = true;
- }
- }
- if (will_write_ && event_mask & EPOLLOUT) {
- // Write side close/full close can only detected by EPOLLHUP, which is
- // caused by EPIPE.
- EXPECT_FALSE(write_closed_);
- int write_size = write_buf_.size();
- int len = send(fd, &write_buf_[0], write_size, MSG_DONTWAIT);
- can_write_ = (len == write_size);
- if (len > 0) {
- bytes_written_ += len;
- EPOLL_VLOG(1) << "fd: " << fd << ", write " << len
- << ", total: " << bytes_written_;
- } else {
- EPOLL_VLOG(1) << "fd: " << fd << " write hit EAGAIN";
- EXPECT_EQ(EAGAIN, errno) << strerror(errno);
- can_write_ = false;
- }
- }
- // Since we can only get on the ready list once, wait till we confirm both
- // read and write side continuation state and set the correct event mask
- // for the ready list.
- event->out_ready_mask = can_read_ ? static_cast<int>(EPOLLIN) : 0;
- if (can_write_) {
- event->out_ready_mask |= EPOLLOUT;
- }
- }
-
- void OnUnregistration(int /*fd*/, bool /*replaced*/) override {
- EXPECT_TRUE(eps_ != nullptr);
- eps_ = nullptr;
- }
-
- void OnShutdown(SimpleEpollServer* /*eps*/, int /*fd*/) override {
- // None of the current tests involve having active callbacks when the
- // server shuts down.
- EPOLL_LOG(FATAL);
- }
-
- std::string Name() const override { return "EdgeTriggerCB"; }
-
- private:
- SimpleEpollServer* eps_;
- std::vector<char> read_buf_;
- int bytes_read_;
- std::vector<char> write_buf_;
- int bytes_written_;
- char peer_char_; // The char we expected to read.
- bool can_read_;
- bool will_read_;
- bool can_write_;
- bool will_write_;
- bool read_closed_;
- bool write_closed_;
-
- void Initialize(int fd, int event_mask) {
- CHECK(eps_);
- can_read_ = can_write_ = false;
- if (event_mask & EPOLLET) {
- int events = 0;
- if (event_mask & EPOLLIN) {
- events |= EPOLLIN;
- can_read_ = true;
- }
- if (event_mask & EPOLLOUT) {
- events |= EPOLLOUT;
- can_write_ = true;
- }
- eps_->SetFDReady(fd, events);
- }
- }
-
- bool CheckReadBuffer(int len) const {
- for (int i = 0; i < len; ++i) {
- if (peer_char_ != read_buf_[i]) {
- return false;
- }
- }
- return true;
- }
-};
-
-// Test adding and removing from the ready list.
-TEST(SimpleEpollServerTest, TestReadyList) {
- SimpleEpollServer ep;
- int pipe_fds[2];
- if (pipe(pipe_fds) < 0) {
- EPOLL_PLOG(FATAL) << "pipe() failed";
- }
-
- // Just use any CB will do, since we never wait on epoll events.
- EdgeTriggerCB reader1(0, 0, 0, 0);
- EdgeTriggerCB reader2(0, 0, 0, 0);
-
- ep.RegisterFD(pipe_fds[0], &reader1, EPOLLIN);
- ep.RegisterFD(pipe_fds[1], &reader2, EPOLLOUT);
-
- // Adding fds that are registered with eps
- EXPECT_FALSE(ep.IsFDReady(pipe_fds[0]));
- EXPECT_FALSE(ep.IsFDReady(pipe_fds[1]));
-
- ep.SetFDReady(pipe_fds[0], EPOLLIN);
- EXPECT_TRUE(ep.IsFDReady(pipe_fds[0]));
- EXPECT_FALSE(ep.IsFDReady(pipe_fds[1]));
- EXPECT_EQ(1u, ep.ReadyListSize());
- ep.SetFDReady(pipe_fds[1], EPOLLOUT);
- EXPECT_TRUE(ep.IsFDReady(pipe_fds[0]));
- EXPECT_TRUE(ep.IsFDReady(pipe_fds[1]));
- EXPECT_EQ(2u, ep.ReadyListSize());
-
- // Now check that SetFDNotReady doesn't affect other fds
- ep.SetFDNotReady(pipe_fds[0]);
- EXPECT_FALSE(ep.IsFDReady(pipe_fds[0]));
- EXPECT_TRUE(ep.IsFDReady(pipe_fds[1]));
- EXPECT_EQ(1u, ep.ReadyListSize());
-
- ep.UnregisterFD(pipe_fds[0]);
- ep.UnregisterFD(pipe_fds[1]);
- EXPECT_EQ(0u, ep.ReadyListSize());
-
- // Now try adding them when they are not registered, and it shouldn't work.
- ep.SetFDReady(pipe_fds[0], EPOLLIN);
- EXPECT_FALSE(ep.IsFDReady(pipe_fds[0]));
- EXPECT_EQ(0u, ep.ReadyListSize());
-
- close(pipe_fds[0]);
- close(pipe_fds[1]);
-}
-
-class EPSWaitThread : public EpollThread {
- public:
- explicit EPSWaitThread(SimpleEpollServer* eps)
- : EpollThread("EPSWait"), eps_(eps), done_(false) {}
-
- void Run() override { eps_->WaitForEventsAndExecuteCallbacks(); }
-
- bool done() { return done_; }
-
- private:
- SimpleEpollServer* eps_;
- bool done_;
-};
-
-TEST(EpollServerTest, TestWake) {
- SimpleEpollServer eps;
- eps.set_timeout_in_us(-1);
- EPSWaitThread eps_thread(&eps);
- eps_thread.Start();
-
- EXPECT_FALSE(eps_thread.done());
- eps.Wake();
- eps_thread.Join();
-}
-
-class UnRegisterWhileProcessingCB : public EpollCallbackInterface {
- public:
- explicit UnRegisterWhileProcessingCB(int fd) : eps_(nullptr), fd_(fd) {}
-
- ~UnRegisterWhileProcessingCB() override {}
-
- void OnShutdown(SimpleEpollServer* /*eps*/, int /*fd*/) override {}
-
- void set_epoll_server(SimpleEpollServer* eps) { eps_ = eps; }
- void OnRegistration(SimpleEpollServer* /*eps*/, int /*fd*/,
- int /*event_mask*/) override {}
- void OnModification(int /*fd*/, int /*event_mask*/) override {}
- void OnEvent(int /*fd*/, EpollEvent* /*event*/) override {
- // This should cause no problems.
- eps_->UnregisterFD(fd_);
- }
- void OnUnregistration(int /*fd*/, bool /*replaced*/) override {}
- std::string Name() const override { return "UnRegisterWhileProcessingCB"; }
-
- protected:
- SimpleEpollServer* eps_;
- int fd_;
-};
-
-class RegisterWhileProcessingCB : public EpollCallbackInterface {
- public:
- RegisterWhileProcessingCB(int fd, EpollCallbackInterface* cb)
- : eps_(nullptr), fd_(fd), cb_(cb) {}
-
- ~RegisterWhileProcessingCB() override {}
-
- void OnShutdown(SimpleEpollServer* /*eps*/, int /*fd*/) override {}
-
- void set_epoll_server(SimpleEpollServer* eps) { eps_ = eps; }
- void OnRegistration(SimpleEpollServer* /*eps*/, int /*fd*/,
- int /*event_mask*/) override {}
- void OnModification(int /*fd*/, int /*event_mask*/) override {}
- void OnEvent(int /*fd*/, EpollEvent* /*event*/) override {
- // This should cause no problems.
- eps_->RegisterFDForReadWrite(fd_, cb_);
- }
- void OnUnregistration(int /*fd*/, bool /*replaced*/) override {}
- std::string Name() const override { return "RegisterWhileProcessingCB"; }
-
- protected:
- SimpleEpollServer* eps_;
- int fd_;
- EpollCallbackInterface* cb_;
-};
-
-// Nothing bad should happen when we do this. We're -only-
-// testing that nothing bad occurs in this test.
-TEST(SimpleEpollServerTest, NothingBadWhenUnRegisteringFDWhileProcessingIt) {
- UnRegisterWhileProcessingCB cb(0);
- {
- FakeSimpleEpollServer epoll_server;
- cb.set_epoll_server(&epoll_server);
- epoll_server.RegisterFDForReadWrite(0, &cb);
- epoll_event ee;
- ee.data.fd = 0;
- epoll_server.AddEvent(0, ee);
- epoll_server.AdvanceBy(1);
- epoll_server.WaitForEventsAndExecuteCallbacks();
- }
-}
-
-//
-// testing that nothing bad occurs in this test.
-TEST(SimpleEpollServerTest,
- NoEventsDeliveredForFdsOfUnregisteredCBsWithReRegdFD) {
- // events: fd0, fd1, fd2
- // fd0 -> unreg fd2
- // fd1 -> reg fd2
- // fd2 -> no event should be seen
- RecordingCB recorder_cb;
- UnRegisterWhileProcessingCB unreg_cb(-3);
- RegisterWhileProcessingCB reg_other_cb(-3, &recorder_cb);
- {
- FakeSimpleEpollServer epoll_server;
- unreg_cb.set_epoll_server(&epoll_server);
- reg_other_cb.set_epoll_server(&epoll_server);
- epoll_server.RegisterFDForReadWrite(-1, &unreg_cb);
- epoll_server.RegisterFDForReadWrite(-2, ®_other_cb);
- epoll_server.RegisterFDForReadWrite(-3, &recorder_cb);
-
- epoll_event ee;
- ee.events = EPOLLIN; // asserted for all events for this test.
-
- // Note that these events are in 'backwards' order in terms of time.
- // Currently, the SimpleEpollServer code invokes the CBs from last delivered
- // to first delivered, so this is to be sure that we invoke the CB for -1
- // before -2, before -3.
- ee.data.fd = -1;
- epoll_server.AddEvent(2, ee);
- ee.data.fd = -2;
- epoll_server.AddEvent(1, ee);
- ee.data.fd = -3;
- epoll_server.AddEvent(0, ee);
-
- epoll_server.AdvanceBy(5);
- epoll_server.WaitForEventsAndExecuteCallbacks();
- }
-
- Recorder correct_recorder;
- correct_recorder.Record(&recorder_cb, CREATION, 0, 0);
- correct_recorder.Record(&recorder_cb, REGISTRATION, -3, EPOLLIN | EPOLLOUT);
- correct_recorder.Record(&recorder_cb, UNREGISTRATION, -3, 0);
- correct_recorder.Record(&recorder_cb, REGISTRATION, -3, EPOLLIN | EPOLLOUT);
- correct_recorder.Record(&recorder_cb, SHUTDOWN, -3, 0);
-
- EXPECT_TRUE(correct_recorder.IsEqual(recorder_cb.recorder()));
-}
-
-class ReRegWhileReadyListOnEvent : public EpollCallbackInterface {
- public:
- explicit ReRegWhileReadyListOnEvent(int /*fd*/) : eps_(nullptr) {}
-
- void OnShutdown(SimpleEpollServer* /*eps*/, int /*fd*/) override {}
-
- void set_epoll_server(SimpleEpollServer* eps) { eps_ = eps; }
- void OnRegistration(SimpleEpollServer* /*eps*/, int /*fd*/,
- int /*event_mask*/) override {}
- void OnModification(int /*fd*/, int /*event_mask*/) override {}
- void OnEvent(int fd, EpollEvent* /*event_mask*/) override {
- // This should cause no problems.
- eps_->UnregisterFD(fd);
- eps_->RegisterFDForReadWrite(fd, this);
- eps_->UnregisterFD(fd);
- }
- void OnUnregistration(int /*fd*/, bool /*replaced*/) override {}
- std::string Name() const override { return "ReRegWhileReadyListOnEvent"; }
-
- protected:
- SimpleEpollServer* eps_;
-};
-
-// Nothing bad should happen when we do this. We're -only-
-// testing that nothing bad occurs in this test.
-TEST(SimpleEpollServerTest,
- NothingBadWhenReRegisteringFDWhileProcessingFromReadyList) {
- ReRegWhileReadyListOnEvent cb(0);
- {
- FakeSimpleEpollServer epoll_server;
- cb.set_epoll_server(&epoll_server);
- epoll_server.RegisterFDForReadWrite(0, &cb);
- epoll_event ee;
- ee.data.fd = 0;
- epoll_server.AddEvent(0, ee);
- epoll_server.AdvanceBy(1);
- epoll_server.WaitForEventsAndExecuteCallbacks();
- }
-}
-
-class UnRegEverythingReadyListOnEvent : public EpollCallbackInterface {
- public:
- UnRegEverythingReadyListOnEvent() : eps_(nullptr), fd_(0), fd_range_(0) {}
-
- void set_fd(int fd) { fd_ = fd; }
- void set_fd_range(int fd_range) { fd_range_ = fd_range; }
- void set_num_called(int* num_called) { num_called_ = num_called; }
-
- void OnShutdown(SimpleEpollServer* /*eps*/, int /*fd*/) override {}
-
- void set_epoll_server(SimpleEpollServer* eps) { eps_ = eps; }
- void OnRegistration(SimpleEpollServer* eps, int fd,
- int /*event_mask*/) override {
- eps->SetFDReady(fd, EPOLLIN);
- }
- void OnModification(int /*fd*/, int /*event_mask*/) override {}
- void OnEvent(int /*fd*/, EpollEvent* /*event*/) override {
- // This should cause no problems.
- CHECK(num_called_ != nullptr);
- ++(*num_called_);
- // Note that we're iterating from -fd_range + 1 -> 0.
- // We do this because there is an FD installed into the
- // epollserver somewhere in the low numbers.
- // Using negative FD numbers (which are guaranteed to not
- // exist in the epoll-server) ensures that we will not
- // come in conflict with the preexisting FD.
- for (int i = -fd_range_ + 1; i <= 0; ++i) {
- eps_->UnregisterFD(i);
- }
- }
- void OnUnregistration(int /*fd*/, bool /*replaced*/) override {}
- std::string Name() const override {
- return "UnRegEverythingReadyListOnEvent";
- }
-
- protected:
- SimpleEpollServer* eps_;
- int fd_;
- int fd_range_;
- int* num_called_;
-};
-
-TEST(SimpleEpollServerTest,
- NothingBadWhenUnRegisteredWhileProcessingFromReadyList) {
- const size_t kNumCallbacks = 32u;
- UnRegEverythingReadyListOnEvent callbacks[kNumCallbacks];
- int num_called = 0;
- {
- FakeSimpleEpollServer epoll_server;
- for (size_t i = 0; i < kNumCallbacks; ++i) {
- callbacks[i].set_fd(-i);
- callbacks[i].set_fd_range(kNumCallbacks);
- callbacks[i].set_num_called(&num_called);
- callbacks[i].set_epoll_server(&epoll_server);
- epoll_server.RegisterFDForReadWrite(0, &callbacks[i]);
- epoll_event ee;
- ee.data.fd = -i;
- epoll_server.AddEvent(0, ee);
- }
- epoll_server.AdvanceBy(1);
- epoll_server.WaitForEventsAndExecuteCallbacks();
- epoll_server.WaitForEventsAndExecuteCallbacks();
- }
- EXPECT_EQ(1, num_called);
-}
-
-TEST(SimpleEpollServerTest, TestThatVerifyReadyListWorksWithNothingInList) {
- FakeSimpleEpollServer epoll_server;
- epoll_server.VerifyReadyList();
-}
-
-TEST(SimpleEpollServerTest, TestThatVerifyReadyListWorksWithStuffInLists) {
- FakeSimpleEpollServer epoll_server;
- epoll_server.VerifyReadyList();
-}
-
-TEST(SimpleEpollServerTest,
- ApproximateNowInUsAccurateOutideOfWaitForEventsAndExecuteCallbacks) {
- FakeSimpleEpollServer epoll_server;
- epoll_server.AdvanceBy(1232);
- EXPECT_EQ(epoll_server.ApproximateNowInUsec(), epoll_server.NowInUsec());
- epoll_server.AdvanceBy(1111);
- EXPECT_EQ(epoll_server.ApproximateNowInUsec(), epoll_server.NowInUsec());
-}
-
-class ApproximateNowInUsecTestCB : public EpollCallbackInterface {
- public:
- ApproximateNowInUsecTestCB() : feps_(nullptr), called_(false) {}
-
- void OnRegistration(SimpleEpollServer* /*eps*/, int /*fd*/,
- int /*event_mask*/) override {}
- void OnModification(int /*fd*/, int /*event_mask*/) override {}
- void OnEvent(int /*fd*/, EpollEvent* /*event*/) override {
- EXPECT_EQ(feps_->ApproximateNowInUsec(), feps_->NowInUsec());
- feps_->AdvanceBy(1111);
- EXPECT_EQ(1 * 1111 + feps_->ApproximateNowInUsec(), feps_->NowInUsec());
- feps_->AdvanceBy(1111);
- EXPECT_EQ(2 * 1111 + feps_->ApproximateNowInUsec(), feps_->NowInUsec());
- called_ = true;
- }
- void OnUnregistration(int /*fd*/, bool /*replaced*/) override {}
- void OnShutdown(SimpleEpollServer* /*eps*/, int /*fd*/) override {}
- std::string Name() const override { return "ApproximateNowInUsecTestCB"; }
-
- void set_fakeepollserver(FakeSimpleEpollServer* feps) { feps_ = feps; }
- bool called() const { return called_; }
-
- protected:
- FakeSimpleEpollServer* feps_;
- bool called_;
-};
-
-TEST(SimpleEpollServerTest,
- ApproximateNowInUsApproximateInsideOfWaitForEventsAndExecuteCallbacks) {
- int dummy_fd = 11111;
- ApproximateNowInUsecTestCB aniutcb;
- {
- FakeSimpleEpollServer epoll_server;
- aniutcb.set_fakeepollserver(&epoll_server);
-
- epoll_server.RegisterFD(dummy_fd, &aniutcb, EPOLLIN);
- epoll_event ee;
- ee.data.fd = dummy_fd;
- ee.events = EPOLLIN;
- epoll_server.AddEvent(10242, ee);
- epoll_server.set_timeout_in_us(-1);
- epoll_server.AdvanceByAndWaitForEventsAndExecuteCallbacks(20000);
- EXPECT_TRUE(aniutcb.called());
- }
-}
-
-// A mock epoll server that also simulates kernel delay in scheduling epoll
-// events.
-class FakeEpollServerWithDelay : public FakeSimpleEpollServer {
- public:
- FakeEpollServerWithDelay() : FakeSimpleEpollServer(), delay(0) {}
-
- int delay;
-
- protected:
- int epoll_wait_impl(int epfd, struct epoll_event* events, int max_events,
- int timeout_in_ms) override {
- int out = FakeSimpleEpollServer::epoll_wait_impl(epfd, events, max_events,
- timeout_in_ms);
- AdvanceBy(delay);
- return out;
- }
-};
-
-// A callback that records the epoll event's delay.
-class RecordDelayOnEvent : public EpollCallbackInterface {
- public:
- RecordDelayOnEvent() : last_delay(-1), eps_(nullptr) {}
-
- ~RecordDelayOnEvent() override {}
-
- void OnShutdown(SimpleEpollServer* /*eps*/, int /*fd*/) override {}
-
- std::string Name() const override { return "RecordDelayOnEvent"; }
-
- void set_epoll_server(SimpleEpollServer* eps) { eps_ = eps; }
- void OnRegistration(SimpleEpollServer* /*eps*/, int /*fd*/,
- int /*event_mask*/) override {}
- void OnModification(int /*fd*/, int /*event_mask*/) override {}
- void OnEvent(int /*fd*/, EpollEvent* /*event*/) override {
- last_delay = eps_->LastDelayInUsec();
- }
- void OnUnregistration(int /*fd*/, bool /*replaced*/) override {}
-
- int64_t last_delay;
-
- protected:
- SimpleEpollServer* eps_;
-};
-
-// Tests that an epoll callback sees the correct delay for its event when it
-// calls LastDelayInUsec().
-TEST(EpollServerTest, TestLastDelay) {
- RecordDelayOnEvent cb;
- FakeEpollServerWithDelay epoll_server;
-
- cb.set_epoll_server(&epoll_server);
-
- epoll_server.RegisterFDForReadWrite(0, &cb);
- epoll_event ee;
- ee.data.fd = 0;
-
- // Inject delay, and confirm that it's reported.
- epoll_server.set_timeout_in_us(5000);
- epoll_server.delay = 6000;
- epoll_server.AddEvent(0, ee);
- epoll_server.AdvanceBy(1);
- epoll_server.WaitForEventsAndExecuteCallbacks();
- EXPECT_EQ(cb.last_delay, 1000);
-
- // Fire an event before the timeout ends, and confirm that reported delay
- // isn't negative.
- epoll_server.set_timeout_in_us(5000);
- epoll_server.delay = 0;
- epoll_server.AddEvent(0, ee);
- epoll_server.AdvanceBy(1);
- epoll_server.WaitForEventsAndExecuteCallbacks();
- EXPECT_EQ(cb.last_delay, 0);
-
- // Wait forever until an event fires, and confirm there's no reported delay.
- epoll_server.set_timeout_in_us(-1);
- epoll_server.delay = 6000;
- epoll_server.AddEvent(0, ee);
- epoll_server.AdvanceBy(1);
- epoll_server.WaitForEventsAndExecuteCallbacks();
- EXPECT_EQ(cb.last_delay, 0);
-}
-
-TEST(SimpleEpollServerAlarmTest, TestShutdown) {
- std::unique_ptr<SimpleEpollServer> eps(new SimpleEpollServer);
- EpollAlarm alarm1;
- EpollAlarm alarm2;
-
- eps->RegisterAlarmApproximateDelta(10000000, &alarm1);
- eps->RegisterAlarmApproximateDelta(10000000, &alarm2);
-
- alarm2.UnregisterIfRegistered();
- EXPECT_FALSE(alarm2.registered());
- eps = nullptr;
-
- EXPECT_FALSE(alarm1.registered());
-}
-
-TEST(SimpleEpollServerAlarmTest, TestUnregister) {
- SimpleEpollServer eps;
- EpollAlarm alarm;
-
- eps.RegisterAlarmApproximateDelta(10000000, &alarm);
- EXPECT_TRUE(alarm.registered());
-
- alarm.UnregisterIfRegistered();
- EXPECT_FALSE(alarm.registered());
-
- alarm.UnregisterIfRegistered();
- EXPECT_FALSE(alarm.registered());
-}
-
-TEST(SimpleEpollServerAlarmTest, TestUnregisterOnDestruction) {
- EpollTestServer eps;
- std::unique_ptr<EpollAlarm> alarm(new EpollAlarm());
- EpollAlarm* alarm_ptr = alarm.get();
-
- eps.RegisterAlarmApproximateDelta(10000000, alarm.get());
- EXPECT_TRUE(eps.ContainsAlarm(alarm_ptr));
- alarm = nullptr;
- EXPECT_EQ(0u, eps.GetNumPendingAlarmsForTest());
-}
-
-TEST(SimpleEpollServerAlarmTest, TestUnregisterOnAlarm) {
- EpollTestServer eps;
- EpollAlarm alarm;
-
- eps.RegisterAlarmApproximateDelta(1, &alarm);
- EXPECT_TRUE(eps.ContainsAlarm(&alarm));
-
- while (alarm.registered()) {
- eps.WaitForEventsAndExecuteCallbacks();
- }
- EXPECT_FALSE(eps.ContainsAlarm(&alarm));
-}
-
-TEST(SimpleEpollServerAlarmTest, TestReregisterAlarm) {
- EpollTestAlarms ep;
-
- EpollAlarm alarm;
- ep.set_time(1000);
- ep.RegisterAlarm(5000, &alarm);
-
- EXPECT_EQ(1u, ep.GetNumPendingAlarmsForTest());
- alarm.ReregisterAlarm(6000);
- EXPECT_EQ(1u, ep.GetNumPendingAlarmsForTest());
-
- ep.set_time(5000);
- ep.set_timeout_in_us(0);
- ep.CallAndReregisterAlarmEvents();
- EXPECT_EQ(1u, ep.GetNumPendingAlarmsForTest());
-
- ep.set_time(6000);
- ep.CallAndReregisterAlarmEvents();
- EXPECT_EQ(0u, ep.GetNumPendingAlarmsForTest());
-}
-
-TEST(SimpleEpollServerAlarmTest, TestThatSameAlarmCanNotBeRegisteredTwice) {
- TestAlarm alarm;
- SimpleEpollServer epoll_server;
- epoll_server.RegisterAlarm(1, &alarm);
- EXPECT_EPOLL_BUG(epoll_server.RegisterAlarm(1, &alarm),
- "Alarm already exists");
-}
-
-} // namespace
-
-} // namespace test
-
-} // namespace epoll_server