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