Project import generated by Copybara.
PiperOrigin-RevId: 243658601
Change-Id: I2522d468d82c86f8f222cf0e70114d6a9ca2b78c
diff --git a/epoll_server/fake_simple_epoll_server.cc b/epoll_server/fake_simple_epoll_server.cc
new file mode 100644
index 0000000..7e55945
--- /dev/null
+++ b/epoll_server/fake_simple_epoll_server.cc
@@ -0,0 +1,56 @@
+// 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 "net/third_party/quiche/src/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/epoll_server/fake_simple_epoll_server.h b/epoll_server/fake_simple_epoll_server.h
new file mode 100644
index 0000000..d245743
--- /dev/null
+++ b/epoll_server/fake_simple_epoll_server.h
@@ -0,0 +1,113 @@
+// 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 <unordered_map>
+#include <unordered_set>
+
+#include "net/third_party/quiche/src/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::unordered_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/epoll_server/platform/api/epoll_address_test_utils.h b/epoll_server/platform/api/epoll_address_test_utils.h
new file mode 100644
index 0000000..ae87ded
--- /dev/null
+++ b/epoll_server/platform/api/epoll_address_test_utils.h
@@ -0,0 +1,16 @@
+// 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 "net/epoll_server/platform/impl/epoll_address_test_utils_impl.h"
+
+namespace epoll_server {
+
+int AddressFamilyUnderTest() { return AddressFamilyUnderTestImpl(); }
+
+} // namespace epoll_server
+
+#endif // QUICHE_EPOLL_SERVER_PLATFORM_API_EPOLL_ADDRESS_TEST_UTILS_H_
diff --git a/epoll_server/platform/api/epoll_bug.h b/epoll_server/platform/api/epoll_bug.h
new file mode 100644
index 0000000..d90c427
--- /dev/null
+++ b/epoll_server/platform/api/epoll_bug.h
@@ -0,0 +1,12 @@
+// 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 "net/epoll_server/platform/impl/epoll_bug_impl.h"
+
+#define EPOLL_BUG EPOLL_BUG_IMPL
+
+#endif // QUICHE_EPOLL_SERVER_PLATFORM_API_EPOLL_BUG_H_
diff --git a/epoll_server/platform/api/epoll_expect_bug.h b/epoll_server/platform/api/epoll_expect_bug.h
new file mode 100644
index 0000000..a7795c6
--- /dev/null
+++ b/epoll_server/platform/api/epoll_expect_bug.h
@@ -0,0 +1,12 @@
+// 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 "net/epoll_server/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/epoll_server/platform/api/epoll_logging.h b/epoll_server/platform/api/epoll_logging.h
new file mode 100644
index 0000000..bcd9b96
--- /dev/null
+++ b/epoll_server/platform/api/epoll_logging.h
@@ -0,0 +1,19 @@
+// 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 "net/epoll_server/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/epoll_server/platform/api/epoll_test.h b/epoll_server/platform/api/epoll_test.h
new file mode 100644
index 0000000..e5a0524
--- /dev/null
+++ b/epoll_server/platform/api/epoll_test.h
@@ -0,0 +1,11 @@
+// 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 "net/epoll_server/platform/impl/epoll_test_impl.h"
+#define EpollTest EpollTestImpl
+
+#endif // QUICHE_EPOLL_SERVER_PLATFORM_API_EPOLL_TEST_H_
diff --git a/epoll_server/platform/api/epoll_thread.h b/epoll_server/platform/api/epoll_thread.h
new file mode 100644
index 0000000..a41dd28
--- /dev/null
+++ b/epoll_server/platform/api/epoll_thread.h
@@ -0,0 +1,27 @@
+// 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 "net/epoll_server/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/epoll_server/platform/api/epoll_time.h b/epoll_server/platform/api/epoll_time.h
new file mode 100644
index 0000000..9fefaa7
--- /dev/null
+++ b/epoll_server/platform/api/epoll_time.h
@@ -0,0 +1,16 @@
+// 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_TIME_H_
+#define QUICHE_EPOLL_SERVER_PLATFORM_API_EPOLL_TIME_H_
+
+#include "net/epoll_server/platform/impl/epoll_time_impl.h"
+
+namespace epoll_server {
+
+int64_t WallTimeNowInUsec() { return WallTimeNowInUsecImpl(); }
+
+} // namespace epoll_server
+
+#endif // QUICHE_EPOLL_SERVER_PLATFORM_API_EPOLL_TIME_H_
diff --git a/epoll_server/simple_epoll_server.cc b/epoll_server/simple_epoll_server.cc
new file mode 100644
index 0000000..0117542
--- /dev/null
+++ b/epoll_server/simple_epoll_server.cc
@@ -0,0 +1,812 @@
+// 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 "net/third_party/quiche/src/epoll_server/simple_epoll_server.h"
+
+#include <errno.h> // for errno and strerror_r
+#include <stdlib.h> // for abort
+#include <unistd.h> // For read, pipe, close and write.
+
+#include <algorithm>
+#include <utility>
+
+#include "net/third_party/quiche/src/epoll_server/platform/api/epoll_bug.h"
+#include "net/third_party/quiche/src/epoll_server/platform/api/epoll_time.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 << "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 WallTimeNowInUsec(); }
+
+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/epoll_server/simple_epoll_server.h b/epoll_server/simple_epoll_server.h
new file mode 100644
index 0000000..78b6d79
--- /dev/null
+++ b/epoll_server/simple_epoll_server.h
@@ -0,0 +1,1050 @@
+// 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_H_
+#define QUICHE_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 "net/third_party/quiche/src/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_; }
+
+ // 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_H_
diff --git a/epoll_server/simple_epoll_server_test.cc b/epoll_server/simple_epoll_server_test.cc
new file mode 100644
index 0000000..140eb2b
--- /dev/null
+++ b/epoll_server/simple_epoll_server_test.cc
@@ -0,0 +1,2517 @@
+// 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 "net/third_party/quiche/src/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 <hash_map>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "net/third_party/quiche/src/epoll_server/fake_simple_epoll_server.h"
+#include "net/third_party/quiche/src/epoll_server/platform/api/epoll_address_test_utils.h"
+#include "net/third_party/quiche/src/epoll_server/platform/api/epoll_expect_bug.h"
+#include "net/third_party/quiche/src/epoll_server/platform/api/epoll_test.h"
+#include "net/third_party/quiche/src/epoll_server/platform/api/epoll_thread.h"
+#include "net/third_party/quiche/src/epoll_server/platform/api/epoll_time.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
+};
+
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+
+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];
+ read(fd, &buf, kLength);
+ }
+ }
+
+ 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);
+ }
+
+ 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) {
+ 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(2, records->size());
+
+ // Call handle event and make sure something was recorded.
+ ep()->HandleEvent(fd(), EPOLLOUT);
+ ep()->CallReadyListCallbacks();
+ EXPECT_EQ(3, records->size());
+
+ // Call handle event and make sure something was recorded.
+ ep()->HandleEvent(fd(), EPOLLIN | O_NONBLOCK);
+ ep()->CallReadyListCallbacks();
+ EXPECT_EQ(4, 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) {
+ 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 __gnu_cxx::hash_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(1, ep.GetNumPendingAlarmsForTest());
+ WaitForAlarm(&ep, alarm);
+ EXPECT_TRUE(alarm.was_called());
+ EXPECT_EQ(0, 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(1, 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(0, 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(1, ep.GetNumPendingAlarmsForTest());
+ WaitForAlarm(&ep, alarm);
+ EXPECT_TRUE(alarm.was_called());
+ EXPECT_EQ(0, ep.GetNumPendingAlarmsForTest());
+ alarm.Reset();
+ int64_t first_now = ep.ApproximateNowInUsec();
+ EXPECT_LT(0, 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(1, 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(0, 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(1, ep.GetNumPendingAlarmsForTest());
+ WaitForAlarm(&ep, alarm);
+ EXPECT_TRUE(alarm.was_called());
+ EXPECT_EQ(0, 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(2, 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(1, 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(2, 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(0, 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(1, 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(1, 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(0, 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(1, 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(1, 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(0, 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(1, ep.GetNumPendingAlarmsForTest());
+ ep.WaitForEventsAndExecuteCallbacks();
+ EXPECT_TRUE(alarm.was_called());
+ EXPECT_EQ(0, 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(1, ep.GetNumPendingAlarmsForTest());
+ EXPECT_TRUE(alarm2.onunregistration_called());
+
+ if (alarm1.get_token(&temptok)) {
+ ep.UnregisterAlarm(temptok);
+ }
+ EXPECT_EQ(0, 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(1, ep.GetNumPendingAlarmsForTest());
+ ASSERT_TRUE(alarm.get_token(&token));
+ ep.ReregisterAlarm(token, 6000);
+ EXPECT_EQ(1, 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(1, 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(2, 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(2, 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(2, 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(3, 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(4, 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(2, 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(2, records_one->size());
+ EXPECT_EQ(2, records_two->size());
+
+ write(pipe_one[1], &data, 1);
+ ep.WaitForEventsAndExecuteCallbacks();
+ EXPECT_EQ(3, records_one->size());
+ EXPECT_EQ(2, records_two->size());
+
+
+ write(pipe_two[1], &data, 1);
+ ep.WaitForEventsAndExecuteCallbacks();
+ EXPECT_EQ(3, records_one->size());
+ EXPECT_EQ(3, records_two->size());
+
+ write(pipe_one[1], &data, 1);
+ write(pipe_two[1], &data, 1);
+ ep.WaitForEventsAndExecuteCallbacks();
+ EXPECT_EQ(4, records_one->size());
+ EXPECT_EQ(4, 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 {}
+
+ 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(2, 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(3, records->size());
+ EXPECT_FALSE(alarm.was_called());
+ ep.RegisterAlarm(WallTimeNowInUsec() + 1000, &alarm);
+ WaitForAlarm(&ep, alarm);
+ EXPECT_EQ(3, 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(4, 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);
+ }
+
+ 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) {
+ 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) {
+ PLOG(FATAL) << "write() failed";
+ }
+ close(writer_pipe);
+
+ _exit(0);
+ }
+ case -1:
+ PLOG(FATAL) << "fork() failed";
+ break;
+ default: { // Parent will receive message.
+ close(writer_pipe);
+ auto ep = absl::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 = absl::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_ ? 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);
+ }
+
+ 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(1, ep.ReadyListSize());
+ ep.SetFDReady(pipe_fds[1], EPOLLOUT);
+ EXPECT_TRUE(ep.IsFDReady(pipe_fds[0]));
+ EXPECT_TRUE(ep.IsFDReady(pipe_fds[1]));
+ EXPECT_EQ(2, 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(1, ep.ReadyListSize());
+
+ ep.UnregisterFD(pipe_fds[0]);
+ ep.UnregisterFD(pipe_fds[1]);
+ EXPECT_EQ(0, 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(0, 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 {}
+ 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 {}
+ 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) override {
+ // This should cause no problems.
+ eps_->UnregisterFD(fd);
+ eps_->RegisterFDForReadWrite(fd, this);
+ eps_->UnregisterFD(fd);
+ }
+ void OnUnregistration(int fd, bool replaced) override {}
+ 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 {}
+ string Name() const override { return "UnRegEverythingReadyListOnEvent"; }
+
+ protected:
+ SimpleEpollServer* eps_;
+ int fd_;
+ int fd_range_;
+ int* num_called_;
+};
+
+TEST(SimpleEpollServerTest,
+ NothingBadWhenUnRegisteredWhileProcessingFromReadyList) {
+ UnRegEverythingReadyListOnEvent callbacks[32];
+ int num_called = 0;
+ {
+ FakeSimpleEpollServer epoll_server;
+ for (size_t i = 0; i < ABSL_ARRAYSIZE(callbacks); ++i) {
+ callbacks[i].set_fd(-i);
+ callbacks[i].set_fd_range(ABSL_ARRAYSIZE(callbacks));
+ 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 {}
+ 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 {}
+
+ 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(0, 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(1, ep.GetNumPendingAlarmsForTest());
+ alarm.ReregisterAlarm(6000);
+ EXPECT_EQ(1, ep.GetNumPendingAlarmsForTest());
+
+ ep.set_time(5000);
+ ep.set_timeout_in_us(0);
+ ep.CallAndReregisterAlarmEvents();
+ EXPECT_EQ(1, ep.GetNumPendingAlarmsForTest());
+
+ ep.set_time(6000);
+ ep.CallAndReregisterAlarmEvents();
+ EXPECT_EQ(0, 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