Project import generated by Copybara.
PiperOrigin-RevId: 243658601
Change-Id: I2522d468d82c86f8f222cf0e70114d6a9ca2b78c
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