blob: dbc3292f96d40266a8aecce8452fd455856a8227 [file] [log] [blame] [edit]
// 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 <memory>
#include <string>
#include <unordered_map>
#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_ptr_util.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];
int data_read;
do {
data_read = read(fd, &buf, kLength);
} while (data_read > 0);
}
}
void OnUnregistration(int fd, bool replaced) override {
recorder_->Record(this, UNREGISTRATION, fd, replaced);
}
void OnShutdown(SimpleEpollServer* eps, int fd) override {
if (fd >= 0) {
eps->UnregisterFD(fd);
}
recorder_->Record(this, SHUTDOWN, fd, 0);
}
std::string Name() const override { return "RecordingCB"; }
const Recorder* recorder() const { return recorder_; }
protected:
Recorder* recorder_;
};
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// A simple test server that adds some test functions to SimpleEpollServer as
// well as allowing access to protected functions.
class EpollTestServer : public SimpleEpollServer {
public:
EpollTestServer() : SimpleEpollServer() {}
~EpollTestServer() override {}
void CheckMapping(int fd, CB* cb) {
CBAndEventMask tmp;
tmp.fd = fd;
FDToCBMap::iterator fd_i = cb_map_.find(tmp);
CHECK(fd_i != cb_map_.end()); // Chokes CHECK_NE.
CHECK(fd_i->cb == cb);
}
void CheckNotMapped(int fd) {
CBAndEventMask tmp;
tmp.fd = fd;
FDToCBMap::iterator fd_i = cb_map_.find(tmp);
CHECK(fd_i == cb_map_.end()); // Chokes CHECK_EQ.
}
void CheckEventMask(int fd, int event_mask) {
CBAndEventMask tmp;
tmp.fd = fd;
FDToCBMap::iterator fd_i = cb_map_.find(tmp);
CHECK(cb_map_.end() != fd_i); // Chokes CHECK_NE.
CHECK_EQ(fd_i->event_mask, event_mask);
}
void CheckNotRegistered(int fd) {
struct epoll_event ee;
memset(&ee, 0, sizeof(ee));
// If the fd is registered, the epoll_ctl call would succeed (return 0) and
// the CHECK would fail.
CHECK(epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, fd, &ee));
}
size_t GetNumPendingAlarmsForTest() const { return alarm_map_.size(); }
bool ContainsAlarm(AlarmCB* ac) {
return all_alarms_.find(ac) != all_alarms_.end();
}
using SimpleEpollServer::WaitForEventsAndCallHandleEvents;
};
class EpollFunctionTest : public EpollTest {
public:
EpollFunctionTest()
: fd_(-1), fd2_(-1), recorder_(nullptr), cb_(nullptr), ep_(nullptr) {
}
~EpollFunctionTest() override {
delete ep_;
delete cb_;
}
void SetUp() override {
ep_ = new EpollTestServer();
cb_ = new RecordingCB();
// recorder_ is safe to use directly as we know it has the same scope as
// cb_
recorder_ = cb_->recorder();
int pipe_fds[2];
if (pipe(pipe_fds) < 0) {
EPOLL_PLOG(FATAL) << "pipe() failed";
}
fd_ = pipe_fds[0];
fd2_ = pipe_fds[1];
}
void TearDown() override {
close(fd_);
close(fd2_);
}
void DeleteSimpleEpollServer() {
delete ep_;
ep_ = nullptr;
}
int fd() { return fd_; }
int fd2() { return fd2_; }
EpollTestServer* ep() { return ep_; }
EpollCallbackInterface* cb() { return cb_; }
const Recorder* recorder() { return recorder_; }
private:
int fd_;
int fd2_;
const Recorder *recorder_;
RecordingCB* cb_;
EpollTestServer* ep_;
};
TEST_F(EpollFunctionTest, TestUnconnectedSocket) {
int fd = socket(AddressFamilyUnderTest(), SOCK_STREAM, IPPROTO_TCP);
ep()->RegisterFD(fd, cb(), EPOLLIN | EPOLLOUT);
ep()->WaitForEventsAndExecuteCallbacks();
Recorder tmp;
tmp.Record(cb(), CREATION, 0, 0);
tmp.Record(cb(), REGISTRATION, fd, EPOLLIN | EPOLLOUT);
tmp.Record(cb(), EVENT, fd, EPOLLOUT | EPOLLHUP);
EXPECT_TRUE(recorder()->IsEqual(&tmp));
}
TEST_F(EpollFunctionTest, TestRegisterFD) {
// Check that the basic register works.
ep()->RegisterFD(fd(), cb(), EPOLLIN);
// Make sure that the fd-CB mapping is there.
ep()->CheckMapping(fd(), cb());
ep()->CheckEventMask(fd(), EPOLLIN);
// Now make sure that if we register again, we stomp the old callback.
// Also make sure we handle O_NONBLOCK correctly
RecordingCB cb2;
ep()->RegisterFD(fd(), &cb2, EPOLLOUT | O_NONBLOCK);
ep()->CheckMapping(fd(), &cb2);
ep()->CheckEventMask(fd(), EPOLLOUT | O_NONBLOCK);
// Clean up.
ep()->UnregisterFD(fd());
}
TEST_F(EpollFunctionTest, TestRegisterFDForWrite) {
ep()->RegisterFDForWrite(fd(), cb());
ep()->CheckMapping(fd(), cb());
ep()->CheckEventMask(fd(), EPOLLOUT);
// Clean up.
ep()->UnregisterFD(fd());
}
TEST_F(EpollFunctionTest, TestRegisterFDForReadWrite) {
ep()->RegisterFDForReadWrite(fd(), cb());
ep()->CheckMapping(fd(), cb());
ep()->CheckEventMask(fd(), EPOLLIN | EPOLLOUT);
// Clean up.
ep()->UnregisterFD(fd());
}
TEST_F(EpollFunctionTest, TestRegisterFDForRead) {
ep()->RegisterFDForRead(fd(), cb());
ep()->CheckMapping(fd(), cb());
ep()->CheckEventMask(fd(), EPOLLIN);
ep()->UnregisterFD(fd());
}
TEST_F(EpollFunctionTest, TestUnregisterFD) {
ep()->RegisterFDForRead(fd(), cb());
ep()->CheckMapping(fd(), cb());
ep()->CheckEventMask(fd(), EPOLLIN);
// Unregister and make sure that it's gone.
ep()->UnregisterFD(fd());
ep()->CheckNotMapped(fd());
ep()->CheckNotRegistered(fd());
// And make sure that unregistering something a second time doesn't cause
// crashes.
ep()->UnregisterFD(fd());
ep()->CheckNotMapped(fd());
ep()->CheckNotRegistered(fd());
}
TEST_F(EpollFunctionTest, TestModifyCallback) {
// Check that nothing terrible happens if we modify an unregistered fd.
ep()->ModifyCallback(fd(), EPOLLOUT);
ep()->CheckNotMapped(fd());
ep()->CheckNotRegistered(fd());
// Check that the basic register works.
ep()->RegisterFD(fd(), cb(), EPOLLIN);
ep()->CheckMapping(fd(), cb());
ep()->CheckEventMask(fd(), EPOLLIN);
// Check that adding a signal swaps it out for the first.
ep()->ModifyCallback(fd(), EPOLLOUT);
ep()->CheckMapping(fd(), cb());
ep()->CheckEventMask(fd(), EPOLLOUT);
// Check that modifying from X to X works correctly.
ep()->ModifyCallback(fd(), EPOLLOUT);
ep()->CheckMapping(fd(), cb());
ep()->CheckEventMask(fd(), EPOLLOUT);
// Check that modifying from something to nothing works.
ep()->ModifyCallback(fd(), 0);
ep()->CheckMapping(fd(), cb());
ep()->CheckEventMask(fd(), 0);
ep()->UnregisterFD(fd());
}
TEST_F(EpollFunctionTest, TestStopRead) {
ep()->RegisterFDForReadWrite(fd(), cb());
ep()->CheckMapping(fd(), cb());
ep()->CheckEventMask(fd(), EPOLLIN | EPOLLOUT);
// Unregister and make sure you only lose the read event.
ep()->StopRead(fd());
ep()->CheckMapping(fd(), cb());
ep()->CheckEventMask(fd(), EPOLLOUT);
ep()->UnregisterFD(fd());
}
TEST_F(EpollFunctionTest, TestStartRead) {
ep()->RegisterFDForWrite(fd(), cb());
ep()->CheckMapping(fd(), cb());
ep()->CheckEventMask(fd(), EPOLLOUT);
// Make sure that StartRead adds EPOLLIN and doesn't remove other signals.
ep()->StartRead(fd());
ep()->CheckMapping(fd(), cb());
ep()->CheckEventMask(fd(), EPOLLIN | EPOLLOUT);
// Clean up.
ep()->UnregisterFD(fd());
}
TEST_F(EpollFunctionTest, TestStopWrite) {
ep()->RegisterFDForReadWrite(fd(), cb());
ep()->CheckMapping(fd(), cb());
ep()->CheckEventMask(fd(), EPOLLIN | EPOLLOUT);
// Unregister write and make sure you only lose the write event.
ep()->StopWrite(fd());
ep()->CheckMapping(fd(), cb());
ep()->CheckEventMask(fd(), EPOLLIN);
ep()->UnregisterFD(fd());
}
TEST_F(EpollFunctionTest, TestStartWrite) {
ep()->RegisterFDForRead(fd(), cb());
ep()->CheckMapping(fd(), cb());
ep()->CheckEventMask(fd(), EPOLLIN);
// Make sure that StartWrite adds EPOLLOUT and doesn't remove other
// signals.
ep()->StartWrite(fd());
ep()->CheckMapping(fd(), cb());
ep()->CheckEventMask(fd(), EPOLLIN | EPOLLOUT);
// Clean up.
ep()->UnregisterFD(fd());
}
TEST_F(EpollFunctionTest, TestSet_timeout_in_us) {
// Check that set works with various values. There's a separate test below
// to make sure the values are used properly.
ep()->set_timeout_in_us(10);
EXPECT_EQ(10, ep()->timeout_in_us_for_test());
ep()->set_timeout_in_us(-1);
EXPECT_EQ(-1, ep()->timeout_in_us_for_test());
}
TEST_F(EpollFunctionTest, TestHandleEvent) {
const std::vector<RecordEntry> *records = recorder()->records();
// Test that nothing bad happens if the FD is not in the map.
ep()->HandleEvent(fd(), EPOLLOUT);
ep()->CallReadyListCallbacks();
ep()->RegisterFD(fd(), cb(), 0);
ep()->CheckMapping(fd(), cb());
ep()->CheckEventMask(fd(), 0);
// At this point we should have creation and registration recorded.
EXPECT_EQ(2u, records->size());
// Call handle event and make sure something was recorded.
ep()->HandleEvent(fd(), EPOLLOUT);
ep()->CallReadyListCallbacks();
EXPECT_EQ(3u, records->size());
// Call handle event and make sure something was recorded.
ep()->HandleEvent(fd(), EPOLLIN | O_NONBLOCK);
ep()->CallReadyListCallbacks();
EXPECT_EQ(4u, records->size());
Recorder tmp;
tmp.Record(cb(), CREATION, 0, 0);
tmp.Record(cb(), REGISTRATION, fd(), 0);
tmp.Record(cb(), EVENT, fd(), EPOLLOUT);
tmp.Record(cb(), EVENT, fd(), EPOLLIN | O_NONBLOCK);
EXPECT_TRUE(recorder()->IsEqual(&tmp));
ep()->UnregisterFD(fd());
}
TEST_F(EpollFunctionTest, TestNumFDsRegistered) {
EXPECT_EQ(0, ep()->NumFDsRegistered());
ep()->RegisterFD(fd(), cb(), 0);
EXPECT_EQ(1, ep()->NumFDsRegistered());
ep()->RegisterFD(fd2(), cb(), 0);
EXPECT_EQ(2, ep()->NumFDsRegistered());
ep()->RegisterFD(fd2(), cb(), 0);
EXPECT_EQ(2, ep()->NumFDsRegistered());
ep()->UnregisterFD(fd2());
EXPECT_EQ(1, ep()->NumFDsRegistered());
ep()->UnregisterFD(fd());
EXPECT_EQ(0, ep()->NumFDsRegistered());
}
// Check all of the individual signals and 1-2 combinations.
TEST_F(EpollFunctionTest, TestEventMaskToString) {
std::string test;
test = SimpleEpollServer::EventMaskToString(EPOLLIN);
EXPECT_EQ(test, "EPOLLIN ");
test = SimpleEpollServer::EventMaskToString(EPOLLOUT);
EXPECT_EQ(test, "EPOLLOUT ");
test = SimpleEpollServer::EventMaskToString(EPOLLPRI);
EXPECT_EQ(test, "EPOLLPRI ");
test = SimpleEpollServer::EventMaskToString(EPOLLERR);
EXPECT_EQ(test, "EPOLLERR ");
test = SimpleEpollServer::EventMaskToString(EPOLLHUP);
EXPECT_EQ(test, "EPOLLHUP ");
test = SimpleEpollServer::EventMaskToString(EPOLLHUP | EPOLLIN);
EXPECT_EQ(test, "EPOLLIN EPOLLHUP ");
test = SimpleEpollServer::EventMaskToString(EPOLLIN | EPOLLOUT);
EXPECT_EQ(test, "EPOLLIN EPOLLOUT ");
}
class TestAlarm : public EpollAlarmCallbackInterface {
public:
TestAlarm()
: time_before_next_alarm_(-1),
was_called_(false),
num_called_(0),
absolute_time_(false),
onshutdown_called_(false),
has_token_(false),
eps_(nullptr) {
}
~TestAlarm() override {
}
int64_t OnAlarm() override {
has_token_ = false;
was_called_ = true;
++num_called_;
if (time_before_next_alarm_ < 0) {
return 0;
}
if (absolute_time_) {
return time_before_next_alarm_;
} else {
return WallTimeNowInUsec() + time_before_next_alarm_;
}
}
void OnShutdown(SimpleEpollServer* eps) override {
onshutdown_called_ = true;
has_token_ = false;
}
void OnRegistration(const SimpleEpollServer::AlarmRegToken& token,
SimpleEpollServer* eps) override {
has_token_ = true;
last_token_ = token;
eps_ = eps;
}
void OnUnregistration() override {
has_token_ = false;
}
void UnregisterIfRegistered(SimpleEpollServer* eps) {
if (has_token_) {
eps->UnregisterAlarm(last_token_);
}
}
void ReregisterAlarm(int64_t timeout_in_us) {
CHECK(has_token_);
eps_->ReregisterAlarm(last_token_, timeout_in_us);
}
void Reset() {
time_before_next_alarm_ = -1;
was_called_ = false;
absolute_time_ = false;
}
bool was_called() const { return was_called_; }
int num_called() const { return num_called_; }
void set_time_before_next_alarm(int64_t time) {
time_before_next_alarm_ = time;
}
void set_absolute_time(bool absolute) {
absolute_time_ = absolute;
}
bool onshutdown_called() { return onshutdown_called_; }
protected:
int64_t time_before_next_alarm_;
bool was_called_;
int num_called_;
// Is time_before_next_alarm relative to the current time or absolute?
bool absolute_time_;
bool onshutdown_called_;
bool has_token_;
SimpleEpollServer::AlarmRegToken last_token_;
SimpleEpollServer* eps_;
};
class TestChildAlarm;
// This node unregister all other alarms when it receives
// OnShutdown() from any one child.
class TestParentAlarm {
public:
void OnShutdown(TestChildAlarm* child, SimpleEpollServer* eps) {
// Unregister
for (ChildTokenMap::const_iterator it = child_tokens_.begin();
it != child_tokens_.end(); ++it) {
if (it->first != child) {
eps->UnregisterAlarm(it->second);
}
}
child_tokens_.clear();
}
void OnRegistration(TestChildAlarm* child,
const SimpleEpollServer::AlarmRegToken& token) {
child_tokens_[child] = token;
}
protected:
typedef std::unordered_map<TestChildAlarm*, SimpleEpollServer::AlarmRegToken>
ChildTokenMap;
ChildTokenMap child_tokens_;
};
class TestChildAlarm : public TestAlarm {
public:
void set_parent(TestParentAlarm* tp) { parent_ = tp; }
void OnShutdown(SimpleEpollServer* eps) override {
onshutdown_called_ = true;
// Inform parent of shutdown
parent_->OnShutdown(this, eps);
}
void OnRegistration(const SimpleEpollServer::AlarmRegToken& token,
SimpleEpollServer* eps) override {
parent_->OnRegistration(this, token);
}
protected:
TestParentAlarm* parent_;
};
class TestAlarmThatRegistersAnotherAlarm : public TestAlarm {
public:
TestAlarmThatRegistersAnotherAlarm()
: alarm_(nullptr),
reg_time_delta_usec_(0),
eps_to_register_(nullptr),
has_reg_alarm_(false) {}
void SetRegisterAlarm(TestAlarm* alarm, int64_t time_delta_usec,
SimpleEpollServer* eps) {
alarm_ = alarm;
reg_time_delta_usec_ = time_delta_usec;
has_reg_alarm_ = true;
eps_to_register_ = eps;
}
int64_t OnAlarm() override {
if (has_reg_alarm_) {
eps_to_register_->RegisterAlarm(
eps_to_register_->ApproximateNowInUsec() + reg_time_delta_usec_,
alarm_);
has_reg_alarm_ = false;
}
return TestAlarm::OnAlarm();
}
protected:
TestAlarm* alarm_;
int64_t reg_time_delta_usec_;
SimpleEpollServer* eps_to_register_;
bool has_reg_alarm_;
};
class TestAlarmThatRegistersAndReregistersAnotherAlarm : public TestAlarm {
public:
TestAlarmThatRegistersAndReregistersAnotherAlarm()
: alarm_(nullptr),
reg_time_delta_usec_(0),
reregister_time_delta_usec_(0),
eps_to_register_(nullptr),
has_reg_alarm_(false) {}
void SetRegisterAndReregisterAlarm(TestAlarm* alarm, int64_t time_delta_usec,
int64_t reregister_delta_usec,
SimpleEpollServer* eps) {
alarm_ = alarm;
reg_time_delta_usec_ = time_delta_usec;
reregister_time_delta_usec_ = reregister_delta_usec;
has_reg_alarm_ = true;
eps_to_register_ = eps;
}
int64_t OnAlarm() override {
if (has_reg_alarm_) {
eps_to_register_->RegisterAlarm(
eps_to_register_->ApproximateNowInUsec() + reg_time_delta_usec_,
alarm_);
alarm_->ReregisterAlarm(eps_to_register_->ApproximateNowInUsec() +
reregister_time_delta_usec_);
has_reg_alarm_ = false;
}
return TestAlarm::OnAlarm();
}
protected:
TestAlarm* alarm_;
int64_t reg_time_delta_usec_;
int64_t reregister_time_delta_usec_;
SimpleEpollServer* eps_to_register_;
bool has_reg_alarm_;
};
class TestAlarmThatUnregistersAnotherAlarm : public TestAlarm {
public:
TestAlarmThatUnregistersAnotherAlarm()
: alarm_(nullptr), eps_to_register_(nullptr), has_unreg_alarm_(false) {}
void SetUnregisterAlarm(TestAlarm* alarm, SimpleEpollServer* eps) {
alarm_ = alarm;
has_unreg_alarm_ = true;
eps_to_register_ = eps;
}
int64_t OnAlarm() override {
if (has_unreg_alarm_) {
has_unreg_alarm_ = false;
alarm_->UnregisterIfRegistered(eps_to_register_);
}
return TestAlarm::OnAlarm();
}
protected:
TestAlarm* alarm_;
SimpleEpollServer* eps_to_register_;
bool has_unreg_alarm_;
};
class TestAlarmUnregister : public TestAlarm {
public:
TestAlarmUnregister()
: onunregistration_called_(false),
iterator_token_(nullptr) {
}
~TestAlarmUnregister() override {
delete iterator_token_;
}
void OnShutdown(SimpleEpollServer* eps) override {
onshutdown_called_ = true;
}
int64_t OnAlarm() override {
delete iterator_token_;
iterator_token_ = nullptr;
return TestAlarm::OnAlarm();
}
void OnRegistration(const SimpleEpollServer::AlarmRegToken& token,
SimpleEpollServer* eps) override {
// Multiple iterator tokens are not maintained by this code,
// so we should have reset the iterator_token in OnAlarm or
// OnUnregistration.
CHECK(iterator_token_ == nullptr);
iterator_token_ = new SimpleEpollServer::AlarmRegToken(token);
}
void OnUnregistration() override {
delete iterator_token_;
iterator_token_ = nullptr;
// Make sure that this alarm was not already unregistered.
CHECK(onunregistration_called_ == false);
onunregistration_called_ = true;
}
bool onunregistration_called() { return onunregistration_called_; }
// Returns true if the token has been filled in with the saved iterator
// and false if it has not.
bool get_token(SimpleEpollServer::AlarmRegToken* token) {
if (iterator_token_ != nullptr) {
*token = *iterator_token_;
return true;
} else {
return false;
}
}
protected:
bool onunregistration_called_;
SimpleEpollServer::AlarmRegToken* iterator_token_;
};
void WaitForAlarm(SimpleEpollServer* eps, const TestAlarm& alarm) {
for (int i = 0; i < 5; ++i) {
// Ideally we would only have to call this once but it could wake up a bit
// early and so not call the alarm. If it wakes up early several times
// there is something wrong.
eps->WaitForEventsAndExecuteCallbacks();
if (alarm.was_called()) {
break;
}
}
}
// Check a couple of alarm times to make sure they're falling within a
// reasonable range.
TEST(SimpleEpollServerTest, TestAlarms) {
EpollTestServer ep;
TestAlarm alarm;
int alarm_time = 10;
// Register an alarm and make sure we wait long enough to hit it.
ep.set_timeout_in_us(alarm_time * 1000 * 2);
ep.RegisterAlarm(WallTimeNowInUsec() + alarm_time, &alarm);
EXPECT_EQ(1u, ep.GetNumPendingAlarmsForTest());
WaitForAlarm(&ep, alarm);
EXPECT_TRUE(alarm.was_called());
EXPECT_EQ(0u, ep.GetNumPendingAlarmsForTest());
alarm.Reset();
// Test a different time just to be careful.
alarm_time = 20;
ep.set_timeout_in_us(alarm_time * 1000 * 2);
ep.RegisterAlarm(WallTimeNowInUsec() + alarm_time, &alarm);
EXPECT_EQ(1u, ep.GetNumPendingAlarmsForTest());
WaitForAlarm(&ep, alarm);
EXPECT_TRUE(alarm.was_called());
alarm.Reset();
// The alarm was a one-time thing. Make sure that we don't hit it again.
EXPECT_EQ(0u, ep.GetNumPendingAlarmsForTest());
ep.WaitForEventsAndExecuteCallbacks();
EXPECT_FALSE(alarm.was_called());
alarm.Reset();
}
// Same as above, but using RegisterAlarmApproximateDelta.
TEST(SimpleEpollServerTest, TestRegisterAlarmApproximateDelta) {
EpollTestServer ep;
TestAlarm alarm;
int alarm_time = 10;
// Register an alarm and make sure we wait long enough to hit it.
ep.set_timeout_in_us(alarm_time * 1000 * 2);
ep.RegisterAlarmApproximateDelta(alarm_time * 1000, &alarm);
EXPECT_EQ(1u, ep.GetNumPendingAlarmsForTest());
WaitForAlarm(&ep, alarm);
EXPECT_TRUE(alarm.was_called());
EXPECT_EQ(0u, ep.GetNumPendingAlarmsForTest());
alarm.Reset();
int64_t first_now = ep.ApproximateNowInUsec();
EXPECT_LT(0u, first_now);
// Test a different time just to be careful.
alarm_time = 20;
ep.set_timeout_in_us(alarm_time * 1000 * 2);
ep.RegisterAlarmApproximateDelta(alarm_time * 1000, &alarm);
EXPECT_EQ(1u, ep.GetNumPendingAlarmsForTest());
WaitForAlarm(&ep, alarm);
EXPECT_TRUE(alarm.was_called());
alarm.Reset();
int64_t second_now = ep.ApproximateNowInUsec();
EXPECT_LT(first_now, second_now);
// The alarm was a one-time thing. Make sure that we don't hit it again.
EXPECT_EQ(0u, ep.GetNumPendingAlarmsForTest());
ep.WaitForEventsAndExecuteCallbacks();
EXPECT_FALSE(alarm.was_called());
alarm.Reset();
}
TEST(SimpleEpollServerTest, TestAlarmsWithInfiniteWait) {
EpollTestServer ep;
TestAlarm alarm;
int alarm_time = 10;
// Register an alarm and make sure we wait long enough to hit it.
ep.set_timeout_in_us(-1);
ep.RegisterAlarm(WallTimeNowInUsec() + alarm_time, &alarm);
EXPECT_EQ(1u, ep.GetNumPendingAlarmsForTest());
WaitForAlarm(&ep, alarm);
EXPECT_TRUE(alarm.was_called());
EXPECT_EQ(0u, ep.GetNumPendingAlarmsForTest());
alarm.Reset();
}
// In this test we have an alarm that when fires gets re-registered
// at almost the same time at which it fires. Here, we want to make
// sure that when the alarm gets re-registered we do not call OnAlarm()
// on the same Alarm object again, until we have called
// WaitForEventsAndExecuteCallbacks(). A poor implementation of epoll
// server alarm handling can potentially cause OnAlarm() to be called
// multiple times. We make sure that the epoll server is not going in
// an infinite loop by checking that OnAlarm() is called exactly once
// on the alarm object that got registered again.
TEST(SimpleEpollServerTest, TestAlarmsThatGetReRegisteredAreNotCalledTwice) {
// This alarm would get registered again
TestAlarm alarm;
TestAlarm alarm2;
EpollTestServer ep;
ep.set_timeout_in_us(-1);
int64_t alarm_time = 10;
int64_t abs_time = WallTimeNowInUsec() + alarm_time * 1000;
// This will make the alarm re-register when OnAlarm is called.
alarm.set_absolute_time(true);
alarm.set_time_before_next_alarm(abs_time + 2);
// Register two alarms and make sure we wait long enough to hit it.
ep.RegisterAlarm(abs_time, &alarm);
ep.RegisterAlarm(abs_time, &alarm2);
EXPECT_EQ(2u, ep.GetNumPendingAlarmsForTest());
WaitForAlarm(&ep, alarm);
EXPECT_TRUE(alarm.was_called());
// Make sure that alarm is called only once.
EXPECT_EQ(1, alarm.num_called());
EXPECT_EQ(1u, ep.GetNumPendingAlarmsForTest());
alarm.Reset();
}
// Here we make sure that when one alarm unregisters another alarm
// (that is supposed to be registered again because its OnAlarm
// returned > 0), the alarm thats supposed to be unregistered does
// actually gets unregistered.
TEST(SimpleEpollServerTest, TestAlarmsOneOnAlarmUnRegistersAnotherAlarm) {
TestAlarm alarm;
TestAlarmThatUnregistersAnotherAlarm alarm2;
EpollTestServer ep;
ep.set_timeout_in_us(-1);
int64_t alarm_time = 1;
int64_t abs_time = WallTimeNowInUsec() + alarm_time * 1000;
// This will make the alarm re-register when OnAlarm is called.
alarm.set_absolute_time(true);
alarm.set_time_before_next_alarm(abs_time + 2);
// Register two alarms and make sure we wait long enough to hit it.
ep.RegisterAlarm(abs_time, &alarm);
// This would cause us to unregister alarm when OnAlarm is called
// on alarm2.
alarm2.SetUnregisterAlarm(&alarm, &ep);
ep.RegisterAlarm(abs_time + 1, &alarm2);
EXPECT_EQ(2u, ep.GetNumPendingAlarmsForTest());
WaitForAlarm(&ep, alarm);
EXPECT_TRUE(alarm.was_called());
// Make sure that alarm is called only once.
EXPECT_EQ(1, alarm.num_called());
EXPECT_EQ(0u, ep.GetNumPendingAlarmsForTest());
alarm.Reset();
}
// Check a couple of alarm times to make sure they're falling within a
// reasonable range.
TEST(SimpleEpollServerTest, TestRepeatAlarms) {
EpollTestServer ep;
TestAlarm alarm;
int alarm_time = 20;
// Register an alarm and make sure we wait long enough to hit it.
ep.set_timeout_in_us(alarm_time * 1000 * 2);
alarm.set_time_before_next_alarm(1000*alarm_time);
ep.RegisterAlarm(WallTimeNowInUsec() + alarm_time, &alarm);
EXPECT_EQ(1u, ep.GetNumPendingAlarmsForTest());
WaitForAlarm(&ep, alarm);
// When we wake up it should be because the Alarm has been called, and has
// registered itself to be called again.
// Make sure the first alarm was called properly.
EXPECT_TRUE(alarm.was_called());
// Resetting means that the alarm is no longer a recurring alarm. It will be
// called once more and then stop.
alarm.Reset();
// Make sure the alarm is called one final time.
EXPECT_EQ(1u, ep.GetNumPendingAlarmsForTest());
ep.set_timeout_in_us(alarm_time * 1000 * 2);
WaitForAlarm(&ep, alarm);
EXPECT_TRUE(alarm.was_called());
alarm.Reset();
// The alarm was a one-time thing. Make sure that we don't hit it again.
EXPECT_EQ(0u, ep.GetNumPendingAlarmsForTest());
ep.WaitForEventsAndExecuteCallbacks();
EXPECT_FALSE(alarm.was_called());
}
// Verify that an alarm that repeats itself in the past works properly.
TEST(SimpleEpollServerTest, TestRepeatAlarmInPast) {
EpollTestServer ep;
TestAlarm alarm;
int64_t alarm_time = 20;
int64_t abs_time = WallTimeNowInUsec() + alarm_time * 1000;
// Make the alarm re-register in the past when OnAlarm is called.
alarm.set_absolute_time(true);
alarm.set_time_before_next_alarm(abs_time - 1000);
// Register the alarm and make sure we wait long enough to hit it.
ep.set_timeout_in_us(alarm_time * 1000 * 2);
ep.RegisterAlarm(abs_time, &alarm);
EXPECT_EQ(1u, ep.GetNumPendingAlarmsForTest());
WaitForAlarm(&ep, alarm);
// When we wake up it should be because the Alarm has been called, and has
// registered itself to be called again.
// Make sure the first alarm was called properly.
EXPECT_TRUE(alarm.was_called());
// Resetting means that the alarm is no longer a recurring alarm. It will be
// called once more and then stop.
alarm.Reset();
// Make sure the alarm is called one final time.
EXPECT_EQ(1u, ep.GetNumPendingAlarmsForTest());
ep.set_timeout_in_us(alarm_time * 1000 * 2);
WaitForAlarm(&ep, alarm);
EXPECT_TRUE(alarm.was_called());
alarm.Reset();
// The alarm was a one-time thing. Make sure that we don't hit it again.
EXPECT_EQ(0u, ep.GetNumPendingAlarmsForTest());
ep.WaitForEventsAndExecuteCallbacks();
EXPECT_FALSE(alarm.was_called());
}
class EpollTestAlarms : public SimpleEpollServer {
public:
EpollTestAlarms() : SimpleEpollServer() {}
inline int64_t NowInUsec() const override { return time_; }
void CallAndReregisterAlarmEvents() override {
recorded_now_in_us_ = NowInUsec();
SimpleEpollServer::CallAndReregisterAlarmEvents();
}
void set_time(int64_t time) { time_ = time; }
size_t GetNumPendingAlarmsForTest() const { return alarm_map_.size(); }
private:
int64_t time_;
};
// Test multiple interleaving alarms to make sure they work right.
// Pattern is roughly:
// time: 15 20 30 40
// alarm: A B A' C
TEST(SimpleEpollServerTest, TestMultipleAlarms) {
EpollTestAlarms ep;
TestAlarm alarmA;
TestAlarm alarmB;
TestAlarm alarmC;
ep.set_timeout_in_us(50 * 1000 * 2);
alarmA.set_time_before_next_alarm(1000 * 30);
alarmA.set_absolute_time(true);
ep.RegisterAlarm(15 * 1000, &alarmA);
ep.RegisterAlarm(20 * 1000, &alarmB);
ep.RegisterAlarm(40 * 1000, &alarmC);
ep.set_time(15 * 1000);
ep.CallAndReregisterAlarmEvents(); // A
EXPECT_TRUE(alarmA.was_called());
EXPECT_FALSE(alarmB.was_called());
EXPECT_FALSE(alarmC.was_called());
alarmA.Reset(); // Unregister A in the future.
ep.set_time(20 * 1000);
ep.CallAndReregisterAlarmEvents(); // B
EXPECT_FALSE(alarmA.was_called());
EXPECT_TRUE(alarmB.was_called());
EXPECT_FALSE(alarmC.was_called());
alarmB.Reset();
ep.set_time(30 * 1000);
ep.CallAndReregisterAlarmEvents(); // A
EXPECT_TRUE(alarmA.was_called());
EXPECT_FALSE(alarmB.was_called());
EXPECT_FALSE(alarmC.was_called());
alarmA.Reset();
ep.set_time(40 * 1000);
ep.CallAndReregisterAlarmEvents(); // C
EXPECT_FALSE(alarmA.was_called());
EXPECT_FALSE(alarmB.was_called());
EXPECT_TRUE(alarmC.was_called());
alarmC.Reset();
ep.CallAndReregisterAlarmEvents(); // None.
EXPECT_FALSE(alarmA.was_called());
EXPECT_FALSE(alarmB.was_called());
EXPECT_FALSE(alarmC.was_called());
}
TEST(SimpleEpollServerTest, TestAlarmOnShutdown) {
TestAlarm alarm1;
{
EpollTestServer ep;
const int64_t now = WallTimeNowInUsec();
ep.RegisterAlarm(now + 5000, &alarm1);
}
EXPECT_TRUE(alarm1.onshutdown_called());
}
// Tests that if we have multiple alarms
// OnShutdown then we handle them properly.
TEST(SimpleEpollServerTest, TestMultipleAlarmOnShutdown) {
TestAlarm alarm1;
TestAlarm alarm2;
TestAlarm alarm3;
{
EpollTestServer ep;
const int64_t now = WallTimeNowInUsec();
ep.RegisterAlarm(now + 5000, &alarm1);
ep.RegisterAlarm(now + 9000, &alarm2);
ep.RegisterAlarm(now + 9000, &alarm3);
}
EXPECT_TRUE(alarm1.onshutdown_called());
EXPECT_TRUE(alarm2.onshutdown_called());
EXPECT_TRUE(alarm3.onshutdown_called());
}
TEST(SimpleEpollServerTest, TestMultipleAlarmUnregistrationOnShutdown) {
TestParentAlarm tp;
TestChildAlarm alarm1;
TestChildAlarm alarm2;
alarm1.set_parent(&tp);
alarm2.set_parent(&tp);
{
EpollTestServer ep;
const int64_t now = WallTimeNowInUsec();
ep.RegisterAlarm(now + 5000, &alarm1);
ep.RegisterAlarm(now + 9000, &alarm2);
}
EXPECT_TRUE(alarm1.onshutdown_called());
EXPECT_FALSE(alarm2.onshutdown_called());
}
// Check an alarm set in the past runs right away.
TEST(SimpleEpollServerTest, TestPastAlarm) {
EpollTestServer ep;
TestAlarm alarm;
// Register the alarm and make sure we wait long enough to hit it.
ep.set_timeout_in_us(1000 * 2);
ep.RegisterAlarm(WallTimeNowInUsec() - 1000, &alarm);
EXPECT_EQ(1u, ep.GetNumPendingAlarmsForTest());
ep.WaitForEventsAndExecuteCallbacks();
EXPECT_TRUE(alarm.was_called());
EXPECT_EQ(0u, ep.GetNumPendingAlarmsForTest());
alarm.Reset();
}
// Test Unregistering of Alarms
TEST(SimpleEpollServerTest, TestUnregisterAlarm) {
EpollTestServer ep;
SimpleEpollServer::AlarmRegToken temptok;
TestAlarmUnregister alarm1;
TestAlarmUnregister alarm2;
ep.RegisterAlarm(WallTimeNowInUsec() + 5 * 1000, &alarm1);
ep.RegisterAlarm(WallTimeNowInUsec() + 13 * 1000, &alarm2);
// Unregister an alarm.
if (alarm2.get_token(&temptok)) {
ep.UnregisterAlarm(temptok);
}
EXPECT_EQ(1u, ep.GetNumPendingAlarmsForTest());
EXPECT_TRUE(alarm2.onunregistration_called());
if (alarm1.get_token(&temptok)) {
ep.UnregisterAlarm(temptok);
}
EXPECT_EQ(0u, ep.GetNumPendingAlarmsForTest());
EXPECT_TRUE(alarm1.onunregistration_called());
}
// Test Reregistering of Alarms
TEST(SimpleEpollServerTest, TestReregisterAlarm) {
EpollTestAlarms ep;
SimpleEpollServer::AlarmRegToken token;
TestAlarmUnregister alarm;
ep.set_time(1000);
ep.RegisterAlarm(5000, &alarm);
EXPECT_EQ(1u, ep.GetNumPendingAlarmsForTest());
ASSERT_TRUE(alarm.get_token(&token));
ep.ReregisterAlarm(token, 6000);
EXPECT_EQ(1u, ep.GetNumPendingAlarmsForTest());
ep.set_time(5000);
ep.set_timeout_in_us(0);
ep.CallAndReregisterAlarmEvents();
EXPECT_FALSE(alarm.was_called());
ep.set_time(6000);
ep.CallAndReregisterAlarmEvents();
EXPECT_TRUE(alarm.was_called());
}
TEST(SimpleEpollServerTest, TestReregisterDeferredAlarm) {
EpollTestAlarms ep;
ep.set_timeout_in_us(0);
TestAlarm alarm;
TestAlarmThatRegistersAndReregistersAnotherAlarm register_alarm;
// Register the alarm in the past so it is added as a deferred alarm.
register_alarm.SetRegisterAndReregisterAlarm(&alarm, -500, 500, &ep);
ep.set_time(1000);
ep.RegisterAlarm(1000, &register_alarm);
// Call reregister twice, first to run register_alarm and second to run any
// scheduled deferred alarms.
ep.CallAndReregisterAlarmEvents();
ep.CallAndReregisterAlarmEvents();
EXPECT_EQ(1u, ep.GetNumPendingAlarmsForTest());
EXPECT_FALSE(alarm.was_called());
ep.set_time(1500);
ep.CallAndReregisterAlarmEvents();
EXPECT_TRUE(alarm.was_called());
}
// Check if an alarm fired and got reregistered, you are able to
// unregister the second registration.
TEST(SimpleEpollServerTest, TestFiredReregisteredAlarm) {
EpollTestAlarms ep;
TestAlarmUnregister alarmA;
SimpleEpollServer::AlarmRegToken first_token;
SimpleEpollServer::AlarmRegToken second_token;
bool found;
ep.set_timeout_in_us(50 * 1000 * 2);
alarmA.set_time_before_next_alarm(1000 * 30);
alarmA.set_absolute_time(true);
// Alarm A first fires at 15, then 30
ep.RegisterAlarm(15 * 1000, &alarmA);
found = alarmA.get_token(&first_token);
EXPECT_TRUE(found);
ep.set_time(15 * 1000);
ep.CallAndReregisterAlarmEvents(); // A
EXPECT_TRUE(alarmA.was_called());
alarmA.Reset();
found = alarmA.get_token(&second_token);
EXPECT_TRUE(found);
if (found) {
ep.UnregisterAlarm(second_token);
}
ep.set_time(30 * 1000);
ep.CallAndReregisterAlarmEvents(); // A
alarmA.Reset();
}
// Here we make sure that one alarm can unregister another alarm
// in OnShutdown().
TEST(SimpleEpollServerTest, TestAlarmCanUnregistersAnotherAlarmOnShutdown) {
TestAlarmThatUnregistersAnotherAlarm alarm1;
TestAlarm alarm2;
{
EpollTestServer ep;
// Register two alarms and make alarm1 is placed in queue in front of alarm2
// so that when the queue is cleared, alarm1 is processed first.
const int64_t now = WallTimeNowInUsec();
ep.RegisterAlarm(now + 5000, &alarm1);
ep.RegisterAlarm(now + 9000, &alarm2);
alarm1.SetUnregisterAlarm(&alarm2, &ep);
EXPECT_EQ(2u, ep.GetNumPendingAlarmsForTest());
}
}
class TestAlarmRegisterAnotherAlarmShutdown : public TestAlarmUnregister {
public:
TestAlarmRegisterAnotherAlarmShutdown(EpollAlarmCallbackInterface* alarm2,
int64_t when)
: alarm2_(alarm2), when_(when) {}
void OnShutdown(SimpleEpollServer* eps) override {
TestAlarmUnregister::OnShutdown(eps);
eps->RegisterAlarm(when_, alarm2_);
}
private:
EpollAlarmCallbackInterface* alarm2_;
int64_t when_;
};
// This tests that alarm registers another alarm when shutting down.
// The two cases are: new alarm comes before and after the alarm being
// notified by OnShutdown()
TEST(SimpleEpollServerTest, AlarmRegistersAnotherAlarmOnShutdownBeforeSelf) {
TestAlarm alarm2;
int64_t alarm_time = WallTimeNowInUsec() + 5000;
TestAlarmRegisterAnotherAlarmShutdown alarm1(&alarm2, alarm_time - 1000);
{
EpollTestAlarms ep;
ep.RegisterAlarm(alarm_time, &alarm1);
}
EXPECT_TRUE(alarm1.onshutdown_called());
EXPECT_FALSE(alarm2.onshutdown_called());
}
TEST(SimpleEpollServerTest, AlarmRegistersAnotherAlarmOnShutdownAfterSelf) {
TestAlarm alarm2;
int64_t alarm_time = WallTimeNowInUsec() + 5000;
TestAlarmRegisterAnotherAlarmShutdown alarm1(&alarm2, alarm_time + 1000);
{
EpollTestAlarms ep;
ep.RegisterAlarm(alarm_time, &alarm1);
}
EXPECT_TRUE(alarm1.onshutdown_called());
EXPECT_TRUE(alarm2.onshutdown_called());
}
TEST(SimpleEpollServerTest, TestWrite) {
SimpleEpollServer ep;
ep.set_timeout_in_us(1);
char data[kPageSize] = {0};
int pipe_fds[2];
if (pipe(pipe_fds) < 0) {
EPOLL_PLOG(FATAL) << "pipe() failed";
}
int read_fd = pipe_fds[0];
int write_fd = pipe_fds[1];
RecordingCB recording_cb;
const Recorder* recorder = recording_cb.recorder();
const std::vector<RecordEntry> *records = recorder->records();
// Register to listen to write events.
ep.RegisterFD(write_fd, &recording_cb, EPOLLOUT | O_NONBLOCK);
// At this point the recorder should have the creation and registration
// events.
EXPECT_EQ(2u, records->size());
// Fill up the pipe.
int written = 1;
for (int i = 0; i < 17 && written > 0 ; ++i) {
written = write(write_fd, &data, kPageSize);
}
EXPECT_LT(written, 0);
// There should be no new events as the pipe is not available for writing.
ep.WaitForEventsAndExecuteCallbacks();
EXPECT_EQ(2u, records->size());
// Now read data from the pipe to make it writable again. This time the
// we should get an EPOLLOUT event.
int size = read(read_fd, &data, kPageSize);
EXPECT_EQ(kPageSize, size);
ep.WaitForEventsAndExecuteCallbacks();
EXPECT_EQ(3u, records->size());
// Now unsubscribe from writable events (which adds a modification record)
// and wait to verify that no event records are added.
ep.StopWrite(write_fd);
ep.WaitForEventsAndExecuteCallbacks();
EXPECT_EQ(4u, records->size());
// We had the right number of events all along. Make sure they were actually
// the right events.
Recorder tmp;
tmp.Record(&recording_cb, CREATION, 0, 0);
tmp.Record(&recording_cb, REGISTRATION, write_fd, EPOLLOUT | O_NONBLOCK);
tmp.Record(&recording_cb, EVENT, write_fd, EPOLLOUT);
tmp.Record(&recording_cb, MODIFICATION, write_fd, O_NONBLOCK);
EXPECT_TRUE(recorder->IsEqual(&tmp));
ep.UnregisterFD(write_fd);
close(read_fd);
close(write_fd);
}
TEST(SimpleEpollServerTest, TestReadWrite) {
SimpleEpollServer ep;
ep.set_timeout_in_us(1);
char data[kPageSize] = {0};
int pipe_fds[2];
if (pipe(pipe_fds) < 0) {
EPOLL_PLOG(FATAL) << "pipe() failed";
}
int read_fd = pipe_fds[0];
int write_fd = pipe_fds[1];
RecordingCB recording_cb;
const Recorder* recorder = recording_cb.recorder();
const std::vector<RecordEntry> *records = recorder->records();
// Register to listen to read and write events.
ep.RegisterFDForReadWrite(read_fd, &recording_cb);
// At this point the recorder should have the creation and registration
// events.
EXPECT_EQ(2u, records->size());
int written = write(write_fd, &data, kPageSize);
EXPECT_EQ(kPageSize, written);
ep.WaitForEventsAndExecuteCallbacks();
ep.UnregisterFD(read_fd);
close(read_fd);
close(write_fd);
}
TEST(SimpleEpollServerTest, TestMultipleFDs) {
SimpleEpollServer ep;
ep.set_timeout_in_us(1);
char data = 'x';
int pipe_one[2];
if (pipe(pipe_one) < 0) {
EPOLL_PLOG(FATAL) << "pipe() failed";
}
int pipe_two[2];
if (pipe(pipe_two) < 0) {
EPOLL_PLOG(FATAL) << "pipe() failed";
}
RecordingCB recording_cb_one;
const Recorder* recorder_one = recording_cb_one.recorder();
const std::vector<RecordEntry> *records_one = recorder_one->records();
RecordingCB recording_cb_two;
const Recorder* recorder_two = recording_cb_two.recorder();
const std::vector<RecordEntry> *records_two = recorder_two->records();
// Register to listen to read events for both pipes
ep.RegisterFDForRead(pipe_one[0], &recording_cb_one);
ep.RegisterFDForRead(pipe_two[0], &recording_cb_two);
EXPECT_EQ(2u, records_one->size());
EXPECT_EQ(2u, records_two->size());
EXPECT_EQ(1, write(pipe_one[1], &data, 1));
ep.WaitForEventsAndExecuteCallbacks();
EXPECT_EQ(3u, records_one->size());
EXPECT_EQ(2u, records_two->size());
EXPECT_EQ(1, write(pipe_two[1], &data, 1));
ep.WaitForEventsAndExecuteCallbacks();
EXPECT_EQ(3u, records_one->size());
EXPECT_EQ(3u, records_two->size());
EXPECT_EQ(1, write(pipe_one[1], &data, 1));
EXPECT_EQ(1, write(pipe_two[1], &data, 1));
ep.WaitForEventsAndExecuteCallbacks();
EXPECT_EQ(4u, records_one->size());
EXPECT_EQ(4u, records_two->size());
ep.WaitForEventsAndExecuteCallbacks();
ep.UnregisterFD(pipe_one[0]);
ep.UnregisterFD(pipe_two[0]);
close(pipe_one[0]);
close(pipe_one[1]);
close(pipe_two[0]);
close(pipe_two[1]);
}
// Check that the SimpleEpollServer calls OnShutdown for any registered FDs.
TEST(SimpleEpollServerTest, TestFDOnShutdown) {
int pipe_fds[2];
if (pipe(pipe_fds) < 0) {
EPOLL_PLOG(FATAL) << "pipe() failed";
}
int read_fd = pipe_fds[0];
int write_fd = pipe_fds[1];
RecordingCB recording_cb1;
RecordingCB recording_cb2;
const Recorder* recorder1 = recording_cb1.recorder();
const Recorder* recorder2 = recording_cb2.recorder();
{
SimpleEpollServer ep;
ep.set_timeout_in_us(1);
// Register to listen to write events.
ep.RegisterFD(write_fd, &recording_cb1, EPOLLOUT | O_NONBLOCK);
ep.RegisterFD(read_fd, &recording_cb2, EPOLLIN | O_NONBLOCK);
}
// Make sure OnShutdown was called for both callbacks.
Recorder write_recorder;
write_recorder.Record(&recording_cb1, CREATION, 0, 0);
write_recorder.Record(
&recording_cb1, REGISTRATION, write_fd, EPOLLOUT | O_NONBLOCK);
write_recorder.Record(&recording_cb1, UNREGISTRATION, write_fd, false);
write_recorder.Record(&recording_cb1, SHUTDOWN, write_fd, 0);
EXPECT_TRUE(recorder1->IsEqual(&write_recorder));
Recorder read_recorder;
read_recorder.Record(&recording_cb2, CREATION, 0, 0);
read_recorder.Record(
&recording_cb2, REGISTRATION, read_fd, EPOLLIN | O_NONBLOCK);
read_recorder.Record(&recording_cb2, UNREGISTRATION, read_fd, false);
read_recorder.Record(&recording_cb2, SHUTDOWN, read_fd, 0);
EXPECT_TRUE(recorder2->IsEqual(&read_recorder));
close(read_fd);
close(write_fd);
}
class UnregisterCB : public EpollCallbackInterface {
public:
explicit UnregisterCB(int fd)
: eps_(nullptr), fd_(fd), onshutdown_called_(false) {
}
~UnregisterCB() override {
}
void OnShutdown(SimpleEpollServer* eps, int fd) override {
eps_->UnregisterFD(fd_);
eps_->UnregisterFD(fd);
onshutdown_called_ = true;
eps_ = nullptr;
}
void set_epollserver(SimpleEpollServer* eps) { eps_ = eps; }
bool onshutdown_called() { return onshutdown_called_; }
void OnRegistration(SimpleEpollServer* eps, int fd, int event_mask) override {
}
void OnModification(int fd, int event_mask) override {}
void OnEvent(int fd, EpollEvent* event) override {}
void OnUnregistration(int fd, bool replaced) override {}
std::string Name() const override { return "UnregisterCB"; }
protected:
SimpleEpollServer* eps_;
int fd_;
bool onshutdown_called_;
};
// Check that unregistering fds in OnShutdown works cleanly.
TEST(SimpleEpollServerTest, TestUnregisteringFDsOnShutdown) {
int pipe_fds[2];
if (pipe(pipe_fds) < 0) {
EPOLL_PLOG(FATAL) << "pipe() failed";
}
int read_fd = pipe_fds[0];
int write_fd = pipe_fds[1];
UnregisterCB unreg_cb1(read_fd);
UnregisterCB unreg_cb2(write_fd);
{
SimpleEpollServer ep;
ep.set_timeout_in_us(1);
unreg_cb1.set_epollserver(&ep);
unreg_cb2.set_epollserver(&ep);
// Register to listen to write events.
ep.RegisterFD(write_fd, &unreg_cb1, EPOLLOUT | O_NONBLOCK);
ep.RegisterFD(read_fd, &unreg_cb2, EPOLLIN | O_NONBLOCK);
}
// Make sure at least one onshutdown was called.
EXPECT_TRUE(unreg_cb1.onshutdown_called() ||
unreg_cb2.onshutdown_called());
// Make sure that both onshutdowns were not called.
EXPECT_TRUE(!(unreg_cb1.onshutdown_called() &&
unreg_cb2.onshutdown_called()));
close(read_fd);
close(write_fd);
}
TEST(SimpleEpollServerTest, TestFDsAndAlarms) {
SimpleEpollServer ep;
ep.set_timeout_in_us(5);
char data = 'x';
int pipe_fds[2];
if (pipe(pipe_fds) < 0) {
EPOLL_PLOG(FATAL) << "pipe() failed";
}
RecordingCB recording_cb;
const Recorder* recorder = recording_cb.recorder();
const std::vector<RecordEntry> *records = recorder->records();
TestAlarm alarm;
ep.RegisterFDForRead(pipe_fds[0], &recording_cb);
EXPECT_EQ(2u, records->size());
EXPECT_FALSE(alarm.was_called());
// Write to the pipe and set a longish alarm so we get a read event followed
// by an alarm event.
int written = write(pipe_fds[1], &data, 1);
EXPECT_EQ(1, written);
ep.WaitForEventsAndExecuteCallbacks();
EXPECT_EQ(3u, records->size());
EXPECT_FALSE(alarm.was_called());
ep.RegisterAlarm(WallTimeNowInUsec() + 1000, &alarm);
WaitForAlarm(&ep, alarm);
EXPECT_EQ(3u, records->size());
EXPECT_TRUE(alarm.was_called());
alarm.Reset();
// Now set a short alarm so the alarm and the read event are called together.
ep.RegisterAlarm(WallTimeNowInUsec(), &alarm);
written = write(pipe_fds[1], &data, 1);
EXPECT_EQ(1, written);
ep.WaitForEventsAndExecuteCallbacks();
EXPECT_TRUE(alarm.was_called());
EXPECT_EQ(4u, records->size());
ep.UnregisterFD(pipe_fds[0]);
close(pipe_fds[0]);
close(pipe_fds[1]);
}
class EpollReader: public EpollCallbackInterface {
public:
explicit EpollReader(int len)
: len_(0),
expected_len_(len),
done_reading_(false) {
memset(&buf_, 0, kMaxBufLen);
}
~EpollReader() override {}
void OnRegistration(SimpleEpollServer* eps, int fd, int event_mask) override {
}
void OnModification(int fd, int event_mask) override {}
void OnEvent(int fd, EpollEvent* event) override {
if (event->in_events & EPOLLIN) {
len_ += read(fd, &buf_ + len_, kMaxBufLen - len_);
}
// If we have finished reading...
if (event->in_events & EPOLLHUP) {
CHECK_EQ(len_, expected_len_);
done_reading_ = true;
}
}
void OnUnregistration(int fd, bool replaced) override {}
void OnShutdown(SimpleEpollServer* eps, int fd) override {
// None of the current tests involve having active callbacks when the
// server shuts down.
EPOLL_LOG(FATAL);
}
std::string Name() const override { return "EpollReader"; }
// Returns true if the data in buf is the same as buf_, false otherwise.
bool CheckOutput(char* buf, int len) {
if (len != len_) {
return false;
}
return !memcmp(buf, buf_, len);
}
bool done_reading() { return done_reading_; }
protected:
int len_;
int expected_len_;
char buf_[kMaxBufLen];
bool done_reading_;
};
void TestPipe(char *test_message, int len) {
int pipe_fds[2];
if (pipe(pipe_fds) < 0) {
EPOLL_PLOG(FATAL) << "pipe failed()";
}
int reader_pipe = pipe_fds[0];
int writer_pipe = pipe_fds[1];
int child_pid;
memset(test_message, 'x', len);
switch (child_pid = fork()) {
case 0: { // Child will send message.
const char *message = test_message;
int size;
close(reader_pipe);
while ((size = write(writer_pipe, message, len)) > 0) {
message += size;
len -= size;
if (len == 0) {
break;
}
}
if (len > 0) {
EPOLL_PLOG(FATAL) << "write() failed";
}
close(writer_pipe);
_exit(0);
}
case -1:
EPOLL_PLOG(FATAL) << "fork() failed";
break;
default: { // Parent will receive message.
close(writer_pipe);
auto ep = EpollMakeUnique<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 = EpollMakeUnique<EpollReader>(len);
// Check that registering a FD for read alerts us when there is data to be
// read.
ep.RegisterFDForRead(read_fd, reader.get());
char data = 'a';
int size = write(write_fd, &data, 1);
EXPECT_EQ(1, size);
ep.WaitForEventsAndExecuteCallbacks();
EXPECT_TRUE(reader->CheckOutput(&data, len));
// Remove the callback for read events, write to the pipe and make sure that
// we did not read more data.
ep.StopRead(read_fd);
size = write(write_fd, &data, len);
EXPECT_EQ(1, size);
// The wait will return after timeout.
ep.WaitForEventsAndExecuteCallbacks();
EXPECT_TRUE(reader->CheckOutput(&data, len));
ep.UnregisterFD(read_fd);
close(read_fd);
close(write_fd);
}
class EdgeTriggerCB : public EpollCallbackInterface {
public:
EdgeTriggerCB(int read_size, int write_size, char write_char, char peer_char)
: eps_(nullptr),
read_buf_(read_size),
write_buf_(write_size, write_char),
peer_char_(peer_char) {
Reset();
}
~EdgeTriggerCB() override {}
void Reset() {
CHECK(eps_ == nullptr);
bytes_read_ = 0;
bytes_written_ = 0;
can_read_ = false;
will_read_ = false;
can_write_ = false;
will_write_ = false;
read_closed_ = false;
write_closed_ = false;
}
void ResetByteCounts() {
bytes_read_ = bytes_written_ = 0;
}
void set_will_read(bool will_read) { will_read_ = will_read; }
void set_will_write(bool will_write) { will_write_ = will_write; }
bool can_write() const { return can_write_; }
int bytes_read() const { return bytes_read_; }
int bytes_written() const { return bytes_written_; }
void OnRegistration(SimpleEpollServer* eps, int fd, int event_mask) override {
EXPECT_TRUE(eps_ == nullptr);
eps_ = eps;
Initialize(fd, event_mask);
}
void OnModification(int fd, int event_mask) override {
EXPECT_TRUE(eps_ != nullptr);
if (event_mask & EPOLLET) {
Initialize(fd, event_mask);
} else {
eps_->SetFDNotReady(fd);
}
}
void OnEvent(int fd, EpollEvent* event) override {
const int event_mask = event->in_events;
if (event_mask & (EPOLLHUP | EPOLLERR)) {
write_closed_ = true;
return;
}
if (will_read_ && event_mask & EPOLLIN) {
EXPECT_FALSE(read_closed_);
int read_size = read_buf_.size();
memset(&read_buf_[0], 0, read_size);
int len = recv(fd, &read_buf_[0], read_size, MSG_DONTWAIT);
// Update the readiness states
can_read_ = (len == read_size);
if (len > 0) {
bytes_read_ += len;
EPOLL_VLOG(1) << "fd: " << fd << ", read " << len
<< ", total: " << bytes_read_;
// Now check the bytes read
EXPECT_TRUE(CheckReadBuffer(len));
} else if (len < 0) {
EPOLL_VLOG(1) << "fd: " << fd << " read hit EAGAIN";
EXPECT_EQ(EAGAIN, errno) << strerror(errno);
can_read_ = false;
} else {
read_closed_ = true;
}
}
if (will_write_ && event_mask & EPOLLOUT) {
// Write side close/full close can only detected by EPOLLHUP, which is
// caused by EPIPE.
EXPECT_FALSE(write_closed_);
int write_size = write_buf_.size();
int len = send(fd, &write_buf_[0], write_size, MSG_DONTWAIT);
can_write_ = (len == write_size);
if (len > 0) {
bytes_written_ += len;
EPOLL_VLOG(1) << "fd: " << fd << ", write " << len
<< ", total: " << bytes_written_;
} else {
EPOLL_VLOG(1) << "fd: " << fd << " write hit EAGAIN";
EXPECT_EQ(EAGAIN, errno) << strerror(errno);
can_write_ = false;
}
}
// Since we can only get on the ready list once, wait till we confirm both
// read and write side continuation state and set the correct event mask
// for the ready list.
event->out_ready_mask = can_read_ ? static_cast<int>(EPOLLIN) : 0;
if (can_write_) {
event->out_ready_mask |= EPOLLOUT;
}
}
void OnUnregistration(int fd, bool replaced) override {
EXPECT_TRUE(eps_ != nullptr);
eps_ = nullptr;
}
void OnShutdown(SimpleEpollServer* eps, int fd) override {
// None of the current tests involve having active callbacks when the
// server shuts down.
EPOLL_LOG(FATAL);
}
std::string Name() const override { return "EdgeTriggerCB"; }
private:
SimpleEpollServer* eps_;
std::vector<char> read_buf_;
int bytes_read_;
std::vector<char> write_buf_;
int bytes_written_;
char peer_char_; // The char we expected to read.
bool can_read_;
bool will_read_;
bool can_write_;
bool will_write_;
bool read_closed_;
bool write_closed_;
void Initialize(int fd, int event_mask) {
CHECK(eps_);
can_read_ = can_write_ = false;
if (event_mask & EPOLLET) {
int events = 0;
if (event_mask & EPOLLIN) {
events |= EPOLLIN;
can_read_ = true;
}
if (event_mask & EPOLLOUT) {
events |= EPOLLOUT;
can_write_ = true;
}
eps_->SetFDReady(fd, events);
}
}
bool CheckReadBuffer(int len) const {
for (int i = 0; i < len; ++i) {
if (peer_char_ != read_buf_[i]) {
return false;
}
}
return true;
}
};
// Test adding and removing from the ready list.
TEST(SimpleEpollServerTest, TestReadyList) {
SimpleEpollServer ep;
int pipe_fds[2];
if (pipe(pipe_fds) < 0) {
EPOLL_PLOG(FATAL) << "pipe() failed";
}
// Just use any CB will do, since we never wait on epoll events.
EdgeTriggerCB reader1(0, 0, 0, 0);
EdgeTriggerCB reader2(0, 0, 0, 0);
ep.RegisterFD(pipe_fds[0], &reader1, EPOLLIN);
ep.RegisterFD(pipe_fds[1], &reader2, EPOLLOUT);
// Adding fds that are registered with eps
EXPECT_FALSE(ep.IsFDReady(pipe_fds[0]));
EXPECT_FALSE(ep.IsFDReady(pipe_fds[1]));
ep.SetFDReady(pipe_fds[0], EPOLLIN);
EXPECT_TRUE(ep.IsFDReady(pipe_fds[0]));
EXPECT_FALSE(ep.IsFDReady(pipe_fds[1]));
EXPECT_EQ(1u, ep.ReadyListSize());
ep.SetFDReady(pipe_fds[1], EPOLLOUT);
EXPECT_TRUE(ep.IsFDReady(pipe_fds[0]));
EXPECT_TRUE(ep.IsFDReady(pipe_fds[1]));
EXPECT_EQ(2u, ep.ReadyListSize());
// Now check that SetFDNotReady doesn't affect other fds
ep.SetFDNotReady(pipe_fds[0]);
EXPECT_FALSE(ep.IsFDReady(pipe_fds[0]));
EXPECT_TRUE(ep.IsFDReady(pipe_fds[1]));
EXPECT_EQ(1u, ep.ReadyListSize());
ep.UnregisterFD(pipe_fds[0]);
ep.UnregisterFD(pipe_fds[1]);
EXPECT_EQ(0u, ep.ReadyListSize());
// Now try adding them when they are not registered, and it shouldn't work.
ep.SetFDReady(pipe_fds[0], EPOLLIN);
EXPECT_FALSE(ep.IsFDReady(pipe_fds[0]));
EXPECT_EQ(0u, ep.ReadyListSize());
close(pipe_fds[0]);
close(pipe_fds[1]);
}
class EPSWaitThread : public EpollThread {
public:
explicit EPSWaitThread(SimpleEpollServer* eps)
: EpollThread("EPSWait"), eps_(eps), done_(false) {}
void Run() override {
eps_->WaitForEventsAndExecuteCallbacks();
}
bool done() { return done_; }
private:
SimpleEpollServer* eps_;
bool done_;
};
TEST(EpollServerTest, TestWake) {
SimpleEpollServer eps;
eps.set_timeout_in_us(-1);
EPSWaitThread eps_thread(&eps);
eps_thread.Start();
EXPECT_FALSE(eps_thread.done());
eps.Wake();
eps_thread.Join();
}
class UnRegisterWhileProcessingCB: public EpollCallbackInterface {
public:
explicit UnRegisterWhileProcessingCB(int fd) : eps_(nullptr), fd_(fd) {}
~UnRegisterWhileProcessingCB() override {
}
void OnShutdown(SimpleEpollServer* eps, int fd) override {}
void set_epoll_server(SimpleEpollServer* eps) { eps_ = eps; }
void OnRegistration(SimpleEpollServer* eps, int fd, int event_mask) override {
}
void OnModification(int fd, int event_mask) override {}
void OnEvent(int fd, EpollEvent* event) override {
// This should cause no problems.
eps_->UnregisterFD(fd_);
}
void OnUnregistration(int fd, bool replaced) override {}
std::string Name() const override { return "UnRegisterWhileProcessingCB"; }
protected:
SimpleEpollServer* eps_;
int fd_;
};
class RegisterWhileProcessingCB: public EpollCallbackInterface {
public:
RegisterWhileProcessingCB(int fd, EpollCallbackInterface* cb)
: eps_(nullptr), fd_(fd), cb_(cb) {}
~RegisterWhileProcessingCB() override {
}
void OnShutdown(SimpleEpollServer* eps, int fd) override {}
void set_epoll_server(SimpleEpollServer* eps) { eps_ = eps; }
void OnRegistration(SimpleEpollServer* eps, int fd, int event_mask) override {
}
void OnModification(int fd, int event_mask) override {}
void OnEvent(int fd, EpollEvent* event) override {
// This should cause no problems.
eps_->RegisterFDForReadWrite(fd_, cb_);
}
void OnUnregistration(int fd, bool replaced) override {}
std::string Name() const override { return "RegisterWhileProcessingCB"; }
protected:
SimpleEpollServer* eps_;
int fd_;
EpollCallbackInterface* cb_;
};
// Nothing bad should happen when we do this. We're -only-
// testing that nothing bad occurs in this test.
TEST(SimpleEpollServerTest, NothingBadWhenUnRegisteringFDWhileProcessingIt) {
UnRegisterWhileProcessingCB cb(0);
{
FakeSimpleEpollServer epoll_server;
cb.set_epoll_server(&epoll_server);
epoll_server.RegisterFDForReadWrite(0, &cb);
epoll_event ee;
ee.data.fd = 0;
epoll_server.AddEvent(0, ee);
epoll_server.AdvanceBy(1);
epoll_server.WaitForEventsAndExecuteCallbacks();
}
}
//
// testing that nothing bad occurs in this test.
TEST(SimpleEpollServerTest,
NoEventsDeliveredForFdsOfUnregisteredCBsWithReRegdFD) {
// events: fd0, fd1, fd2
// fd0 -> unreg fd2
// fd1 -> reg fd2
// fd2 -> no event should be seen
RecordingCB recorder_cb;
UnRegisterWhileProcessingCB unreg_cb(-3);
RegisterWhileProcessingCB reg_other_cb(-3, &recorder_cb);
{
FakeSimpleEpollServer epoll_server;
unreg_cb.set_epoll_server(&epoll_server);
reg_other_cb.set_epoll_server(&epoll_server);
epoll_server.RegisterFDForReadWrite(-1, &unreg_cb);
epoll_server.RegisterFDForReadWrite(-2, &reg_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 {}
std::string Name() const override { return "ReRegWhileReadyListOnEvent"; }
protected:
SimpleEpollServer* eps_;
};
// Nothing bad should happen when we do this. We're -only-
// testing that nothing bad occurs in this test.
TEST(SimpleEpollServerTest,
NothingBadWhenReRegisteringFDWhileProcessingFromReadyList) {
ReRegWhileReadyListOnEvent cb(0);
{
FakeSimpleEpollServer epoll_server;
cb.set_epoll_server(&epoll_server);
epoll_server.RegisterFDForReadWrite(0, &cb);
epoll_event ee;
ee.data.fd = 0;
epoll_server.AddEvent(0, ee);
epoll_server.AdvanceBy(1);
epoll_server.WaitForEventsAndExecuteCallbacks();
}
}
class UnRegEverythingReadyListOnEvent: public EpollCallbackInterface {
public:
UnRegEverythingReadyListOnEvent() : eps_(nullptr), fd_(0), fd_range_(0) {}
void set_fd(int fd) { fd_ = fd; }
void set_fd_range(int fd_range) { fd_range_ = fd_range; }
void set_num_called(int* num_called) { num_called_ = num_called; }
void OnShutdown(SimpleEpollServer* eps, int fd) override {}
void set_epoll_server(SimpleEpollServer* eps) { eps_ = eps; }
void OnRegistration(SimpleEpollServer* eps, int fd, int event_mask) override {
eps->SetFDReady(fd, EPOLLIN);
}
void OnModification(int fd, int event_mask) override {}
void OnEvent(int fd, EpollEvent* event) override {
// This should cause no problems.
CHECK(num_called_ != nullptr);
++(*num_called_);
// Note that we're iterating from -fd_range + 1 -> 0.
// We do this because there is an FD installed into the
// epollserver somewhere in the low numbers.
// Using negative FD numbers (which are guaranteed to not
// exist in the epoll-server) ensures that we will not
// come in conflict with the preexisting FD.
for (int i = -fd_range_ + 1; i <= 0; ++i) {
eps_->UnregisterFD(i);
}
}
void OnUnregistration(int fd, bool replaced) override {}
std::string Name() const override {
return "UnRegEverythingReadyListOnEvent";
}
protected:
SimpleEpollServer* eps_;
int fd_;
int fd_range_;
int* num_called_;
};
TEST(SimpleEpollServerTest,
NothingBadWhenUnRegisteredWhileProcessingFromReadyList) {
const size_t kNumCallbacks = 32u;
UnRegEverythingReadyListOnEvent callbacks[kNumCallbacks];
int num_called = 0;
{
FakeSimpleEpollServer epoll_server;
for (size_t i = 0; i < kNumCallbacks; ++i) {
callbacks[i].set_fd(-i);
callbacks[i].set_fd_range(kNumCallbacks);
callbacks[i].set_num_called(&num_called);
callbacks[i].set_epoll_server(&epoll_server);
epoll_server.RegisterFDForReadWrite(0, &callbacks[i]);
epoll_event ee;
ee.data.fd = -i;
epoll_server.AddEvent(0, ee);
}
epoll_server.AdvanceBy(1);
epoll_server.WaitForEventsAndExecuteCallbacks();
epoll_server.WaitForEventsAndExecuteCallbacks();
}
EXPECT_EQ(1, num_called);
}
TEST(SimpleEpollServerTest, TestThatVerifyReadyListWorksWithNothingInList) {
FakeSimpleEpollServer epoll_server;
epoll_server.VerifyReadyList();
}
TEST(SimpleEpollServerTest, TestThatVerifyReadyListWorksWithStuffInLists) {
FakeSimpleEpollServer epoll_server;
epoll_server.VerifyReadyList();
}
TEST(SimpleEpollServerTest,
ApproximateNowInUsAccurateOutideOfWaitForEventsAndExecuteCallbacks) {
FakeSimpleEpollServer epoll_server;
epoll_server.AdvanceBy(1232);
EXPECT_EQ(epoll_server.ApproximateNowInUsec(), epoll_server.NowInUsec());
epoll_server.AdvanceBy(1111);
EXPECT_EQ(epoll_server.ApproximateNowInUsec(), epoll_server.NowInUsec());
}
class ApproximateNowInUsecTestCB: public EpollCallbackInterface {
public:
ApproximateNowInUsecTestCB() : feps_(nullptr), called_(false) {}
void OnRegistration(SimpleEpollServer* eps, int fd, int event_mask) override {
}
void OnModification(int fd, int event_mask) override {}
void OnEvent(int fd, EpollEvent* event) override {
EXPECT_EQ(feps_->ApproximateNowInUsec(), feps_->NowInUsec());
feps_->AdvanceBy(1111);
EXPECT_EQ(1 * 1111 + feps_->ApproximateNowInUsec(), feps_->NowInUsec());
feps_->AdvanceBy(1111);
EXPECT_EQ(2 * 1111 + feps_->ApproximateNowInUsec(), feps_->NowInUsec());
called_ = true;
}
void OnUnregistration(int fd, bool replaced) override {}
void OnShutdown(SimpleEpollServer* eps, int fd) override {}
std::string Name() const override { return "ApproximateNowInUsecTestCB"; }
void set_fakeepollserver(FakeSimpleEpollServer* feps) { feps_ = feps; }
bool called() const { return called_; }
protected:
FakeSimpleEpollServer* feps_;
bool called_;
};
TEST(SimpleEpollServerTest,
ApproximateNowInUsApproximateInsideOfWaitForEventsAndExecuteCallbacks) {
int dummy_fd = 11111;
ApproximateNowInUsecTestCB aniutcb;
{
FakeSimpleEpollServer epoll_server;
aniutcb.set_fakeepollserver(&epoll_server);
epoll_server.RegisterFD(dummy_fd, &aniutcb, EPOLLIN);
epoll_event ee;
ee.data.fd = dummy_fd;
ee.events = EPOLLIN;
epoll_server.AddEvent(10242, ee);
epoll_server.set_timeout_in_us(-1);
epoll_server.AdvanceByAndWaitForEventsAndExecuteCallbacks(20000);
EXPECT_TRUE(aniutcb.called());
}
}
// A mock epoll server that also simulates kernel delay in scheduling epoll
// events.
class FakeEpollServerWithDelay : public FakeSimpleEpollServer {
public:
FakeEpollServerWithDelay() : FakeSimpleEpollServer(), delay(0) {}
int delay;
protected:
int epoll_wait_impl(int epfd, struct epoll_event* events, int max_events,
int timeout_in_ms) override {
int out = FakeSimpleEpollServer::epoll_wait_impl(epfd, events, max_events,
timeout_in_ms);
AdvanceBy(delay);
return out;
}
};
// A callback that records the epoll event's delay.
class RecordDelayOnEvent: public EpollCallbackInterface {
public:
RecordDelayOnEvent() : last_delay(-1), eps_(nullptr) {}
~RecordDelayOnEvent() override {
}
void OnShutdown(SimpleEpollServer* eps, int fd) override {}
std::string Name() const override { return "RecordDelayOnEvent"; }
void set_epoll_server(SimpleEpollServer* eps) { eps_ = eps; }
void OnRegistration(SimpleEpollServer* eps, int fd, int event_mask) override {
}
void OnModification(int fd, int event_mask) override {}
void OnEvent(int fd, EpollEvent* event) override {
last_delay = eps_->LastDelayInUsec();
}
void OnUnregistration(int fd, bool replaced) override {}
int64_t last_delay;
protected:
SimpleEpollServer* eps_;
};
// Tests that an epoll callback sees the correct delay for its event when it
// calls LastDelayInUsec().
TEST(EpollServerTest, TestLastDelay) {
RecordDelayOnEvent cb;
FakeEpollServerWithDelay epoll_server;
cb.set_epoll_server(&epoll_server);
epoll_server.RegisterFDForReadWrite(0, &cb);
epoll_event ee;
ee.data.fd = 0;
// Inject delay, and confirm that it's reported.
epoll_server.set_timeout_in_us(5000);
epoll_server.delay = 6000;
epoll_server.AddEvent(0, ee);
epoll_server.AdvanceBy(1);
epoll_server.WaitForEventsAndExecuteCallbacks();
EXPECT_EQ(cb.last_delay, 1000);
// Fire an event before the timeout ends, and confirm that reported delay
// isn't negative.
epoll_server.set_timeout_in_us(5000);
epoll_server.delay = 0;
epoll_server.AddEvent(0, ee);
epoll_server.AdvanceBy(1);
epoll_server.WaitForEventsAndExecuteCallbacks();
EXPECT_EQ(cb.last_delay, 0);
// Wait forever until an event fires, and confirm there's no reported delay.
epoll_server.set_timeout_in_us(-1);
epoll_server.delay = 6000;
epoll_server.AddEvent(0, ee);
epoll_server.AdvanceBy(1);
epoll_server.WaitForEventsAndExecuteCallbacks();
EXPECT_EQ(cb.last_delay, 0);
}
TEST(SimpleEpollServerAlarmTest, TestShutdown) {
std::unique_ptr<SimpleEpollServer> eps(new SimpleEpollServer);
EpollAlarm alarm1;
EpollAlarm alarm2;
eps->RegisterAlarmApproximateDelta(10000000, &alarm1);
eps->RegisterAlarmApproximateDelta(10000000, &alarm2);
alarm2.UnregisterIfRegistered();
EXPECT_FALSE(alarm2.registered());
eps = nullptr;
EXPECT_FALSE(alarm1.registered());
}
TEST(SimpleEpollServerAlarmTest, TestUnregister) {
SimpleEpollServer eps;
EpollAlarm alarm;
eps.RegisterAlarmApproximateDelta(10000000, &alarm);
EXPECT_TRUE(alarm.registered());
alarm.UnregisterIfRegistered();
EXPECT_FALSE(alarm.registered());
alarm.UnregisterIfRegistered();
EXPECT_FALSE(alarm.registered());
}
TEST(SimpleEpollServerAlarmTest, TestUnregisterOnDestruction) {
EpollTestServer eps;
std::unique_ptr<EpollAlarm> alarm(new EpollAlarm());
EpollAlarm* alarm_ptr = alarm.get();
eps.RegisterAlarmApproximateDelta(10000000, alarm.get());
EXPECT_TRUE(eps.ContainsAlarm(alarm_ptr));
alarm = nullptr;
EXPECT_EQ(0u, eps.GetNumPendingAlarmsForTest());
}
TEST(SimpleEpollServerAlarmTest, TestUnregisterOnAlarm) {
EpollTestServer eps;
EpollAlarm alarm;
eps.RegisterAlarmApproximateDelta(1, &alarm);
EXPECT_TRUE(eps.ContainsAlarm(&alarm));
while (alarm.registered()) {
eps.WaitForEventsAndExecuteCallbacks();
}
EXPECT_FALSE(eps.ContainsAlarm(&alarm));
}
TEST(SimpleEpollServerAlarmTest, TestReregisterAlarm) {
EpollTestAlarms ep;
EpollAlarm alarm;
ep.set_time(1000);
ep.RegisterAlarm(5000, &alarm);
EXPECT_EQ(1u, ep.GetNumPendingAlarmsForTest());
alarm.ReregisterAlarm(6000);
EXPECT_EQ(1u, ep.GetNumPendingAlarmsForTest());
ep.set_time(5000);
ep.set_timeout_in_us(0);
ep.CallAndReregisterAlarmEvents();
EXPECT_EQ(1u, ep.GetNumPendingAlarmsForTest());
ep.set_time(6000);
ep.CallAndReregisterAlarmEvents();
EXPECT_EQ(0u, ep.GetNumPendingAlarmsForTest());
}
TEST(SimpleEpollServerAlarmTest, TestThatSameAlarmCanNotBeRegisteredTwice) {
TestAlarm alarm;
SimpleEpollServer epoll_server;
epoll_server.RegisterAlarm(1, &alarm);
EXPECT_EPOLL_BUG(epoll_server.RegisterAlarm(1, &alarm),
"Alarm already exists");
}
} // namespace
} // namespace test
} // namespace epoll_server