| // 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 "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 "epoll_server/fake_simple_epoll_server.h" |
| #include "epoll_server/platform/api/epoll_address_test_utils.h" |
| #include "epoll_server/platform/api/epoll_expect_bug.h" |
| #include "epoll_server/platform/api/epoll_ptr_util.h" |
| #include "epoll_server/platform/api/epoll_test.h" |
| #include "epoll_server/platform/api/epoll_thread.h" |
| #include "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, ®ister_alarm); |
| // Call reregister twice, first to run register_alarm and second to run any |
| // scheduled deferred alarms. |
| ep.CallAndReregisterAlarmEvents(); |
| ep.CallAndReregisterAlarmEvents(); |
| |
| EXPECT_EQ(1u, ep.GetNumPendingAlarmsForTest()); |
| EXPECT_FALSE(alarm.was_called()); |
| |
| ep.set_time(1500); |
| ep.CallAndReregisterAlarmEvents(); |
| EXPECT_TRUE(alarm.was_called()); |
| } |
| |
| // Check if an alarm fired and got reregistered, you are able to |
| // unregister the second registration. |
| TEST(SimpleEpollServerTest, TestFiredReregisteredAlarm) { |
| EpollTestAlarms ep; |
| TestAlarmUnregister alarmA; |
| |
| SimpleEpollServer::AlarmRegToken first_token; |
| SimpleEpollServer::AlarmRegToken second_token; |
| bool found; |
| |
| ep.set_timeout_in_us(50 * 1000 * 2); |
| alarmA.set_time_before_next_alarm(1000 * 30); |
| alarmA.set_absolute_time(true); |
| |
| // Alarm A first fires at 15, then 30 |
| ep.RegisterAlarm(15 * 1000, &alarmA); |
| |
| found = alarmA.get_token(&first_token); |
| EXPECT_TRUE(found); |
| |
| ep.set_time(15 * 1000); |
| ep.CallAndReregisterAlarmEvents(); // A |
| EXPECT_TRUE(alarmA.was_called()); |
| |
| alarmA.Reset(); |
| |
| found = alarmA.get_token(&second_token); |
| EXPECT_TRUE(found); |
| if (found) { |
| ep.UnregisterAlarm(second_token); |
| } |
| |
| ep.set_time(30 * 1000); |
| ep.CallAndReregisterAlarmEvents(); // A |
| |
| alarmA.Reset(); |
| } |
| |
| // Here we make sure that one alarm can unregister another alarm |
| // in OnShutdown(). |
| TEST(SimpleEpollServerTest, TestAlarmCanUnregistersAnotherAlarmOnShutdown) { |
| TestAlarmThatUnregistersAnotherAlarm alarm1; |
| TestAlarm alarm2; |
| { |
| EpollTestServer ep; |
| // Register two alarms and make alarm1 is placed in queue in front of alarm2 |
| // so that when the queue is cleared, alarm1 is processed first. |
| const int64_t now = WallTimeNowInUsec(); |
| ep.RegisterAlarm(now + 5000, &alarm1); |
| ep.RegisterAlarm(now + 9000, &alarm2); |
| alarm1.SetUnregisterAlarm(&alarm2, &ep); |
| EXPECT_EQ(2u, ep.GetNumPendingAlarmsForTest()); |
| } |
| } |
| |
| class TestAlarmRegisterAnotherAlarmShutdown : public TestAlarmUnregister { |
| public: |
| TestAlarmRegisterAnotherAlarmShutdown(EpollAlarmCallbackInterface* alarm2, |
| int64_t when) |
| : alarm2_(alarm2), when_(when) {} |
| void OnShutdown(SimpleEpollServer* eps) override { |
| TestAlarmUnregister::OnShutdown(eps); |
| eps->RegisterAlarm(when_, alarm2_); |
| } |
| |
| private: |
| EpollAlarmCallbackInterface* alarm2_; |
| int64_t when_; |
| }; |
| |
| // This tests that alarm registers another alarm when shutting down. |
| // The two cases are: new alarm comes before and after the alarm being |
| // notified by OnShutdown() |
| TEST(SimpleEpollServerTest, AlarmRegistersAnotherAlarmOnShutdownBeforeSelf) { |
| TestAlarm alarm2; |
| int64_t alarm_time = WallTimeNowInUsec() + 5000; |
| TestAlarmRegisterAnotherAlarmShutdown alarm1(&alarm2, alarm_time - 1000); |
| { |
| EpollTestAlarms ep; |
| ep.RegisterAlarm(alarm_time, &alarm1); |
| } |
| EXPECT_TRUE(alarm1.onshutdown_called()); |
| EXPECT_FALSE(alarm2.onshutdown_called()); |
| } |
| |
| TEST(SimpleEpollServerTest, AlarmRegistersAnotherAlarmOnShutdownAfterSelf) { |
| TestAlarm alarm2; |
| int64_t alarm_time = WallTimeNowInUsec() + 5000; |
| TestAlarmRegisterAnotherAlarmShutdown alarm1(&alarm2, alarm_time + 1000); |
| { |
| EpollTestAlarms ep; |
| ep.RegisterAlarm(alarm_time, &alarm1); |
| } |
| EXPECT_TRUE(alarm1.onshutdown_called()); |
| EXPECT_TRUE(alarm2.onshutdown_called()); |
| } |
| |
| TEST(SimpleEpollServerTest, TestWrite) { |
| SimpleEpollServer ep; |
| ep.set_timeout_in_us(1); |
| char data[kPageSize] = {0}; |
| |
| int pipe_fds[2]; |
| if (pipe(pipe_fds) < 0) { |
| EPOLL_PLOG(FATAL) << "pipe() failed"; |
| } |
| int read_fd = pipe_fds[0]; |
| int write_fd = pipe_fds[1]; |
| |
| RecordingCB recording_cb; |
| const Recorder* recorder = recording_cb.recorder(); |
| const std::vector<RecordEntry> *records = recorder->records(); |
| |
| // Register to listen to write events. |
| ep.RegisterFD(write_fd, &recording_cb, EPOLLOUT | O_NONBLOCK); |
| // At this point the recorder should have the creation and registration |
| // events. |
| EXPECT_EQ(2u, records->size()); |
| |
| // Fill up the pipe. |
| int written = 1; |
| for (int i = 0; i < 17 && written > 0 ; ++i) { |
| written = write(write_fd, &data, kPageSize); |
| } |
| EXPECT_LT(written, 0); |
| |
| // There should be no new events as the pipe is not available for writing. |
| ep.WaitForEventsAndExecuteCallbacks(); |
| EXPECT_EQ(2u, records->size()); |
| |
| // Now read data from the pipe to make it writable again. This time the |
| // we should get an EPOLLOUT event. |
| int size = read(read_fd, &data, kPageSize); |
| EXPECT_EQ(kPageSize, size); |
| ep.WaitForEventsAndExecuteCallbacks(); |
| EXPECT_EQ(3u, records->size()); |
| |
| // Now unsubscribe from writable events (which adds a modification record) |
| // and wait to verify that no event records are added. |
| ep.StopWrite(write_fd); |
| ep.WaitForEventsAndExecuteCallbacks(); |
| EXPECT_EQ(4u, records->size()); |
| |
| // We had the right number of events all along. Make sure they were actually |
| // the right events. |
| Recorder tmp; |
| tmp.Record(&recording_cb, CREATION, 0, 0); |
| tmp.Record(&recording_cb, REGISTRATION, write_fd, EPOLLOUT | O_NONBLOCK); |
| tmp.Record(&recording_cb, EVENT, write_fd, EPOLLOUT); |
| tmp.Record(&recording_cb, MODIFICATION, write_fd, O_NONBLOCK); |
| |
| EXPECT_TRUE(recorder->IsEqual(&tmp)); |
| ep.UnregisterFD(write_fd); |
| |
| close(read_fd); |
| close(write_fd); |
| } |
| |
| TEST(SimpleEpollServerTest, TestReadWrite) { |
| SimpleEpollServer ep; |
| ep.set_timeout_in_us(1); |
| char data[kPageSize] = {0}; |
| |
| int pipe_fds[2]; |
| if (pipe(pipe_fds) < 0) { |
| EPOLL_PLOG(FATAL) << "pipe() failed"; |
| } |
| int read_fd = pipe_fds[0]; |
| int write_fd = pipe_fds[1]; |
| |
| RecordingCB recording_cb; |
| const Recorder* recorder = recording_cb.recorder(); |
| const std::vector<RecordEntry> *records = recorder->records(); |
| |
| // Register to listen to read and write events. |
| ep.RegisterFDForReadWrite(read_fd, &recording_cb); |
| // At this point the recorder should have the creation and registration |
| // events. |
| EXPECT_EQ(2u, records->size()); |
| |
| int written = write(write_fd, &data, kPageSize); |
| EXPECT_EQ(kPageSize, written); |
| |
| ep.WaitForEventsAndExecuteCallbacks(); |
| ep.UnregisterFD(read_fd); |
| |
| close(read_fd); |
| close(write_fd); |
| } |
| |
| TEST(SimpleEpollServerTest, TestMultipleFDs) { |
| SimpleEpollServer ep; |
| ep.set_timeout_in_us(1); |
| char data = 'x'; |
| |
| int pipe_one[2]; |
| if (pipe(pipe_one) < 0) { |
| EPOLL_PLOG(FATAL) << "pipe() failed"; |
| } |
| int pipe_two[2]; |
| if (pipe(pipe_two) < 0) { |
| EPOLL_PLOG(FATAL) << "pipe() failed"; |
| } |
| |
| RecordingCB recording_cb_one; |
| const Recorder* recorder_one = recording_cb_one.recorder(); |
| const std::vector<RecordEntry> *records_one = recorder_one->records(); |
| |
| RecordingCB recording_cb_two; |
| const Recorder* recorder_two = recording_cb_two.recorder(); |
| const std::vector<RecordEntry> *records_two = recorder_two->records(); |
| |
| // Register to listen to read events for both pipes |
| ep.RegisterFDForRead(pipe_one[0], &recording_cb_one); |
| ep.RegisterFDForRead(pipe_two[0], &recording_cb_two); |
| |
| EXPECT_EQ(2u, records_one->size()); |
| EXPECT_EQ(2u, records_two->size()); |
| |
| EXPECT_EQ(1, write(pipe_one[1], &data, 1)); |
| ep.WaitForEventsAndExecuteCallbacks(); |
| EXPECT_EQ(3u, records_one->size()); |
| EXPECT_EQ(2u, records_two->size()); |
| |
| EXPECT_EQ(1, write(pipe_two[1], &data, 1)); |
| ep.WaitForEventsAndExecuteCallbacks(); |
| EXPECT_EQ(3u, records_one->size()); |
| EXPECT_EQ(3u, records_two->size()); |
| |
| EXPECT_EQ(1, write(pipe_one[1], &data, 1)); |
| EXPECT_EQ(1, write(pipe_two[1], &data, 1)); |
| ep.WaitForEventsAndExecuteCallbacks(); |
| EXPECT_EQ(4u, records_one->size()); |
| EXPECT_EQ(4u, records_two->size()); |
| |
| ep.WaitForEventsAndExecuteCallbacks(); |
| ep.UnregisterFD(pipe_one[0]); |
| ep.UnregisterFD(pipe_two[0]); |
| close(pipe_one[0]); |
| close(pipe_one[1]); |
| close(pipe_two[0]); |
| close(pipe_two[1]); |
| } |
| |
| // Check that the SimpleEpollServer calls OnShutdown for any registered FDs. |
| TEST(SimpleEpollServerTest, TestFDOnShutdown) { |
| int pipe_fds[2]; |
| if (pipe(pipe_fds) < 0) { |
| EPOLL_PLOG(FATAL) << "pipe() failed"; |
| } |
| int read_fd = pipe_fds[0]; |
| int write_fd = pipe_fds[1]; |
| |
| RecordingCB recording_cb1; |
| RecordingCB recording_cb2; |
| const Recorder* recorder1 = recording_cb1.recorder(); |
| const Recorder* recorder2 = recording_cb2.recorder(); |
| |
| { |
| SimpleEpollServer ep; |
| ep.set_timeout_in_us(1); |
| |
| // Register to listen to write events. |
| ep.RegisterFD(write_fd, &recording_cb1, EPOLLOUT | O_NONBLOCK); |
| ep.RegisterFD(read_fd, &recording_cb2, EPOLLIN | O_NONBLOCK); |
| } |
| |
| // Make sure OnShutdown was called for both callbacks. |
| Recorder write_recorder; |
| write_recorder.Record(&recording_cb1, CREATION, 0, 0); |
| write_recorder.Record( |
| &recording_cb1, REGISTRATION, write_fd, EPOLLOUT | O_NONBLOCK); |
| write_recorder.Record(&recording_cb1, UNREGISTRATION, write_fd, false); |
| write_recorder.Record(&recording_cb1, SHUTDOWN, write_fd, 0); |
| EXPECT_TRUE(recorder1->IsEqual(&write_recorder)); |
| |
| Recorder read_recorder; |
| read_recorder.Record(&recording_cb2, CREATION, 0, 0); |
| read_recorder.Record( |
| &recording_cb2, REGISTRATION, read_fd, EPOLLIN | O_NONBLOCK); |
| read_recorder.Record(&recording_cb2, UNREGISTRATION, read_fd, false); |
| read_recorder.Record(&recording_cb2, SHUTDOWN, read_fd, 0); |
| EXPECT_TRUE(recorder2->IsEqual(&read_recorder)); |
| |
| close(read_fd); |
| close(write_fd); |
| } |
| |
| class UnregisterCB : public EpollCallbackInterface { |
| public: |
| explicit UnregisterCB(int fd) |
| : eps_(nullptr), fd_(fd), onshutdown_called_(false) { |
| } |
| |
| ~UnregisterCB() override { |
| } |
| |
| void OnShutdown(SimpleEpollServer* /*eps*/, int fd) override { |
| eps_->UnregisterFD(fd_); |
| eps_->UnregisterFD(fd); |
| onshutdown_called_ = true; |
| eps_ = nullptr; |
| } |
| |
| void set_epollserver(SimpleEpollServer* eps) { eps_ = eps; } |
| bool onshutdown_called() { return onshutdown_called_; } |
| |
| void OnRegistration(SimpleEpollServer* /*eps*/, int /*fd*/, |
| int /*event_mask*/) override {} |
| void OnModification(int /*fd*/, int /*event_mask*/) override {} |
| void OnEvent(int /*fd*/, EpollEvent* /*event*/) override {} |
| void OnUnregistration(int /*fd*/, bool /*replaced*/) override {} |
| |
| std::string Name() const override { return "UnregisterCB"; } |
| |
| protected: |
| SimpleEpollServer* eps_; |
| int fd_; |
| bool onshutdown_called_; |
| }; |
| |
| // Check that unregistering fds in OnShutdown works cleanly. |
| TEST(SimpleEpollServerTest, TestUnregisteringFDsOnShutdown) { |
| int pipe_fds[2]; |
| if (pipe(pipe_fds) < 0) { |
| EPOLL_PLOG(FATAL) << "pipe() failed"; |
| } |
| int read_fd = pipe_fds[0]; |
| int write_fd = pipe_fds[1]; |
| |
| UnregisterCB unreg_cb1(read_fd); |
| UnregisterCB unreg_cb2(write_fd); |
| |
| { |
| SimpleEpollServer ep; |
| ep.set_timeout_in_us(1); |
| |
| unreg_cb1.set_epollserver(&ep); |
| unreg_cb2.set_epollserver(&ep); |
| |
| // Register to listen to write events. |
| ep.RegisterFD(write_fd, &unreg_cb1, EPOLLOUT | O_NONBLOCK); |
| ep.RegisterFD(read_fd, &unreg_cb2, EPOLLIN | O_NONBLOCK); |
| } |
| |
| // Make sure at least one onshutdown was called. |
| EXPECT_TRUE(unreg_cb1.onshutdown_called() || |
| unreg_cb2.onshutdown_called()); |
| // Make sure that both onshutdowns were not called. |
| EXPECT_TRUE(!(unreg_cb1.onshutdown_called() && |
| unreg_cb2.onshutdown_called())); |
| |
| close(read_fd); |
| close(write_fd); |
| } |
| |
| TEST(SimpleEpollServerTest, TestFDsAndAlarms) { |
| SimpleEpollServer ep; |
| ep.set_timeout_in_us(5); |
| char data = 'x'; |
| |
| int pipe_fds[2]; |
| if (pipe(pipe_fds) < 0) { |
| EPOLL_PLOG(FATAL) << "pipe() failed"; |
| } |
| |
| RecordingCB recording_cb; |
| const Recorder* recorder = recording_cb.recorder(); |
| const std::vector<RecordEntry> *records = recorder->records(); |
| |
| TestAlarm alarm; |
| |
| ep.RegisterFDForRead(pipe_fds[0], &recording_cb); |
| |
| EXPECT_EQ(2u, records->size()); |
| EXPECT_FALSE(alarm.was_called()); |
| |
| // Write to the pipe and set a longish alarm so we get a read event followed |
| // by an alarm event. |
| int written = write(pipe_fds[1], &data, 1); |
| EXPECT_EQ(1, written); |
| ep.WaitForEventsAndExecuteCallbacks(); |
| EXPECT_EQ(3u, records->size()); |
| EXPECT_FALSE(alarm.was_called()); |
| ep.RegisterAlarm(WallTimeNowInUsec() + 1000, &alarm); |
| WaitForAlarm(&ep, alarm); |
| EXPECT_EQ(3u, records->size()); |
| EXPECT_TRUE(alarm.was_called()); |
| alarm.Reset(); |
| |
| // Now set a short alarm so the alarm and the read event are called together. |
| ep.RegisterAlarm(WallTimeNowInUsec(), &alarm); |
| written = write(pipe_fds[1], &data, 1); |
| EXPECT_EQ(1, written); |
| ep.WaitForEventsAndExecuteCallbacks(); |
| EXPECT_TRUE(alarm.was_called()); |
| EXPECT_EQ(4u, records->size()); |
| |
| ep.UnregisterFD(pipe_fds[0]); |
| |
| close(pipe_fds[0]); |
| close(pipe_fds[1]); |
| } |
| |
| class EpollReader: public EpollCallbackInterface { |
| public: |
| explicit EpollReader(int len) |
| : len_(0), |
| expected_len_(len), |
| done_reading_(false) { |
| memset(&buf_, 0, kMaxBufLen); |
| } |
| |
| ~EpollReader() override {} |
| |
| void OnRegistration(SimpleEpollServer* /*eps*/, int /*fd*/, |
| int /*event_mask*/) override {} |
| |
| void OnModification(int /*fd*/, int /*event_mask*/) override {} |
| |
| void OnEvent(int fd, EpollEvent* event) override { |
| if (event->in_events & EPOLLIN) { |
| len_ += read(fd, &buf_ + len_, kMaxBufLen - len_); |
| } |
| |
| // If we have finished reading... |
| if (event->in_events & EPOLLHUP) { |
| CHECK_EQ(len_, expected_len_); |
| done_reading_ = true; |
| } |
| } |
| |
| void OnUnregistration(int /*fd*/, bool /*replaced*/) override {} |
| |
| void OnShutdown(SimpleEpollServer* /*eps*/, int /*fd*/) override { |
| // None of the current tests involve having active callbacks when the |
| // server shuts down. |
| EPOLL_LOG(FATAL); |
| } |
| |
| std::string Name() const override { return "EpollReader"; } |
| |
| // Returns true if the data in buf is the same as buf_, false otherwise. |
| bool CheckOutput(char* buf, int len) { |
| if (len != len_) { |
| return false; |
| } |
| return !memcmp(buf, buf_, len); |
| } |
| |
| bool done_reading() { return done_reading_; } |
| |
| protected: |
| int len_; |
| int expected_len_; |
| char buf_[kMaxBufLen]; |
| bool done_reading_; |
| }; |
| |
| void TestPipe(char *test_message, int len) { |
| int pipe_fds[2]; |
| if (pipe(pipe_fds) < 0) { |
| EPOLL_PLOG(FATAL) << "pipe failed()"; |
| } |
| int reader_pipe = pipe_fds[0]; |
| int writer_pipe = pipe_fds[1]; |
| int child_pid; |
| memset(test_message, 'x', len); |
| |
| switch (child_pid = fork()) { |
| case 0: { // Child will send message. |
| const char *message = test_message; |
| int size; |
| close(reader_pipe); |
| while ((size = write(writer_pipe, message, len)) > 0) { |
| message += size; |
| len -= size; |
| if (len == 0) { |
| break; |
| } |
| } |
| if (len > 0) { |
| EPOLL_PLOG(FATAL) << "write() failed"; |
| } |
| close(writer_pipe); |
| |
| _exit(0); |
| } |
| case -1: |
| EPOLL_PLOG(FATAL) << "fork() failed"; |
| break; |
| default: { // Parent will receive message. |
| close(writer_pipe); |
| auto ep = 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, ®_other_cb); |
| epoll_server.RegisterFDForReadWrite(-3, &recorder_cb); |
| |
| epoll_event ee; |
| ee.events = EPOLLIN; // asserted for all events for this test. |
| |
| // Note that these events are in 'backwards' order in terms of time. |
| // Currently, the SimpleEpollServer code invokes the CBs from last delivered |
| // to first delivered, so this is to be sure that we invoke the CB for -1 |
| // before -2, before -3. |
| ee.data.fd = -1; |
| epoll_server.AddEvent(2, ee); |
| ee.data.fd = -2; |
| epoll_server.AddEvent(1, ee); |
| ee.data.fd = -3; |
| epoll_server.AddEvent(0, ee); |
| |
| epoll_server.AdvanceBy(5); |
| epoll_server.WaitForEventsAndExecuteCallbacks(); |
| } |
| |
| Recorder correct_recorder; |
| correct_recorder.Record(&recorder_cb, CREATION, 0, 0); |
| correct_recorder.Record(&recorder_cb, REGISTRATION, -3, |
| EPOLLIN | EPOLLOUT); |
| correct_recorder.Record(&recorder_cb, UNREGISTRATION, -3, 0); |
| correct_recorder.Record(&recorder_cb, REGISTRATION, -3, |
| EPOLLIN | EPOLLOUT); |
| correct_recorder.Record(&recorder_cb, SHUTDOWN, -3, 0); |
| |
| EXPECT_TRUE(correct_recorder.IsEqual(recorder_cb.recorder())); |
| } |
| |
| class ReRegWhileReadyListOnEvent: public EpollCallbackInterface { |
| public: |
| explicit ReRegWhileReadyListOnEvent(int /*fd*/) : eps_(nullptr) {} |
| |
| void OnShutdown(SimpleEpollServer* /*eps*/, int /*fd*/) override {} |
| |
| void set_epoll_server(SimpleEpollServer* eps) { eps_ = eps; } |
| void OnRegistration(SimpleEpollServer* /*eps*/, int /*fd*/, |
| int /*event_mask*/) override {} |
| void OnModification(int /*fd*/, int /*event_mask*/) override {} |
| void OnEvent(int fd, EpollEvent* /*event_mask*/) override { |
| // This should cause no problems. |
| eps_->UnregisterFD(fd); |
| eps_->RegisterFDForReadWrite(fd, this); |
| eps_->UnregisterFD(fd); |
| } |
| void OnUnregistration(int /*fd*/, bool /*replaced*/) override {} |
| std::string Name() const override { return "ReRegWhileReadyListOnEvent"; } |
| |
| protected: |
| SimpleEpollServer* eps_; |
| }; |
| |
| // Nothing bad should happen when we do this. We're -only- |
| // testing that nothing bad occurs in this test. |
| TEST(SimpleEpollServerTest, |
| NothingBadWhenReRegisteringFDWhileProcessingFromReadyList) { |
| ReRegWhileReadyListOnEvent cb(0); |
| { |
| FakeSimpleEpollServer epoll_server; |
| cb.set_epoll_server(&epoll_server); |
| epoll_server.RegisterFDForReadWrite(0, &cb); |
| epoll_event ee; |
| ee.data.fd = 0; |
| epoll_server.AddEvent(0, ee); |
| epoll_server.AdvanceBy(1); |
| epoll_server.WaitForEventsAndExecuteCallbacks(); |
| } |
| } |
| |
| class UnRegEverythingReadyListOnEvent: public EpollCallbackInterface { |
| public: |
| UnRegEverythingReadyListOnEvent() : eps_(nullptr), fd_(0), fd_range_(0) {} |
| |
| void set_fd(int fd) { fd_ = fd; } |
| void set_fd_range(int fd_range) { fd_range_ = fd_range; } |
| void set_num_called(int* num_called) { num_called_ = num_called; } |
| |
| void OnShutdown(SimpleEpollServer* /*eps*/, int /*fd*/) override {} |
| |
| void set_epoll_server(SimpleEpollServer* eps) { eps_ = eps; } |
| void OnRegistration(SimpleEpollServer* eps, int fd, |
| int /*event_mask*/) override { |
| eps->SetFDReady(fd, EPOLLIN); |
| } |
| void OnModification(int /*fd*/, int /*event_mask*/) override {} |
| void OnEvent(int /*fd*/, EpollEvent* /*event*/) override { |
| // This should cause no problems. |
| CHECK(num_called_ != nullptr); |
| ++(*num_called_); |
| // Note that we're iterating from -fd_range + 1 -> 0. |
| // We do this because there is an FD installed into the |
| // epollserver somewhere in the low numbers. |
| // Using negative FD numbers (which are guaranteed to not |
| // exist in the epoll-server) ensures that we will not |
| // come in conflict with the preexisting FD. |
| for (int i = -fd_range_ + 1; i <= 0; ++i) { |
| eps_->UnregisterFD(i); |
| } |
| } |
| void OnUnregistration(int /*fd*/, bool /*replaced*/) override {} |
| std::string Name() const override { |
| return "UnRegEverythingReadyListOnEvent"; |
| } |
| |
| protected: |
| SimpleEpollServer* eps_; |
| int fd_; |
| int fd_range_; |
| int* num_called_; |
| }; |
| |
| TEST(SimpleEpollServerTest, |
| NothingBadWhenUnRegisteredWhileProcessingFromReadyList) { |
| const size_t kNumCallbacks = 32u; |
| UnRegEverythingReadyListOnEvent callbacks[kNumCallbacks]; |
| int num_called = 0; |
| { |
| FakeSimpleEpollServer epoll_server; |
| for (size_t i = 0; i < kNumCallbacks; ++i) { |
| callbacks[i].set_fd(-i); |
| callbacks[i].set_fd_range(kNumCallbacks); |
| callbacks[i].set_num_called(&num_called); |
| callbacks[i].set_epoll_server(&epoll_server); |
| epoll_server.RegisterFDForReadWrite(0, &callbacks[i]); |
| epoll_event ee; |
| ee.data.fd = -i; |
| epoll_server.AddEvent(0, ee); |
| } |
| epoll_server.AdvanceBy(1); |
| epoll_server.WaitForEventsAndExecuteCallbacks(); |
| epoll_server.WaitForEventsAndExecuteCallbacks(); |
| } |
| EXPECT_EQ(1, num_called); |
| } |
| |
| TEST(SimpleEpollServerTest, TestThatVerifyReadyListWorksWithNothingInList) { |
| FakeSimpleEpollServer epoll_server; |
| epoll_server.VerifyReadyList(); |
| } |
| |
| TEST(SimpleEpollServerTest, TestThatVerifyReadyListWorksWithStuffInLists) { |
| FakeSimpleEpollServer epoll_server; |
| epoll_server.VerifyReadyList(); |
| } |
| |
| TEST(SimpleEpollServerTest, |
| ApproximateNowInUsAccurateOutideOfWaitForEventsAndExecuteCallbacks) { |
| FakeSimpleEpollServer epoll_server; |
| epoll_server.AdvanceBy(1232); |
| EXPECT_EQ(epoll_server.ApproximateNowInUsec(), epoll_server.NowInUsec()); |
| epoll_server.AdvanceBy(1111); |
| EXPECT_EQ(epoll_server.ApproximateNowInUsec(), epoll_server.NowInUsec()); |
| } |
| |
| class ApproximateNowInUsecTestCB: public EpollCallbackInterface { |
| public: |
| ApproximateNowInUsecTestCB() : feps_(nullptr), called_(false) {} |
| |
| void OnRegistration(SimpleEpollServer* /*eps*/, int /*fd*/, |
| int /*event_mask*/) override {} |
| void OnModification(int /*fd*/, int /*event_mask*/) override {} |
| void OnEvent(int /*fd*/, EpollEvent* /*event*/) override { |
| EXPECT_EQ(feps_->ApproximateNowInUsec(), feps_->NowInUsec()); |
| feps_->AdvanceBy(1111); |
| EXPECT_EQ(1 * 1111 + feps_->ApproximateNowInUsec(), feps_->NowInUsec()); |
| feps_->AdvanceBy(1111); |
| EXPECT_EQ(2 * 1111 + feps_->ApproximateNowInUsec(), feps_->NowInUsec()); |
| called_ = true; |
| } |
| void OnUnregistration(int /*fd*/, bool /*replaced*/) override {} |
| void OnShutdown(SimpleEpollServer* /*eps*/, int /*fd*/) override {} |
| std::string Name() const override { return "ApproximateNowInUsecTestCB"; } |
| |
| void set_fakeepollserver(FakeSimpleEpollServer* feps) { feps_ = feps; } |
| bool called() const { return called_; } |
| |
| protected: |
| FakeSimpleEpollServer* feps_; |
| bool called_; |
| }; |
| |
| TEST(SimpleEpollServerTest, |
| ApproximateNowInUsApproximateInsideOfWaitForEventsAndExecuteCallbacks) { |
| int dummy_fd = 11111; |
| ApproximateNowInUsecTestCB aniutcb; |
| { |
| FakeSimpleEpollServer epoll_server; |
| aniutcb.set_fakeepollserver(&epoll_server); |
| |
| epoll_server.RegisterFD(dummy_fd, &aniutcb, EPOLLIN); |
| epoll_event ee; |
| ee.data.fd = dummy_fd; |
| ee.events = EPOLLIN; |
| epoll_server.AddEvent(10242, ee); |
| epoll_server.set_timeout_in_us(-1); |
| epoll_server.AdvanceByAndWaitForEventsAndExecuteCallbacks(20000); |
| EXPECT_TRUE(aniutcb.called()); |
| } |
| } |
| |
| // A mock epoll server that also simulates kernel delay in scheduling epoll |
| // events. |
| class FakeEpollServerWithDelay : public FakeSimpleEpollServer { |
| public: |
| FakeEpollServerWithDelay() : FakeSimpleEpollServer(), delay(0) {} |
| |
| int delay; |
| |
| protected: |
| int epoll_wait_impl(int epfd, struct epoll_event* events, int max_events, |
| int timeout_in_ms) override { |
| int out = FakeSimpleEpollServer::epoll_wait_impl(epfd, events, max_events, |
| timeout_in_ms); |
| AdvanceBy(delay); |
| return out; |
| } |
| }; |
| |
| // A callback that records the epoll event's delay. |
| class RecordDelayOnEvent: public EpollCallbackInterface { |
| public: |
| RecordDelayOnEvent() : last_delay(-1), eps_(nullptr) {} |
| |
| ~RecordDelayOnEvent() override { |
| } |
| |
| void OnShutdown(SimpleEpollServer* /*eps*/, int /*fd*/) override {} |
| |
| std::string Name() const override { return "RecordDelayOnEvent"; } |
| |
| void set_epoll_server(SimpleEpollServer* eps) { eps_ = eps; } |
| void OnRegistration(SimpleEpollServer* /*eps*/, int /*fd*/, |
| int /*event_mask*/) override {} |
| void OnModification(int /*fd*/, int /*event_mask*/) override {} |
| void OnEvent(int /*fd*/, EpollEvent* /*event*/) override { |
| last_delay = eps_->LastDelayInUsec(); |
| } |
| void OnUnregistration(int /*fd*/, bool /*replaced*/) override {} |
| |
| int64_t last_delay; |
| |
| protected: |
| SimpleEpollServer* eps_; |
| }; |
| |
| // Tests that an epoll callback sees the correct delay for its event when it |
| // calls LastDelayInUsec(). |
| TEST(EpollServerTest, TestLastDelay) { |
| RecordDelayOnEvent cb; |
| FakeEpollServerWithDelay epoll_server; |
| |
| cb.set_epoll_server(&epoll_server); |
| |
| epoll_server.RegisterFDForReadWrite(0, &cb); |
| epoll_event ee; |
| ee.data.fd = 0; |
| |
| // Inject delay, and confirm that it's reported. |
| epoll_server.set_timeout_in_us(5000); |
| epoll_server.delay = 6000; |
| epoll_server.AddEvent(0, ee); |
| epoll_server.AdvanceBy(1); |
| epoll_server.WaitForEventsAndExecuteCallbacks(); |
| EXPECT_EQ(cb.last_delay, 1000); |
| |
| // Fire an event before the timeout ends, and confirm that reported delay |
| // isn't negative. |
| epoll_server.set_timeout_in_us(5000); |
| epoll_server.delay = 0; |
| epoll_server.AddEvent(0, ee); |
| epoll_server.AdvanceBy(1); |
| epoll_server.WaitForEventsAndExecuteCallbacks(); |
| EXPECT_EQ(cb.last_delay, 0); |
| |
| // Wait forever until an event fires, and confirm there's no reported delay. |
| epoll_server.set_timeout_in_us(-1); |
| epoll_server.delay = 6000; |
| epoll_server.AddEvent(0, ee); |
| epoll_server.AdvanceBy(1); |
| epoll_server.WaitForEventsAndExecuteCallbacks(); |
| EXPECT_EQ(cb.last_delay, 0); |
| } |
| |
| TEST(SimpleEpollServerAlarmTest, TestShutdown) { |
| std::unique_ptr<SimpleEpollServer> eps(new SimpleEpollServer); |
| EpollAlarm alarm1; |
| EpollAlarm alarm2; |
| |
| eps->RegisterAlarmApproximateDelta(10000000, &alarm1); |
| eps->RegisterAlarmApproximateDelta(10000000, &alarm2); |
| |
| alarm2.UnregisterIfRegistered(); |
| EXPECT_FALSE(alarm2.registered()); |
| eps = nullptr; |
| |
| EXPECT_FALSE(alarm1.registered()); |
| } |
| |
| TEST(SimpleEpollServerAlarmTest, TestUnregister) { |
| SimpleEpollServer eps; |
| EpollAlarm alarm; |
| |
| eps.RegisterAlarmApproximateDelta(10000000, &alarm); |
| EXPECT_TRUE(alarm.registered()); |
| |
| alarm.UnregisterIfRegistered(); |
| EXPECT_FALSE(alarm.registered()); |
| |
| alarm.UnregisterIfRegistered(); |
| EXPECT_FALSE(alarm.registered()); |
| } |
| |
| TEST(SimpleEpollServerAlarmTest, TestUnregisterOnDestruction) { |
| EpollTestServer eps; |
| std::unique_ptr<EpollAlarm> alarm(new EpollAlarm()); |
| EpollAlarm* alarm_ptr = alarm.get(); |
| |
| eps.RegisterAlarmApproximateDelta(10000000, alarm.get()); |
| EXPECT_TRUE(eps.ContainsAlarm(alarm_ptr)); |
| alarm = nullptr; |
| EXPECT_EQ(0u, eps.GetNumPendingAlarmsForTest()); |
| } |
| |
| TEST(SimpleEpollServerAlarmTest, TestUnregisterOnAlarm) { |
| EpollTestServer eps; |
| EpollAlarm alarm; |
| |
| eps.RegisterAlarmApproximateDelta(1, &alarm); |
| EXPECT_TRUE(eps.ContainsAlarm(&alarm)); |
| |
| while (alarm.registered()) { |
| eps.WaitForEventsAndExecuteCallbacks(); |
| } |
| EXPECT_FALSE(eps.ContainsAlarm(&alarm)); |
| } |
| |
| TEST(SimpleEpollServerAlarmTest, TestReregisterAlarm) { |
| EpollTestAlarms ep; |
| |
| EpollAlarm alarm; |
| ep.set_time(1000); |
| ep.RegisterAlarm(5000, &alarm); |
| |
| EXPECT_EQ(1u, ep.GetNumPendingAlarmsForTest()); |
| alarm.ReregisterAlarm(6000); |
| EXPECT_EQ(1u, ep.GetNumPendingAlarmsForTest()); |
| |
| ep.set_time(5000); |
| ep.set_timeout_in_us(0); |
| ep.CallAndReregisterAlarmEvents(); |
| EXPECT_EQ(1u, ep.GetNumPendingAlarmsForTest()); |
| |
| ep.set_time(6000); |
| ep.CallAndReregisterAlarmEvents(); |
| EXPECT_EQ(0u, ep.GetNumPendingAlarmsForTest()); |
| } |
| |
| TEST(SimpleEpollServerAlarmTest, TestThatSameAlarmCanNotBeRegisteredTwice) { |
| TestAlarm alarm; |
| SimpleEpollServer epoll_server; |
| epoll_server.RegisterAlarm(1, &alarm); |
| EXPECT_EPOLL_BUG(epoll_server.RegisterAlarm(1, &alarm), |
| "Alarm already exists"); |
| } |
| |
| } // namespace |
| |
| } // namespace test |
| |
| } // namespace epoll_server |