Project import generated by Copybara. PiperOrigin-RevId: 243658601 Change-Id: I2522d468d82c86f8f222cf0e70114d6a9ca2b78c
diff --git a/epoll_server/fake_simple_epoll_server.cc b/epoll_server/fake_simple_epoll_server.cc new file mode 100644 index 0000000..7e55945 --- /dev/null +++ b/epoll_server/fake_simple_epoll_server.cc
@@ -0,0 +1,56 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/third_party/quiche/src/epoll_server/fake_simple_epoll_server.h" + +namespace epoll_server { +namespace test { + +FakeTimeSimpleEpollServer::FakeTimeSimpleEpollServer() : now_in_usec_(0) {} + +FakeTimeSimpleEpollServer::~FakeTimeSimpleEpollServer() = default; + +int64_t FakeTimeSimpleEpollServer::NowInUsec() const { return now_in_usec_; } + +FakeSimpleEpollServer::FakeSimpleEpollServer() : until_in_usec_(-1) {} + +FakeSimpleEpollServer::~FakeSimpleEpollServer() = default; + +int FakeSimpleEpollServer::epoll_wait_impl(int epfd, struct epoll_event* events, + int max_events, int timeout_in_ms) { + int num_events = 0; + while (!event_queue_.empty() && num_events < max_events && + event_queue_.begin()->first <= NowInUsec() && + ((until_in_usec_ == -1) || + (event_queue_.begin()->first < until_in_usec_))) { + int64_t event_time_in_usec = event_queue_.begin()->first; + events[num_events] = event_queue_.begin()->second; + if (event_time_in_usec > NowInUsec()) { + set_now_in_usec(event_time_in_usec); + } + event_queue_.erase(event_queue_.begin()); + ++num_events; + } + if (num_events == 0) { // then we'd have waited 'till the timeout. + if (until_in_usec_ < 0) { // then we don't care what the final time is. + if (timeout_in_ms > 0) { + AdvanceBy(timeout_in_ms * 1000); + } + } else { // except we assume that we don't wait for the timeout + // period if until_in_usec_ is a positive number. + set_now_in_usec(until_in_usec_); + // And reset until_in_usec_ to signal no waiting (as + // the AdvanceByExactly* stuff is meant to be one-shot, + // as are all similar net::EpollServer functions) + until_in_usec_ = -1; + } + } + if (until_in_usec_ >= 0) { + CHECK(until_in_usec_ >= NowInUsec()); + } + return num_events; +} + +} // namespace test +} // namespace epoll_server
diff --git a/epoll_server/fake_simple_epoll_server.h b/epoll_server/fake_simple_epoll_server.h new file mode 100644 index 0000000..d245743 --- /dev/null +++ b/epoll_server/fake_simple_epoll_server.h
@@ -0,0 +1,113 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef QUICHE_EPOLL_SERVER_FAKE_SIMPLE_EPOLL_SERVER_H_ +#define QUICHE_EPOLL_SERVER_FAKE_SIMPLE_EPOLL_SERVER_H_ + +#include <stddef.h> +#include <stdint.h> + +#include <unordered_map> +#include <unordered_set> + +#include "net/third_party/quiche/src/epoll_server/simple_epoll_server.h" + +namespace epoll_server { +namespace test { + +// Unlike the full FakeEpollServer, this only lies about the time but lets +// fd events operate normally. Usefully when interacting with real backends +// but wanting to skip forward in time to trigger timeouts. +class FakeTimeSimpleEpollServer : public SimpleEpollServer { + public: + FakeTimeSimpleEpollServer(); + FakeTimeSimpleEpollServer(const FakeTimeSimpleEpollServer&) = delete; + FakeTimeSimpleEpollServer operator=(const FakeTimeSimpleEpollServer&) = + delete; + + ~FakeTimeSimpleEpollServer() override; + + // Replaces the net::EpollServer NowInUsec. + int64_t NowInUsec() const override; + + void set_now_in_usec(int64_t nius) { now_in_usec_ = nius; } + + // Advances the virtual 'now' by advancement_usec. + void AdvanceBy(int64_t advancement_usec) { + set_now_in_usec(NowInUsec() + advancement_usec); + } + + // Advances the virtual 'now' by advancement_usec, and + // calls WaitForEventAndExecteCallbacks. + // Note that the WaitForEventsAndExecuteCallbacks invocation + // may cause NowInUs to advance beyond what was specified here. + // If that is not desired, use the AdvanceByExactly calls. + void AdvanceByAndWaitForEventsAndExecuteCallbacks(int64_t advancement_usec) { + AdvanceBy(advancement_usec); + WaitForEventsAndExecuteCallbacks(); + } + + private: + int64_t now_in_usec_; +}; + +class FakeSimpleEpollServer : public FakeTimeSimpleEpollServer { + public: // type definitions + using EventQueue = std::unordered_multimap<int64_t, struct epoll_event>; + + FakeSimpleEpollServer(); + FakeSimpleEpollServer(const FakeSimpleEpollServer&) = delete; + FakeSimpleEpollServer operator=(const FakeSimpleEpollServer&) = delete; + + ~FakeSimpleEpollServer() override; + + // time_in_usec is the time at which the event specified + // by 'ee' will be delivered. Note that it -is- possible + // to add an event for a time which has already been passed.. + // .. upon the next time that the callbacks are invoked, + // all events which are in the 'past' will be delivered. + void AddEvent(int64_t time_in_usec, const struct epoll_event& ee) { + event_queue_.insert(std::make_pair(time_in_usec, ee)); + } + + // Advances the virtual 'now' by advancement_usec, + // and ensure that the next invocation of + // WaitForEventsAndExecuteCallbacks goes no farther than + // advancement_usec from the current time. + void AdvanceByExactly(int64_t advancement_usec) { + until_in_usec_ = NowInUsec() + advancement_usec; + set_now_in_usec(NowInUsec() + advancement_usec); + } + + // As above, except calls WaitForEventsAndExecuteCallbacks. + void AdvanceByExactlyAndCallCallbacks(int64_t advancement_usec) { + AdvanceByExactly(advancement_usec); + WaitForEventsAndExecuteCallbacks(); + } + + std::unordered_set<AlarmCB*>::size_type NumberOfAlarms() const { + return all_alarms_.size(); + } + + protected: // functions + // These functions do nothing here, as we're not actually + // using the epoll_* syscalls. + void DelFD(int fd) const override {} + void AddFD(int fd, int event_mask) const override {} + void ModFD(int fd, int event_mask) const override {} + + // Replaces the epoll_server's epoll_wait_impl. + int epoll_wait_impl(int epfd, struct epoll_event* events, int max_events, + int timeout_in_ms) override; + void SetNonblocking(int fd) override {} + + private: // members + EventQueue event_queue_; + int64_t until_in_usec_; +}; + +} // namespace test +} // namespace epoll_server + +#endif // QUICHE_EPOLL_SERVER_FAKE_SIMPLE_EPOLL_SERVER_H_
diff --git a/epoll_server/platform/api/epoll_address_test_utils.h b/epoll_server/platform/api/epoll_address_test_utils.h new file mode 100644 index 0000000..ae87ded --- /dev/null +++ b/epoll_server/platform/api/epoll_address_test_utils.h
@@ -0,0 +1,16 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef QUICHE_EPOLL_SERVER_PLATFORM_API_EPOLL_ADDRESS_TEST_UTILS_H_ +#define QUICHE_EPOLL_SERVER_PLATFORM_API_EPOLL_ADDRESS_TEST_UTILS_H_ + +#include "net/epoll_server/platform/impl/epoll_address_test_utils_impl.h" + +namespace epoll_server { + +int AddressFamilyUnderTest() { return AddressFamilyUnderTestImpl(); } + +} // namespace epoll_server + +#endif // QUICHE_EPOLL_SERVER_PLATFORM_API_EPOLL_ADDRESS_TEST_UTILS_H_
diff --git a/epoll_server/platform/api/epoll_bug.h b/epoll_server/platform/api/epoll_bug.h new file mode 100644 index 0000000..d90c427 --- /dev/null +++ b/epoll_server/platform/api/epoll_bug.h
@@ -0,0 +1,12 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef QUICHE_EPOLL_SERVER_PLATFORM_API_EPOLL_BUG_H_ +#define QUICHE_EPOLL_SERVER_PLATFORM_API_EPOLL_BUG_H_ + +#include "net/epoll_server/platform/impl/epoll_bug_impl.h" + +#define EPOLL_BUG EPOLL_BUG_IMPL + +#endif // QUICHE_EPOLL_SERVER_PLATFORM_API_EPOLL_BUG_H_
diff --git a/epoll_server/platform/api/epoll_expect_bug.h b/epoll_server/platform/api/epoll_expect_bug.h new file mode 100644 index 0000000..a7795c6 --- /dev/null +++ b/epoll_server/platform/api/epoll_expect_bug.h
@@ -0,0 +1,12 @@ +// Copyright (c) 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef QUICHE_EPOLL_SERVER_PLATFORM_API_EPOLL_EXPECT_BUG_H_ +#define QUICHE_EPOLL_SERVER_PLATFORM_API_EPOLL_EXPECT_BUG_H_ + +#include "net/epoll_server/platform/impl/epoll_expect_bug_impl.h" + +#define EXPECT_EPOLL_BUG EXPECT_EPOLL_BUG_IMPL + +#endif // QUICHE_EPOLL_SERVER_PLATFORM_API_EPOLL_EXPECT_BUG_H_
diff --git a/epoll_server/platform/api/epoll_logging.h b/epoll_server/platform/api/epoll_logging.h new file mode 100644 index 0000000..bcd9b96 --- /dev/null +++ b/epoll_server/platform/api/epoll_logging.h
@@ -0,0 +1,19 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef QUICHE_EPOLL_SERVER_PLATFORM_API_EPOLL_LOGGING_H_ +#define QUICHE_EPOLL_SERVER_PLATFORM_API_EPOLL_LOGGING_H_ + +#include "net/epoll_server/platform/impl/epoll_logging_impl.h" + +namespace epoll_server { + +#define EPOLL_LOG(severity) EPOLL_LOG_IMPL(severity) +#define EPOLL_VLOG(verbosity) EPOLL_VLOG_IMPL(verbosity) +#define EPOLL_DVLOG(verbosity) EPOLL_DVLOG_IMPL(verbosity) +#define EPOLL_PLOG(severity) EPOLL_PLOG_IMPL(severity) + +} // namespace epoll_server + +#endif // QUICHE_EPOLL_SERVER_PLATFORM_API_EPOLL_LOGGING_H_
diff --git a/epoll_server/platform/api/epoll_test.h b/epoll_server/platform/api/epoll_test.h new file mode 100644 index 0000000..e5a0524 --- /dev/null +++ b/epoll_server/platform/api/epoll_test.h
@@ -0,0 +1,11 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef QUICHE_EPOLL_SERVER_PLATFORM_API_EPOLL_TEST_H_ +#define QUICHE_EPOLL_SERVER_PLATFORM_API_EPOLL_TEST_H_ + +#include "net/epoll_server/platform/impl/epoll_test_impl.h" +#define EpollTest EpollTestImpl + +#endif // QUICHE_EPOLL_SERVER_PLATFORM_API_EPOLL_TEST_H_
diff --git a/epoll_server/platform/api/epoll_thread.h b/epoll_server/platform/api/epoll_thread.h new file mode 100644 index 0000000..a41dd28 --- /dev/null +++ b/epoll_server/platform/api/epoll_thread.h
@@ -0,0 +1,27 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef QUICHE_EPOLL_SERVER_PLATFORM_API_EPOLL_THREAD_H_ +#define QUICHE_EPOLL_SERVER_PLATFORM_API_EPOLL_THREAD_H_ + +#include <string> + +#include "net/epoll_server/platform/impl/epoll_thread_impl.h" + +namespace epoll_server { + +// A class representing a thread of execution in QUIC. +class EpollThread : public EpollThreadImpl { + public: + EpollThread(const std::string& string) : EpollThreadImpl(string) {} + EpollThread(const EpollThread&) = delete; + EpollThread& operator=(const EpollThread&) = delete; + + // Impl defines a virtual void Run() method which subclasses + // must implement. +}; + +} // namespace epoll_server + +#endif // QUICHE_EPOLL_SERVER_PLATFORM_API_EPOLL_THREAD_H_
diff --git a/epoll_server/platform/api/epoll_time.h b/epoll_server/platform/api/epoll_time.h new file mode 100644 index 0000000..9fefaa7 --- /dev/null +++ b/epoll_server/platform/api/epoll_time.h
@@ -0,0 +1,16 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef QUICHE_EPOLL_SERVER_PLATFORM_API_EPOLL_TIME_H_ +#define QUICHE_EPOLL_SERVER_PLATFORM_API_EPOLL_TIME_H_ + +#include "net/epoll_server/platform/impl/epoll_time_impl.h" + +namespace epoll_server { + +int64_t WallTimeNowInUsec() { return WallTimeNowInUsecImpl(); } + +} // namespace epoll_server + +#endif // QUICHE_EPOLL_SERVER_PLATFORM_API_EPOLL_TIME_H_
diff --git a/epoll_server/simple_epoll_server.cc b/epoll_server/simple_epoll_server.cc new file mode 100644 index 0000000..0117542 --- /dev/null +++ b/epoll_server/simple_epoll_server.cc
@@ -0,0 +1,812 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/third_party/quiche/src/epoll_server/simple_epoll_server.h" + +#include <errno.h> // for errno and strerror_r +#include <stdlib.h> // for abort +#include <unistd.h> // For read, pipe, close and write. + +#include <algorithm> +#include <utility> + +#include "net/third_party/quiche/src/epoll_server/platform/api/epoll_bug.h" +#include "net/third_party/quiche/src/epoll_server/platform/api/epoll_time.h" + +// Design notes: An efficient implementation of ready list has the following +// desirable properties: +// +// A. O(1) insertion into/removal from the list in any location. +// B. Once the callback is found by hash lookup using the fd, the lookup of +// corresponding entry in the list is O(1). +// C. Safe insertion into/removal from the list during list iteration. (The +// ready list's purpose is to enable completely event driven I/O model. +// Thus, all the interesting bits happen in the callback. It is critical +// to not place any restriction on the API during list iteration. +// +// The current implementation achieves these goals with the following design: +// +// - The ready list is constructed as a doubly linked list to enable O(1) +// insertion/removal (see man 3 queue). +// - The forward and backward links are directly embedded inside the +// CBAndEventMask struct. This enables O(1) lookup in the list for a given +// callback. (Techincally, we could've used std::list of hash_set::iterator, +// and keep a list::iterator in CBAndEventMask to achieve the same effect. +// However, iterators have two problems: no way to portably invalidate them, +// and no way to tell whether an iterator is singular or not. The only way to +// overcome these issues is to keep bools in both places, but that throws off +// memory alignment (up to 7 wasted bytes for each bool). The extra level of +// indirection will also likely be less cache friendly. Direct manipulation +// of link pointers makes it easier to retrieve the CBAndEventMask from the +// list, easier to check whether an CBAndEventMask is in the list, uses less +// memory (save 32 bytes/fd), and does not affect cache usage (we need to +// read in the struct to use the callback anyway).) +// - Embed the fd directly into CBAndEventMask and switch to using hash_set. +// This removes the need to store hash_map::iterator in the list just so that +// we can get both the fd and the callback. +// - The ready list is "one shot": each entry is removed before OnEvent is +// called. This removes the mutation-while-iterating problem. +// - Use two lists to keep track of callbacks. The ready_list_ is the one used +// for registration. Before iteration, the ready_list_ is swapped into the +// tmp_list_. Once iteration is done, tmp_list_ will be empty, and +// ready_list_ will have all the new ready fds. + +// The size we use for buffers passed to strerror_r +static const int kErrorBufferSize = 256; + +namespace epoll_server { + +template <typename T> +class AutoReset { + public: + AutoReset(T* scoped_variable, T new_value) + : scoped_variable_(scoped_variable), + original_value_(std::move(*scoped_variable)) { + *scoped_variable_ = std::move(new_value); + } + AutoReset(const AutoReset&) = delete; + AutoReset& operator=(const AutoReset&) = delete; + + ~AutoReset() { *scoped_variable_ = std::move(original_value_); } + + private: + T* scoped_variable_; + T original_value_; +}; + +// Clears the pipe and returns. Used for waking the epoll server up. +class ReadPipeCallback : public EpollCallbackInterface { + public: + void OnEvent(int fd, EpollEvent* event) override { + DCHECK(event->in_events == EPOLLIN); + int data; + int data_read = 1; + // Read until the pipe is empty. + while (data_read > 0) { + data_read = read(fd, &data, sizeof(data)); + } + } + void OnShutdown(SimpleEpollServer* eps, int fd) override {} + void OnRegistration(SimpleEpollServer*, int, int) override {} + void OnModification(int, int) override {} // COV_NF_LINE + void OnUnregistration(int, bool) override {} // COV_NF_LINE + std::string Name() const override { return "ReadPipeCallback"; } +}; + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +SimpleEpollServer::SimpleEpollServer() + : epoll_fd_(epoll_create(1024)), + timeout_in_us_(0), + recorded_now_in_us_(0), + ready_list_size_(0), + wake_cb_(new ReadPipeCallback), + read_fd_(-1), + write_fd_(-1), + in_wait_for_events_and_execute_callbacks_(false), + in_shutdown_(false), + last_delay_in_usec_(0) { + // ensure that the epoll_fd_ is valid. + CHECK_NE(epoll_fd_, -1); + LIST_INIT(&ready_list_); + LIST_INIT(&tmp_list_); + + int pipe_fds[2]; + if (pipe(pipe_fds) < 0) { + // Unfortunately, it is impossible to test any such initialization in + // a constructor (as virtual methods do not yet work). + // This -could- be solved by moving initialization to an outside + // call... + int saved_errno = errno; + char buf[kErrorBufferSize]; + EPOLL_LOG(FATAL) << "Error " << saved_errno << " in pipe(): " + << strerror_r(saved_errno, buf, sizeof(buf)); + } + read_fd_ = pipe_fds[0]; + write_fd_ = pipe_fds[1]; + RegisterFD(read_fd_, wake_cb_.get(), EPOLLIN); +} + +void SimpleEpollServer::CleanupFDToCBMap() { + auto cb_iter = cb_map_.begin(); + while (cb_iter != cb_map_.end()) { + int fd = cb_iter->fd; + CB* cb = cb_iter->cb; + + cb_iter->in_use = true; + if (cb) { + cb->OnShutdown(this, fd); + } + + cb_map_.erase(cb_iter); + cb_iter = cb_map_.begin(); + } +} + +void SimpleEpollServer::CleanupTimeToAlarmCBMap() { + TimeToAlarmCBMap::iterator erase_it; + + // Call OnShutdown() on alarms. Note that the structure of the loop + // is similar to the structure of loop in the function HandleAlarms() + for (auto i = alarm_map_.begin(); i != alarm_map_.end();) { + // Note that OnShutdown() can call UnregisterAlarm() on + // other iterators. OnShutdown() should not call UnregisterAlarm() + // on self because by definition the iterator is not valid any more. + i->second->OnShutdown(this); + erase_it = i; + ++i; + alarm_map_.erase(erase_it); + } +} + +SimpleEpollServer::~SimpleEpollServer() { + DCHECK_EQ(in_shutdown_, false); + in_shutdown_ = true; +#ifdef EPOLL_SERVER_EVENT_TRACING + EPOLL_LOG(INFO) << "\n" << event_recorder_; +#endif + EPOLL_VLOG(2) << "Shutting down epoll server "; + CleanupFDToCBMap(); + + LIST_INIT(&ready_list_); + LIST_INIT(&tmp_list_); + + CleanupTimeToAlarmCBMap(); + + close(read_fd_); + close(write_fd_); + close(epoll_fd_); +} + +// Whether a CBAandEventMask is on the ready list is determined by a non-NULL +// le_prev pointer (le_next being NULL indicates end of list). +inline void SimpleEpollServer::AddToReadyList(CBAndEventMask* cb_and_mask) { + if (cb_and_mask->entry.le_prev == NULL) { + LIST_INSERT_HEAD(&ready_list_, cb_and_mask, entry); + ++ready_list_size_; + } +} + +inline void SimpleEpollServer::RemoveFromReadyList( + const CBAndEventMask& cb_and_mask) { + if (cb_and_mask.entry.le_prev != NULL) { + LIST_REMOVE(&cb_and_mask, entry); + // Clean up all the ready list states. Don't bother with the other fields + // as they are initialized when the CBAandEventMask is added to the ready + // list. This saves a few cycles in the inner loop. + cb_and_mask.entry.le_prev = NULL; + --ready_list_size_; + if (ready_list_size_ == 0) { + DCHECK(ready_list_.lh_first == NULL); + DCHECK(tmp_list_.lh_first == NULL); + } + } +} + +void SimpleEpollServer::RegisterFD(int fd, CB* cb, int event_mask) { + CHECK(cb); + EPOLL_VLOG(3) << "RegisterFD fd=" << fd << " event_mask=" << event_mask; + auto fd_i = cb_map_.find(CBAndEventMask(NULL, 0, fd)); + if (cb_map_.end() != fd_i) { + // do we just abort, or do we just unregister the other callback? + // for now, lets just unregister the other callback. + + // unregister any callback that may already be registered for this FD. + CB* other_cb = fd_i->cb; + if (other_cb) { + // Must remove from the ready list before erasing. + RemoveFromReadyList(*fd_i); + other_cb->OnUnregistration(fd, true); + ModFD(fd, event_mask); + } else { + // already unregistered, so just recycle the node. + AddFD(fd, event_mask); + } + fd_i->cb = cb; + fd_i->event_mask = event_mask; + fd_i->events_to_fake = 0; + } else { + AddFD(fd, event_mask); + cb_map_.insert(CBAndEventMask(cb, event_mask, fd)); + } + + // set the FD to be non-blocking. + SetNonblocking(fd); + + cb->OnRegistration(this, fd, event_mask); +} + +void SimpleEpollServer::SetNonblocking(int fd) { + int flags = fcntl(fd, F_GETFL, 0); + if (flags == -1) { + int saved_errno = errno; + char buf[kErrorBufferSize]; + EPOLL_LOG(FATAL) << "Error " << saved_errno << " doing fcntl(" << fd + << ", F_GETFL, 0): " + << strerror_r(saved_errno, buf, sizeof(buf)); + } + if (!(flags & O_NONBLOCK)) { + int saved_flags = flags; + flags = fcntl(fd, F_SETFL, flags | O_NONBLOCK); + if (flags == -1) { + // bad. + int saved_errno = errno; + char buf[kErrorBufferSize]; + EPOLL_LOG(FATAL) << "Error " << saved_errno << " doing fcntl(" << fd + << ", F_SETFL, " << saved_flags + << "): " << strerror_r(saved_errno, buf, sizeof(buf)); + } + } +} + +int SimpleEpollServer::epoll_wait_impl(int epfd, struct epoll_event* events, + int max_events, int timeout_in_ms) { + return epoll_wait(epfd, events, max_events, timeout_in_ms); +} + +void SimpleEpollServer::RegisterFDForWrite(int fd, CB* cb) { + RegisterFD(fd, cb, EPOLLOUT); +} + +void SimpleEpollServer::RegisterFDForReadWrite(int fd, CB* cb) { + RegisterFD(fd, cb, EPOLLIN | EPOLLOUT); +} + +void SimpleEpollServer::RegisterFDForRead(int fd, CB* cb) { + RegisterFD(fd, cb, EPOLLIN); +} + +void SimpleEpollServer::UnregisterFD(int fd) { + auto fd_i = cb_map_.find(CBAndEventMask(NULL, 0, fd)); + if (cb_map_.end() == fd_i || fd_i->cb == NULL) { + // Doesn't exist in server, or has gone through UnregisterFD once and still + // inside the callchain of OnEvent. + return; + } +#ifdef EPOLL_SERVER_EVENT_TRACING + event_recorder_.RecordUnregistration(fd); +#endif + CB* cb = fd_i->cb; + // Since the links are embedded within the struct, we must remove it from the + // list before erasing it from the hash_set. + RemoveFromReadyList(*fd_i); + DelFD(fd); + cb->OnUnregistration(fd, false); + // fd_i->cb is NULL if that fd is unregistered inside the callchain of + // OnEvent. Since the SimpleEpollServer needs a valid CBAndEventMask after + // OnEvent returns in order to add it to the ready list, we cannot have + // UnregisterFD erase the entry if it is in use. Thus, a NULL fd_i->cb is used + // as a condition that tells the SimpleEpollServer that this entry is unused + // at a later point. + if (!fd_i->in_use) { + cb_map_.erase(fd_i); + } else { + // Remove all trace of the registration, and just keep the node alive long + // enough so the code that calls OnEvent doesn't have to worry about + // figuring out whether the CBAndEventMask is valid or not. + fd_i->cb = NULL; + fd_i->event_mask = 0; + fd_i->events_to_fake = 0; + } +} + +void SimpleEpollServer::ModifyCallback(int fd, int event_mask) { + ModifyFD(fd, ~0, event_mask); +} + +void SimpleEpollServer::StopRead(int fd) { ModifyFD(fd, EPOLLIN, 0); } + +void SimpleEpollServer::StartRead(int fd) { ModifyFD(fd, 0, EPOLLIN); } + +void SimpleEpollServer::StopWrite(int fd) { ModifyFD(fd, EPOLLOUT, 0); } + +void SimpleEpollServer::StartWrite(int fd) { ModifyFD(fd, 0, EPOLLOUT); } + +void SimpleEpollServer::HandleEvent(int fd, int event_mask) { +#ifdef EPOLL_SERVER_EVENT_TRACING + event_recorder_.RecordEpollEvent(fd, event_mask); +#endif + auto fd_i = cb_map_.find(CBAndEventMask(NULL, 0, fd)); + if (fd_i == cb_map_.end() || fd_i->cb == NULL) { + // Ignore the event. + // This could occur if epoll() returns a set of events, and + // while processing event A (earlier) we removed the callback + // for event B (and are now processing event B). + return; + } + fd_i->events_asserted = event_mask; + CBAndEventMask* cb_and_mask = const_cast<CBAndEventMask*>(&*fd_i); + AddToReadyList(cb_and_mask); +} + +void SimpleEpollServer::WaitForEventsAndExecuteCallbacks() { + if (in_wait_for_events_and_execute_callbacks_) { + EPOLL_LOG(DFATAL) << "Attempting to call WaitForEventsAndExecuteCallbacks" + " when an ancestor to the current function is already" + " WaitForEventsAndExecuteCallbacks!"; + // The line below is actually tested, but in coverage mode, + // we never see it. + return; // COV_NF_LINE + } + AutoReset<bool> recursion_guard(&in_wait_for_events_and_execute_callbacks_, + true); + if (alarm_map_.empty()) { + // no alarms, this is business as usual. + WaitForEventsAndCallHandleEvents(timeout_in_us_, events_, events_size_); + recorded_now_in_us_ = 0; + return; + } + + // store the 'now'. If we recomputed 'now' every iteration + // down below, then we might never exit that loop-- any + // long-running alarms might install other long-running + // alarms, etc. By storing it here now, we ensure that + // a more reasonable amount of work is done here. + int64_t now_in_us = NowInUsec(); + + // Get the first timeout from the alarm_map where it is + // stored in absolute time. + int64_t next_alarm_time_in_us = alarm_map_.begin()->first; + EPOLL_VLOG(4) << "next_alarm_time = " << next_alarm_time_in_us + << " now = " << now_in_us + << " timeout_in_us = " << timeout_in_us_; + + int64_t wait_time_in_us; + int64_t alarm_timeout_in_us = next_alarm_time_in_us - now_in_us; + + // If the next alarm is sooner than the default timeout, or if there is no + // timeout (timeout_in_us_ == -1), wake up when the alarm should fire. + // Otherwise use the default timeout. + if (alarm_timeout_in_us < timeout_in_us_ || timeout_in_us_ < 0) { + wait_time_in_us = std::max(alarm_timeout_in_us, static_cast<int64_t>(0)); + } else { + wait_time_in_us = timeout_in_us_; + } + + EPOLL_VLOG(4) << "wait_time_in_us = " << wait_time_in_us; + + // wait for events. + + WaitForEventsAndCallHandleEvents(wait_time_in_us, events_, events_size_); + CallAndReregisterAlarmEvents(); + recorded_now_in_us_ = 0; +} + +void SimpleEpollServer::SetFDReady(int fd, int events_to_fake) { + auto fd_i = cb_map_.find(CBAndEventMask(NULL, 0, fd)); + if (cb_map_.end() != fd_i && fd_i->cb != NULL) { + // This const_cast is necessary for LIST_HEAD_INSERT to work. Declaring + // entry mutable is insufficient because LIST_HEAD_INSERT assigns the + // forward pointer of the list head to the current cb_and_mask, and the + // compiler complains that it can't assign a const T* to a T*. + CBAndEventMask* cb_and_mask = const_cast<CBAndEventMask*>(&*fd_i); + // Note that there is no clearly correct behavior here when + // cb_and_mask->events_to_fake != 0 and this function is called. + // Of the two operations: + // cb_and_mask->events_to_fake = events_to_fake + // cb_and_mask->events_to_fake |= events_to_fake + // the first was picked because it discourages users from calling + // SetFDReady repeatedly to build up the correct event set as it is more + // efficient to call SetFDReady once with the correct, final mask. + cb_and_mask->events_to_fake = events_to_fake; + AddToReadyList(cb_and_mask); + } +} + +void SimpleEpollServer::SetFDNotReady(int fd) { + auto fd_i = cb_map_.find(CBAndEventMask(NULL, 0, fd)); + if (cb_map_.end() != fd_i) { + RemoveFromReadyList(*fd_i); + } +} + +bool SimpleEpollServer::IsFDReady(int fd) const { + auto fd_i = cb_map_.find(CBAndEventMask(NULL, 0, fd)); + return (cb_map_.end() != fd_i && fd_i->cb != NULL && + fd_i->entry.le_prev != NULL); +} + +void SimpleEpollServer::VerifyReadyList() const { + int count = 0; + CBAndEventMask* cur = ready_list_.lh_first; + for (; cur; cur = cur->entry.le_next) { + ++count; + } + for (cur = tmp_list_.lh_first; cur; cur = cur->entry.le_next) { + ++count; + } + CHECK_EQ(ready_list_size_, count) << "Ready list size does not match count"; +} + +void SimpleEpollServer::RegisterAlarm(int64_t timeout_time_in_us, AlarmCB* ac) { + EPOLL_VLOG(4) << "RegisteringAlarm " << ac << " at : " << timeout_time_in_us; + CHECK(ac); + if (all_alarms_.find(ac) != all_alarms_.end()) { + EPOLL_BUG << "Alarm already exists"; + } + + auto alarm_iter = alarm_map_.insert(std::make_pair(timeout_time_in_us, ac)); + + all_alarms_.insert(ac); + // Pass the iterator to the EpollAlarmCallbackInterface. + ac->OnRegistration(alarm_iter, this); +} + +// Unregister a specific alarm callback: iterator_token must be a +// valid iterator. The caller must ensure the validity of the iterator. +void SimpleEpollServer::UnregisterAlarm(const AlarmRegToken& iterator_token) { + AlarmCB* cb = iterator_token->second; + EPOLL_VLOG(4) << "UnregisteringAlarm " << cb; + alarm_map_.erase(iterator_token); + all_alarms_.erase(cb); + cb->OnUnregistration(); +} + +SimpleEpollServer::AlarmRegToken SimpleEpollServer::ReregisterAlarm( + SimpleEpollServer::AlarmRegToken iterator_token, + int64_t timeout_time_in_us) { + AlarmCB* cb = iterator_token->second; + alarm_map_.erase(iterator_token); + return alarm_map_.emplace(timeout_time_in_us, cb); +} + +int SimpleEpollServer::NumFDsRegistered() const { + DCHECK_GE(cb_map_.size(), 1u); + // Omit the internal FD (read_fd_) + return cb_map_.size() - 1; +} + +void SimpleEpollServer::Wake() { + char data = 'd'; // 'd' is for data. It's good enough for me. + int rv = write(write_fd_, &data, 1); + DCHECK_EQ(rv, 1); +} + +int64_t SimpleEpollServer::NowInUsec() const { return WallTimeNowInUsec(); } + +int64_t SimpleEpollServer::ApproximateNowInUsec() const { + if (recorded_now_in_us_ != 0) { + return recorded_now_in_us_; + } + return this->NowInUsec(); +} + +std::string SimpleEpollServer::EventMaskToString(int event_mask) { + std::string s; + if (event_mask & EPOLLIN) s += "EPOLLIN "; + if (event_mask & EPOLLPRI) s += "EPOLLPRI "; + if (event_mask & EPOLLOUT) s += "EPOLLOUT "; + if (event_mask & EPOLLRDNORM) s += "EPOLLRDNORM "; + if (event_mask & EPOLLRDBAND) s += "EPOLLRDBAND "; + if (event_mask & EPOLLWRNORM) s += "EPOLLWRNORM "; + if (event_mask & EPOLLWRBAND) s += "EPOLLWRBAND "; + if (event_mask & EPOLLMSG) s += "EPOLLMSG "; + if (event_mask & EPOLLERR) s += "EPOLLERR "; + if (event_mask & EPOLLHUP) s += "EPOLLHUP "; + if (event_mask & EPOLLONESHOT) s += "EPOLLONESHOT "; + if (event_mask & EPOLLET) s += "EPOLLET "; + return s; +} + +void SimpleEpollServer::LogStateOnCrash() { + EPOLL_LOG(ERROR) + << "-------------------Epoll Server-------------------------"; + EPOLL_LOG(ERROR) << "Epoll server " << this << " polling on fd " << epoll_fd_; + EPOLL_LOG(ERROR) << "timeout_in_us_: " << timeout_in_us_; + + // Log sessions with alarms. + EPOLL_LOG(ERROR) << alarm_map_.size() << " alarms registered."; + for (auto it = alarm_map_.begin(); it != alarm_map_.end(); ++it) { + const bool skipped = + alarms_reregistered_and_should_be_skipped_.find(it->second) != + alarms_reregistered_and_should_be_skipped_.end(); + EPOLL_LOG(ERROR) << "Alarm " << it->second << " registered at time " + << it->first << " and should be skipped = " << skipped; + } + + EPOLL_LOG(ERROR) << cb_map_.size() << " fd callbacks registered."; + for (auto it = cb_map_.begin(); it != cb_map_.end(); ++it) { + EPOLL_LOG(ERROR) << "fd: " << it->fd << " with mask " << it->event_mask + << " registered with cb: " << it->cb; + } + EPOLL_LOG(ERROR) + << "-------------------/Epoll Server------------------------"; +} + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +void SimpleEpollServer::DelFD(int fd) const { + struct epoll_event ee; + memset(&ee, 0, sizeof(ee)); +#ifdef EPOLL_SERVER_EVENT_TRACING + event_recorder_.RecordFDMaskEvent(fd, 0, "DelFD"); +#endif + if (epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, fd, &ee)) { + int saved_errno = errno; + char buf[kErrorBufferSize]; + EPOLL_LOG(FATAL) << "Epoll set removal error for fd " << fd << ": " + << strerror_r(saved_errno, buf, sizeof(buf)); + } +} + +//////////////////////////////////////// + +void SimpleEpollServer::AddFD(int fd, int event_mask) const { + struct epoll_event ee; + memset(&ee, 0, sizeof(ee)); + ee.events = event_mask | EPOLLERR | EPOLLHUP; + ee.data.fd = fd; +#ifdef EPOLL_SERVER_EVENT_TRACING + event_recorder_.RecordFDMaskEvent(fd, ee.events, "AddFD"); +#endif + if (epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, fd, &ee)) { + int saved_errno = errno; + char buf[kErrorBufferSize]; + EPOLL_LOG(FATAL) << "Epoll set insertion error for fd " << fd << ": " + << strerror_r(saved_errno, buf, sizeof(buf)); + } +} + +//////////////////////////////////////// + +void SimpleEpollServer::ModFD(int fd, int event_mask) const { + struct epoll_event ee; + memset(&ee, 0, sizeof(ee)); + ee.events = event_mask | EPOLLERR | EPOLLHUP; + ee.data.fd = fd; +#ifdef EPOLL_SERVER_EVENT_TRACING + event_recorder_.RecordFDMaskEvent(fd, ee.events, "ModFD"); +#endif + EPOLL_VLOG(3) << "modifying fd= " << fd << " " + << EventMaskToString(ee.events); + if (epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, fd, &ee)) { + int saved_errno = errno; + char buf[kErrorBufferSize]; + EPOLL_LOG(FATAL) << "Epoll set modification error for fd " << fd << ": " + << strerror_r(saved_errno, buf, sizeof(buf)); + } +} + +//////////////////////////////////////// + +void SimpleEpollServer::ModifyFD(int fd, int remove_event, int add_event) { + auto fd_i = cb_map_.find(CBAndEventMask(NULL, 0, fd)); + if (cb_map_.end() == fd_i) { + EPOLL_VLOG(2) << "Didn't find the fd " << fd << "in internal structures"; + return; + } + + if (fd_i->cb != NULL) { + int& event_mask = fd_i->event_mask; + EPOLL_VLOG(3) << "fd= " << fd + << " event_mask before: " << EventMaskToString(event_mask); + event_mask &= ~remove_event; + event_mask |= add_event; + + EPOLL_VLOG(3) << " event_mask after: " << EventMaskToString(event_mask); + + ModFD(fd, event_mask); + + fd_i->cb->OnModification(fd, event_mask); + } +} + +void SimpleEpollServer::WaitForEventsAndCallHandleEvents( + int64_t timeout_in_us, struct epoll_event events[], int events_size) { + if (timeout_in_us == 0 || ready_list_.lh_first != NULL) { + // If ready list is not empty, then don't sleep at all. + timeout_in_us = 0; + } else if (timeout_in_us < 0) { + EPOLL_LOG(INFO) << "Negative epoll timeout: " << timeout_in_us + << "us; epoll will wait forever for events."; + // If timeout_in_us is < 0 we are supposed to Wait forever. This means we + // should set timeout_in_us to -1000 so we will + // Wait(-1000/1000) == Wait(-1) == Wait forever. + timeout_in_us = -1000; + } else { + // If timeout is specified, and the ready list is empty. + if (timeout_in_us < 1000) { + timeout_in_us = 1000; + } + } + const int timeout_in_ms = timeout_in_us / 1000; + int64_t expected_wakeup_us = NowInUsec() + timeout_in_us; + + int nfds = epoll_wait_impl(epoll_fd_, events, events_size, timeout_in_ms); + EPOLL_VLOG(3) << "nfds=" << nfds; + +#ifdef EPOLL_SERVER_EVENT_TRACING + event_recorder_.RecordEpollWaitEvent(timeout_in_ms, nfds); +#endif + + // If you're wondering why the NowInUsec() is recorded here, the answer is + // simple: If we did it before the epoll_wait_impl, then the max error for + // the ApproximateNowInUs() call would be as large as the maximum length of + // epoll_wait, which can be arbitrarily long. Since this would make + // ApproximateNowInUs() worthless, we instead record the time -after- we've + // done epoll_wait, which guarantees that the maximum error is the amount of + // time it takes to process all the events generated by epoll_wait. + recorded_now_in_us_ = NowInUsec(); + + if (timeout_in_us > 0) { + int64_t delta = NowInUsec() - expected_wakeup_us; + last_delay_in_usec_ = delta > 0 ? delta : 0; + } else { + // timeout_in_us < 0 means we waited forever until an event; + // timeout_in_us == 0 means there was no kernel delay to track. + last_delay_in_usec_ = 0; + } + + if (nfds > 0) { + for (int i = 0; i < nfds; ++i) { + int event_mask = events[i].events; + int fd = events[i].data.fd; + HandleEvent(fd, event_mask); + } + } else if (nfds < 0) { + // Catch interrupted syscall and just ignore it and move on. + if (errno != EINTR && errno != 0) { + int saved_errno = errno; + char buf[kErrorBufferSize]; + EPOLL_LOG(FATAL) << "Error " << saved_errno << " in epoll_wait: " + << strerror_r(saved_errno, buf, sizeof(buf)); + } + } + + // Now run through the ready list. + if (ready_list_.lh_first) { + CallReadyListCallbacks(); + } +} + +void SimpleEpollServer::CallReadyListCallbacks() { + // Check pre-conditions. + DCHECK(tmp_list_.lh_first == NULL); + // Swap out the ready_list_ into the tmp_list_ before traversing the list to + // enable SetFDReady() to just push new items into the ready_list_. + std::swap(ready_list_.lh_first, tmp_list_.lh_first); + if (tmp_list_.lh_first) { + tmp_list_.lh_first->entry.le_prev = &tmp_list_.lh_first; + EpollEvent event(0); + while (tmp_list_.lh_first != NULL) { + DCHECK_GT(ready_list_size_, 0); + CBAndEventMask* cb_and_mask = tmp_list_.lh_first; + RemoveFromReadyList(*cb_and_mask); + + event.out_ready_mask = 0; + event.in_events = + cb_and_mask->events_asserted | cb_and_mask->events_to_fake; + // TODO(fenix): get rid of the two separate fields in cb_and_mask. + cb_and_mask->events_asserted = 0; + cb_and_mask->events_to_fake = 0; + { + // OnEvent() may call UnRegister, so we set in_use, here. Any + // UnRegister call will now simply set the cb to NULL instead of + // invalidating the cb_and_mask object (by deleting the object in the + // map to which cb_and_mask refers) + AutoReset<bool> in_use_guard(&(cb_and_mask->in_use), true); + cb_and_mask->cb->OnEvent(cb_and_mask->fd, &event); + } + + // Since OnEvent may have called UnregisterFD, we must check here that + // the callback is still valid. If it isn't, then UnregisterFD *was* + // called, and we should now get rid of the object. + if (cb_and_mask->cb == NULL) { + cb_map_.erase(*cb_and_mask); + } else if (event.out_ready_mask != 0) { + cb_and_mask->events_to_fake = event.out_ready_mask; + AddToReadyList(cb_and_mask); + } + } + } + DCHECK(tmp_list_.lh_first == NULL); +} + +void SimpleEpollServer::CallAndReregisterAlarmEvents() { + int64_t now_in_us = recorded_now_in_us_; + DCHECK_NE(0, recorded_now_in_us_); + + TimeToAlarmCBMap::iterator erase_it; + + // execute alarms. + for (auto i = alarm_map_.begin(); i != alarm_map_.end();) { + if (i->first > now_in_us) { + break; + } + AlarmCB* cb = i->second; + // Execute the OnAlarm() only if we did not register + // it in this loop itself. + const bool added_in_this_round = + alarms_reregistered_and_should_be_skipped_.find(cb) != + alarms_reregistered_and_should_be_skipped_.end(); + if (added_in_this_round) { + ++i; + continue; + } + all_alarms_.erase(cb); + const int64_t new_timeout_time_in_us = cb->OnAlarm(); + + erase_it = i; + ++i; + alarm_map_.erase(erase_it); + + if (new_timeout_time_in_us > 0) { + // We add to hash_set only if the new timeout is <= now_in_us. + // if timeout is > now_in_us then we have no fear that this alarm + // can be reexecuted in this loop, and hence we do not need to + // worry about a recursive loop. + EPOLL_DVLOG(3) << "Reregistering alarm " + << " " << cb << " " << new_timeout_time_in_us << " " + << now_in_us; + if (new_timeout_time_in_us <= now_in_us) { + alarms_reregistered_and_should_be_skipped_.insert(cb); + } + RegisterAlarm(new_timeout_time_in_us, cb); + } + } + alarms_reregistered_and_should_be_skipped_.clear(); +} + +EpollAlarm::EpollAlarm() : eps_(NULL), registered_(false) {} + +EpollAlarm::~EpollAlarm() { UnregisterIfRegistered(); } + +int64_t EpollAlarm::OnAlarm() { + registered_ = false; + return 0; +} + +void EpollAlarm::OnRegistration(const SimpleEpollServer::AlarmRegToken& token, + SimpleEpollServer* eps) { + DCHECK_EQ(false, registered_); + + token_ = token; + eps_ = eps; + registered_ = true; +} + +void EpollAlarm::OnUnregistration() { registered_ = false; } + +void EpollAlarm::OnShutdown(SimpleEpollServer* eps) { + registered_ = false; + eps_ = NULL; +} + +// If the alarm was registered, unregister it. +void EpollAlarm::UnregisterIfRegistered() { + if (!registered_) { + return; + } + + eps_->UnregisterAlarm(token_); +} + +void EpollAlarm::ReregisterAlarm(int64_t timeout_time_in_us) { + DCHECK(registered_); + token_ = eps_->ReregisterAlarm(token_, timeout_time_in_us); +} + +} // namespace epoll_server
diff --git a/epoll_server/simple_epoll_server.h b/epoll_server/simple_epoll_server.h new file mode 100644 index 0000000..78b6d79 --- /dev/null +++ b/epoll_server/simple_epoll_server.h
@@ -0,0 +1,1050 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef QUICHE_EPOLL_SERVER_H_ +#define QUICHE_EPOLL_SERVER_H_ + +#include <fcntl.h> +#include <stddef.h> +#include <stdint.h> +#include <sys/queue.h> + +#include <map> +#include <memory> +#include <string> +#include <unordered_map> +#include <unordered_set> +#include <vector> + +// #define EPOLL_SERVER_EVENT_TRACING 1 +// +// Defining EPOLL_SERVER_EVENT_TRACING +// causes code to exist which didn't before. +// This code tracks each event generated by the epollserver, +// as well as providing a per-fd-registered summary of +// events. Note that enabling this code vastly slows +// down operations, and uses substantially more +// memory. For these reasons, it should only be enabled by developers doing +// development at their workstations. +// +// A structure called 'EventRecorder' will exist when +// the macro is defined. See the EventRecorder class interface +// within the SimpleEpollServer class for more details. +#ifdef EPOLL_SERVER_EVENT_TRACING +#include <ostream> +#endif + +#include <sys/epoll.h> + +#include "net/third_party/quiche/src/epoll_server/platform/api/epoll_logging.h" + +namespace epoll_server { + +class SimpleEpollServer; +class EpollAlarmCallbackInterface; +class ReadPipeCallback; + +struct EpollEvent { + EpollEvent(int events) : in_events(events), out_ready_mask(0) {} + + int in_events; // incoming events + int out_ready_mask; // the new event mask for ready list (0 means don't + // get on the ready list). This field is always + // initialized to 0 when the event is passed to + // OnEvent. +}; + +// Callbacks which go into SimpleEpollServers are expected to derive from this +// class. +class EpollCallbackInterface { + public: + // Summary: + // Called when the callback is registered into a SimpleEpollServer. + // Args: + // eps - the poll server into which this callback was registered + // fd - the file descriptor which was registered + // event_mask - the event mask (composed of EPOLLIN, EPOLLOUT, etc) + // which was registered (and will initially be used + // in the epoll() calls) + virtual void OnRegistration(SimpleEpollServer* eps, int fd, + int event_mask) = 0; + + // Summary: + // Called when the event_mask is modified (for a file-descriptor) + // Args: + // fd - the file descriptor which was registered + // event_mask - the event mask (composed of EPOLLIN, EPOLLOUT, etc) + // which was is now curren (and will be used + // in subsequent epoll() calls) + virtual void OnModification(int fd, int event_mask) = 0; + + // Summary: + // Called whenever an event occurs on the file-descriptor. + // This is where the bulk of processing is expected to occur. + // Args: + // fd - the file descriptor which was registered + // event - a struct that contains the event mask (composed of EPOLLIN, + // EPOLLOUT, etc), a flag that indicates whether this is a true + // epoll_wait event vs one from the ready list, and an output + // parameter for OnEvent to inform the SimpleEpollServer whether to + // put this fd on the ready list. + virtual void OnEvent(int fd, EpollEvent* event) = 0; + + // Summary: + // Called when the file-descriptor is unregistered from the poll-server. + // Args: + // fd - the file descriptor which was registered, and of this call, is now + // unregistered. + // replaced - If true, this callback is being replaced by another, otherwise + // it is simply being removed. + virtual void OnUnregistration(int fd, bool replaced) = 0; + + // Summary: + // Called when the epoll server is shutting down. This is different from + // OnUnregistration because the subclass may want to clean up memory. + // This is called in leiu of OnUnregistration. + // Args: + // fd - the file descriptor which was registered. + virtual void OnShutdown(SimpleEpollServer* eps, int fd) = 0; + + // Summary: + // Returns a name describing the class for use in debug/error reporting. + virtual std::string Name() const = 0; + + virtual ~EpollCallbackInterface() {} + + protected: + EpollCallbackInterface() {} +}; + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +class SimpleEpollServer { + public: + typedef EpollAlarmCallbackInterface AlarmCB; + typedef EpollCallbackInterface CB; + + typedef std::multimap<int64_t, AlarmCB*> TimeToAlarmCBMap; + typedef TimeToAlarmCBMap::iterator AlarmRegToken; + + // Summary: + // Constructor: + // By default, we don't wait any amount of time for events, and + // we suggest to the epoll-system that we're going to use on-the-order + // of 1024 FDs. + SimpleEpollServer(); + + SimpleEpollServer(const SimpleEpollServer&) = delete; + SimpleEpollServer operator=(const SimpleEpollServer&) = delete; + + //////////////////////////////////////// + + // Destructor + virtual ~SimpleEpollServer(); + + //////////////////////////////////////// + + // Summary + // Register a callback to be called whenever an event contained + // in the set of events included in event_mask occurs on the + // file-descriptor 'fd' + // + // Note that only one callback is allowed to be registered for + // any specific file-decriptor. + // + // If a callback is registered for a file-descriptor which has already + // been registered, then the previous callback is unregistered with + // the 'replaced' flag set to true. I.e. the previous callback's + // OnUnregistration() function is called like so: + // OnUnregistration(fd, true); + // + // The epoll server does NOT take on ownership of the callback: the callback + // creator is responsible for managing that memory. + // + // Args: + // fd - a valid file-descriptor + // cb - an instance of a subclass of EpollCallbackInterface + // event_mask - a combination of (EPOLLOUT, EPOLLIN.. etc) indicating + // the events for which the callback would like to be + // called. + virtual void RegisterFD(int fd, CB* cb, int event_mask); + + //////////////////////////////////////// + + // Summary: + // A shortcut for RegisterFD which sets things up such that the + // callback is called when 'fd' is available for writing. + // Args: + // fd - a valid file-descriptor + // cb - an instance of a subclass of EpollCallbackInterface + virtual void RegisterFDForWrite(int fd, CB* cb); + + //////////////////////////////////////// + + // Summary: + // A shortcut for RegisterFD which sets things up such that the + // callback is called when 'fd' is available for reading or writing. + // Args: + // fd - a valid file-descriptor + // cb - an instance of a subclass of EpollCallbackInterface + virtual void RegisterFDForReadWrite(int fd, CB* cb); + + //////////////////////////////////////// + + // Summary: + // A shortcut for RegisterFD which sets things up such that the + // callback is called when 'fd' is available for reading. + // Args: + // fd - a valid file-descriptor + // cb - an instance of a subclass of EpollCallbackInterface + virtual void RegisterFDForRead(int fd, CB* cb); + + //////////////////////////////////////// + + // Summary: + // Removes the FD and the associated callback from the pollserver. + // If the callback is registered with other FDs, they will continue + // to be processed using the callback without modification. + // If the file-descriptor specified is not registered in the + // epoll_server, then nothing happens as a result of this call. + // Args: + // fd - the file-descriptor which should no-longer be monitored. + virtual void UnregisterFD(int fd); + + //////////////////////////////////////// + + // Summary: + // Modifies the event mask for the file-descriptor, replacing + // the old event_mask with the new one specified here. + // If the file-descriptor specified is not registered in the + // epoll_server, then nothing happens as a result of this call. + // Args: + // fd - the fd whose event mask should be modified. + // event_mask - the new event mask. + virtual void ModifyCallback(int fd, int event_mask); + + //////////////////////////////////////// + + // Summary: + // Modifies the event mask for the file-descriptor such that we + // no longer request events when 'fd' is readable. + // If the file-descriptor specified is not registered in the + // epoll_server, then nothing happens as a result of this call. + // Args: + // fd - the fd whose event mask should be modified. + virtual void StopRead(int fd); + + //////////////////////////////////////// + + // Summary: + // Modifies the event mask for the file-descriptor such that we + // request events when 'fd' is readable. + // If the file-descriptor specified is not registered in the + // epoll_server, then nothing happens as a result of this call. + // Args: + // fd - the fd whose event mask should be modified. + virtual void StartRead(int fd); + + //////////////////////////////////////// + + // Summary: + // Modifies the event mask for the file-descriptor such that we + // no longer request events when 'fd' is writable. + // If the file-descriptor specified is not registered in the + // epoll_server, then nothing happens as a result of this call. + // Args: + // fd - the fd whose event mask should be modified. + virtual void StopWrite(int fd); + + //////////////////////////////////////// + + // Summary: + // Modifies the event mask for the file-descriptor such that we + // request events when 'fd' is writable. + // If the file-descriptor specified is not registered in the + // epoll_server, then nothing happens as a result of this call. + // Args: + // fd - the fd whose event mask should be modified. + virtual void StartWrite(int fd); + + //////////////////////////////////////// + + // Summary: + // Looks up the callback associated with the file-descriptor 'fd'. + // If a callback is associated with this file-descriptor, then + // it's OnEvent() method is called with the file-descriptor 'fd', + // and event_mask 'event_mask' + // + // If no callback is registered for this file-descriptor, nothing + // will happen as a result of this call. + // + // This function is used internally by the SimpleEpollServer, but is + // available publicly so that events might be 'faked'. Calling + // this function with an fd and event_mask is equivalent (as far + // as the callback is concerned) to having a real event generated + // by epoll (except, of course, that read(), etc won't necessarily + // be able to read anything) + // Args: + // fd - the file-descriptor on which an event has occurred. + // event_mask - a bitmask representing the events which have occurred + // on/for this fd. This bitmask is composed of + // POLLIN, POLLOUT, etc. + // + void HandleEvent(int fd, int event_mask); + + // Summary: + // Call this when you want the pollserver to + // wait for events and execute the callbacks associated with + // the file-descriptors on which those events have occurred. + // Depending on the value of timeout_in_us_, this may or may + // not return immediately. Please reference the set_timeout() + // function for the specific behaviour. + virtual void WaitForEventsAndExecuteCallbacks(); + + // Summary: + // When an fd is registered to use edge trigger notification, the ready + // list can be used to simulate level trigger semantics. Edge trigger + // registration doesn't send an initial event, and only rising edge (going + // from blocked to unblocked) events are sent. A callback can put itself on + // the ready list by calling SetFDReady() after calling RegisterFD(). The + // OnEvent method of all callbacks associated with the fds on the ready + // list will be called immediately after processing the events returned by + // epoll_wait(). The fd is removed from the ready list before the + // callback's OnEvent() method is invoked. To stay on the ready list, the + // OnEvent() (or some function in that call chain) must call SetFDReady + // again. When a fd is unregistered using UnregisterFD(), the fd is + // automatically removed from the ready list. + // + // When the callback for a edge triggered fd hits the falling edge (about + // to block, either because of it got an EAGAIN, or had a short read/write + // operation), it should remove itself from the ready list using + // SetFDNotReady() (since OnEvent cannot distinguish between invocation + // from the ready list vs from a normal epoll event). All four ready list + // methods are safe to be called within the context of the callbacks. + // + // Since the ready list invokes EpollCallbackInterface::OnEvent, only fds + // that are registered with the SimpleEpollServer will be put on the ready + // list. SetFDReady() and SetFDNotReady() will do nothing if the + // SimpleEpollServer doesn't know about the fd passed in. + // + // Since the ready list cannot reliably determine proper set of events + // which should be sent to the callback, SetFDReady() requests the caller + // to provide the ready list with the event mask, which will be used later + // when OnEvent() is invoked by the ready list. Hence, the event_mask + // passedto SetFDReady() does not affect the actual epoll registration of + // the fd with the kernel. If a fd is already put on the ready list, and + // SetFDReady() is called again for that fd with a different event_mask, + // the event_mask will be updated. + virtual void SetFDReady(int fd, int events_to_fake); + + virtual void SetFDNotReady(int fd); + + // Summary: + // IsFDReady(), ReadyListSize(), and VerifyReadyList are intended as + // debugging tools and for writing unit tests. + // ISFDReady() returns whether a fd is in the ready list. + // ReadyListSize() returns the number of fds on the ready list. + // VerifyReadyList() checks the consistency of internal data structure. It + // will CHECK if it finds an error. + virtual bool IsFDReady(int fd) const; + + size_t ReadyListSize() const { return ready_list_size_; } + + void VerifyReadyList() const; + + //////////////////////////////////////// + + // Summary: + // Registers an alarm 'ac' to go off at time 'timeout_time_in_us'. + // If the callback returns a positive number from its OnAlarm() function, + // then the callback will be re-registered at that time, else the alarm + // owner is responsible for freeing up memory. + // + // Important: A give AlarmCB* can not be registered again if it is already + // registered. If a user wants to register a callback again it should first + // unregister the previous callback before calling RegisterAlarm again. + // Args: + // timeout_time_in_us - the absolute time at which the alarm should go off + // ac - the alarm which will be called. + virtual void RegisterAlarm(int64_t timeout_time_in_us, AlarmCB* ac); + + // Summary: + // Registers an alarm 'ac' to go off at time: (ApproximateNowInUs() + + // delta_in_us). While this is somewhat less accurate (see the description + // for ApproximateNowInUs() to see how 'approximate'), the error is never + // worse than the amount of time it takes to process all events in one + // WaitForEvents. As with 'RegisterAlarm()', if the callback returns a + // positive number from its OnAlarm() function, then the callback will be + // re-registered at that time, else the alarm owner is responsible for + // freeing up memory. + // Note that this function is purely a convienence. The + // same thing may be accomplished by using RegisterAlarm with + // ApproximateNowInUs() directly. + // + // Important: A give AlarmCB* can not be registered again if it is already + // registered. If a user wants to register a callback again it should first + // unregister the previous callback before calling RegisterAlarm again. + // Args: + // delta_in_us - the delta in microseconds from the ApproximateTimeInUs() at + // which point the alarm should go off. + // ac - the alarm which will be called. + void RegisterAlarmApproximateDelta(int64_t delta_in_us, AlarmCB* ac) { + RegisterAlarm(ApproximateNowInUsec() + delta_in_us, ac); + } + + //////////////////////////////////////// + + // Summary: + // Unregister the alarm referred to by iterator_token; Callers should + // be warned that a token may have become already invalid when OnAlarm() + // is called, was unregistered, or OnShutdown was called on that alarm. + // Args: + // iterator_token - iterator to the alarm callback to unregister. + virtual void UnregisterAlarm( + const SimpleEpollServer::AlarmRegToken& iterator_token); + + virtual SimpleEpollServer::AlarmRegToken ReregisterAlarm( + SimpleEpollServer::AlarmRegToken iterator_token, + int64_t timeout_time_in_us); + + //////////////////////////////////////// + + // Summary: + // returns the number of file-descriptors registered in this + // SimpleEpollServer. + // Returns: + // number of FDs registered (discounting the internal pipe used for Wake) + virtual int NumFDsRegistered() const; + + // Summary: + // Force the epoll server to wake up (by writing to an internal pipe). + virtual void Wake(); + + // Summary: + // Wrapper around WallTimer's NowInUsec. We do this so that we can test + // SimpleEpollServer without using the system clock (and can avoid the + // flakiness that would ensue) + // Returns: + // the current time as number of microseconds since the Unix epoch. + virtual int64_t NowInUsec() const; + + // Summary: + // Since calling NowInUsec() many thousands of times per + // WaitForEventsAndExecuteCallbacks function call is, to say the least, + // inefficient, we allow users to use an approximate time instead. The + // time returned from this function is as accurate as NowInUsec() when + // WaitForEventsAndExecuteCallbacks is not an ancestor of the caller's + // callstack. + // However, when WaitForEventsAndExecuteCallbacks -is- an ancestor, then + // this function returns the time at which the + // WaitForEventsAndExecuteCallbacks function started to process events or + // alarms. + // + // Essentially, this function makes available a fast and mostly accurate + // mechanism for getting the time for any function handling an event or + // alarm. When functions which are not handling callbacks or alarms call + // this function, they get the slow and "absolutely" accurate time. + // + // Users should be encouraged to use this function. + // Returns: + // the "approximate" current time as number of microseconds since the Unix + // epoch. + virtual int64_t ApproximateNowInUsec() const; + + static std::string EventMaskToString(int event_mask); + + // Summary: + // Logs the state of the epoll server with EPOLL_LOG(ERROR). + void LogStateOnCrash(); + + // Summary: + // Set the timeout to the value specified. + // If the timeout is set to a negative number, + // WaitForEventsAndExecuteCallbacks() will only return when an event has + // occurred + // If the timeout is set to zero, + // WaitForEventsAndExecuteCallbacks() will return immediately + // If the timeout is set to a positive number, + // WaitForEventsAndExecuteCallbacks() will return when an event has + // occurred, or when timeout_in_us microseconds has elapsed, whichever + // is first. + // Args: + // timeout_in_us - value specified depending on behaviour desired. + // See above. + void set_timeout_in_us(int64_t timeout_in_us) { + timeout_in_us_ = timeout_in_us; + } + + //////////////////////////////////////// + + // Summary: + // Accessor for the current value of timeout_in_us. + int timeout_in_us_for_test() const { return timeout_in_us_; } + + // Summary: + // Returns true when the SimpleEpollServer() is being destroyed. + bool in_shutdown() const { return in_shutdown_; } + + // Compatibility stub. + void Shutdown() {} + + // Summary: + // A function for implementing the ready list. It invokes OnEvent for each + // of the fd in the ready list, and takes care of adding them back to the + // ready list if the callback requests it (by checking that out_ready_mask + // is non-zero). + void CallReadyListCallbacks(); + + int64_t LastDelayInUsec() const { return last_delay_in_usec_; } + + protected: + virtual void SetNonblocking(int fd); + + // This exists here so that we can override this function in unittests + // in order to make effective mock SimpleEpollServer objects. + virtual int epoll_wait_impl(int epfd, struct epoll_event* events, + int max_events, int timeout_in_ms); + + // this struct is used internally, and is never used by anything external + // to this class. Some of its members are declared mutable to get around the + // restriction imposed by hash_set. Since hash_set knows nothing about the + // objects it stores, it has to assume that every bit of the object is used + // in the hash function and equal_to comparison. Thus hash_set::iterator is a + // const iterator. In this case, the only thing that must stay constant is + // fd. Everything else are just along for the ride and changing them doesn't + // compromise the hash_set integrity. + struct CBAndEventMask { + CBAndEventMask() + : cb(NULL), + fd(-1), + event_mask(0), + events_asserted(0), + events_to_fake(0), + in_use(false) { + entry.le_next = NULL; + entry.le_prev = NULL; + } + + CBAndEventMask(EpollCallbackInterface* cb, int event_mask, int fd) + : cb(cb), + fd(fd), + event_mask(event_mask), + events_asserted(0), + events_to_fake(0), + in_use(false) { + entry.le_next = NULL; + entry.le_prev = NULL; + } + + // Required operator for hash_set. Normally operator== should be a free + // standing function. However, since CBAndEventMask is a protected type and + // it will never be a base class, it makes no difference. + bool operator==(const CBAndEventMask& cb_and_mask) const { + return fd == cb_and_mask.fd; + } + // A callback. If the fd is unregistered inside the callchain of OnEvent, + // the cb will be set to NULL. + mutable EpollCallbackInterface* cb; + + mutable LIST_ENTRY(CBAndEventMask) entry; + // file descriptor registered with the epoll server. + int fd; + // the current event_mask registered for this callback. + mutable int event_mask; + // the event_mask that was returned by epoll + mutable int events_asserted; + // the event_mask for the ready list to use to call OnEvent. + mutable int events_to_fake; + // toggle around calls to OnEvent to tell UnregisterFD to not erase the + // iterator because HandleEvent is using it. + mutable bool in_use; + }; + + // Custom hash function to be used by hash_set. + struct CBAndEventMaskHash { + size_t operator()(const CBAndEventMask& cb_and_eventmask) const { + return static_cast<size_t>(cb_and_eventmask.fd); + } + }; + + using FDToCBMap = std::unordered_set<CBAndEventMask, CBAndEventMaskHash>; + + // the following four functions are OS-specific, and are likely + // to be changed in a subclass if the poll/select method is changed + // from epoll. + + // Summary: + // Deletes a file-descriptor from the set of FDs that should be + // monitored with epoll. + // Note that this only deals with modifying data relating -directly- + // with the epoll call-- it does not modify any data within the + // epoll_server. + // Args: + // fd - the file descriptor to-be-removed from the monitoring set + virtual void DelFD(int fd) const; + + //////////////////////////////////////// + + // Summary: + // Adds a file-descriptor to the set of FDs that should be + // monitored with epoll. + // Note that this only deals with modifying data relating -directly- + // with the epoll call. + // Args: + // fd - the file descriptor to-be-added to the monitoring set + // event_mask - the event mask (consisting of EPOLLIN, EPOLLOUT, etc + // OR'd together) which will be associated with this + // FD initially. + virtual void AddFD(int fd, int event_mask) const; + + //////////////////////////////////////// + + // Summary: + // Modifies a file-descriptor in the set of FDs that should be + // monitored with epoll. + // Note that this only deals with modifying data relating -directly- + // with the epoll call. + // Args: + // fd - the file descriptor to-be-added to the monitoring set + // event_mask - the event mask (consisting of EPOLLIN, EPOLLOUT, etc + // OR'd together) which will be associated with this + // FD after this call. + virtual void ModFD(int fd, int event_mask) const; + + //////////////////////////////////////// + + // Summary: + // Modified the event mask associated with an FD in the set of + // data needed by epoll. + // Events are removed before they are added, thus, if ~0 is put + // in 'remove_event', whatever is put in 'add_event' will be + // the new event mask. + // If the file-descriptor specified is not registered in the + // epoll_server, then nothing happens as a result of this call. + // Args: + // fd - the file descriptor whose event mask is to be modified + // remove_event - the events which are to be removed from the current + // event_mask + // add_event - the events which are to be added to the current event_mask + // + // + virtual void ModifyFD(int fd, int remove_event, int add_event); + + //////////////////////////////////////// + + // Summary: + // Waits for events, and calls HandleEvents() for each + // fd, event pair discovered to possibly have an event. + // Note that a callback (B) may get a spurious event if + // another callback (A) has closed a file-descriptor N, and + // the callback (B) has a newly opened file-descriptor, which + // also happens to be N. + virtual void WaitForEventsAndCallHandleEvents(int64_t timeout_in_us, + struct epoll_event events[], + int events_size); + + // Summary: + // An internal function for implementing the ready list. It adds a fd's + // CBAndEventMask to the ready list. If the fd is already on the ready + // list, it is a no-op. + void AddToReadyList(CBAndEventMask* cb_and_mask); + + // Summary: + // An internal function for implementing the ready list. It remove a fd's + // CBAndEventMask from the ready list. If the fd is not on the ready list, + // it is a no-op. + void RemoveFromReadyList(const CBAndEventMask& cb_and_mask); + + // Summary: + // Calls any pending alarms that should go off and reregisters them if they + // were recurring. + virtual void CallAndReregisterAlarmEvents(); + + // The file-descriptor created for epolling + int epoll_fd_; + + // The mapping of file-descriptor to CBAndEventMasks + FDToCBMap cb_map_; + + // Custom hash function to be used by hash_set. + struct AlarmCBHash { + size_t operator()(AlarmCB* const& p) const { + return reinterpret_cast<size_t>(p); + } + }; + + // TODO(sushantj): Having this hash_set is avoidable. We currently have it + // only so that we can enforce stringent checks that a caller can not register + // the same alarm twice. One option is to have an implementation in which + // this hash_set is used only in the debug mode. + using AlarmCBMap = std::unordered_set<AlarmCB*, AlarmCBHash>; + AlarmCBMap all_alarms_; + + TimeToAlarmCBMap alarm_map_; + + // The amount of time in microseconds that we'll wait before returning + // from the WaitForEventsAndExecuteCallbacks() function. + // If this is positive, wait that many microseconds. + // If this is negative, wait forever, or for the first event that occurs + // If this is zero, never wait for an event. + int64_t timeout_in_us_; + + // This is nonzero only after the invocation of epoll_wait_impl within + // WaitForEventsAndCallHandleEvents and before the function + // WaitForEventsAndExecuteCallbacks returns. At all other times, this is + // zero. This enables us to have relatively accurate time returned from the + // ApproximateNowInUs() function. See that function for more details. + int64_t recorded_now_in_us_; + + // This is used to implement CallAndReregisterAlarmEvents. This stores + // all alarms that were reregistered because OnAlarm() returned a + // value > 0 and the time at which they should be executed is less that + // the current time. By storing such alarms in this map we ensure + // that while calling CallAndReregisterAlarmEvents we do not call + // OnAlarm on any alarm in this set. This ensures that we do not + // go in an infinite loop. + AlarmCBMap alarms_reregistered_and_should_be_skipped_; + + LIST_HEAD(ReadyList, CBAndEventMask) ready_list_; + LIST_HEAD(TmpList, CBAndEventMask) tmp_list_; + int ready_list_size_; + // TODO(alyssar): make this into something that scales up. + static const int events_size_ = 256; + struct epoll_event events_[256]; + +#ifdef EPOLL_SERVER_EVENT_TRACING + struct EventRecorder { + public: + EventRecorder() : num_records_(0), record_threshold_(10000) {} + + ~EventRecorder() { Clear(); } + + // When a number of events equals the record threshold, + // the collected data summary for all FDs will be written + // to EPOLL_LOG(INFO). Note that this does not include the + // individual events (if you'reinterested in those, you'll + // have to get at them programmatically). + // After any such flushing to EPOLL_LOG(INFO) all events will + // be cleared. + // Note that the definition of an 'event' is a bit 'hazy', + // as it includes the 'Unregistration' event, and perhaps + // others. + void set_record_threshold(int64_t new_threshold) { + record_threshold_ = new_threshold; + } + + void Clear() { + for (int i = 0; i < debug_events_.size(); ++i) { + delete debug_events_[i]; + } + debug_events_.clear(); + unregistered_fds_.clear(); + event_counts_.clear(); + } + + void MaybeRecordAndClear() { + ++num_records_; + if ((num_records_ > record_threshold_) && (record_threshold_ > 0)) { + EPOLL_LOG(INFO) << "\n" << *this; + num_records_ = 0; + Clear(); + } + } + + void RecordFDMaskEvent(int fd, int mask, const char* function) { + FDMaskOutput* fdmo = new FDMaskOutput(fd, mask, function); + debug_events_.push_back(fdmo); + MaybeRecordAndClear(); + } + + void RecordEpollWaitEvent(int timeout_in_ms, int num_events_generated) { + EpollWaitOutput* ewo = + new EpollWaitOutput(timeout_in_ms, num_events_generated); + debug_events_.push_back(ewo); + MaybeRecordAndClear(); + } + + void RecordEpollEvent(int fd, int event_mask) { + Events& events_for_fd = event_counts_[fd]; + events_for_fd.AssignFromMask(event_mask); + MaybeRecordAndClear(); + } + + friend ostream& operator<<(ostream& os, const EventRecorder& er) { + for (int i = 0; i < er.unregistered_fds_.size(); ++i) { + os << "fd: " << er.unregistered_fds_[i] << "\n"; + os << er.unregistered_fds_[i]; + } + for (EventCountsMap::const_iterator i = er.event_counts_.begin(); + i != er.event_counts_.end(); ++i) { + os << "fd: " << i->first << "\n"; + os << i->second; + } + for (int i = 0; i < er.debug_events_.size(); ++i) { + os << *(er.debug_events_[i]) << "\n"; + } + return os; + } + + void RecordUnregistration(int fd) { + EventCountsMap::iterator i = event_counts_.find(fd); + if (i != event_counts_.end()) { + unregistered_fds_.push_back(i->second); + event_counts_.erase(i); + } + MaybeRecordAndClear(); + } + + protected: + class DebugOutput { + public: + friend ostream& operator<<(ostream& os, const DebugOutput& debug_output) { + debug_output.OutputToStream(os); + return os; + } + virtual void OutputToStream(ostream* os) const = 0; + virtual ~DebugOutput() {} + }; + + class FDMaskOutput : public DebugOutput { + public: + FDMaskOutput(int fd, int mask, const char* function) + : fd_(fd), mask_(mask), function_(function) {} + virtual void OutputToStream(ostream* os) const { + (*os) << "func: " << function_ << "\tfd: " << fd_; + if (mask_ != 0) { + (*os) << "\tmask: " << EventMaskToString(mask_); + } + } + int fd_; + int mask_; + const char* function_; + }; + + class EpollWaitOutput : public DebugOutput { + public: + EpollWaitOutput(int timeout_in_ms, int num_events_generated) + : timeout_in_ms_(timeout_in_ms), + num_events_generated_(num_events_generated) {} + virtual void OutputToStream(ostream* os) const { + (*os) << "timeout_in_ms: " << timeout_in_ms_ + << "\tnum_events_generated: " << num_events_generated_; + } + + protected: + int timeout_in_ms_; + int num_events_generated_; + }; + + struct Events { + Events() + : epoll_in(0), + epoll_pri(0), + epoll_out(0), + epoll_rdnorm(0), + epoll_rdband(0), + epoll_wrnorm(0), + epoll_wrband(0), + epoll_msg(0), + epoll_err(0), + epoll_hup(0), + epoll_oneshot(0), + epoll_et(0) {} + + void AssignFromMask(int event_mask) { + if (event_mask & EPOLLIN) ++epoll_in; + if (event_mask & EPOLLPRI) ++epoll_pri; + if (event_mask & EPOLLOUT) ++epoll_out; + if (event_mask & EPOLLRDNORM) ++epoll_rdnorm; + if (event_mask & EPOLLRDBAND) ++epoll_rdband; + if (event_mask & EPOLLWRNORM) ++epoll_wrnorm; + if (event_mask & EPOLLWRBAND) ++epoll_wrband; + if (event_mask & EPOLLMSG) ++epoll_msg; + if (event_mask & EPOLLERR) ++epoll_err; + if (event_mask & EPOLLHUP) ++epoll_hup; + if (event_mask & EPOLLONESHOT) ++epoll_oneshot; + if (event_mask & EPOLLET) ++epoll_et; + } + + friend ostream& operator<<(ostream& os, const Events& ev) { + if (ev.epoll_in) { + os << "\t EPOLLIN: " << ev.epoll_in << "\n"; + } + if (ev.epoll_pri) { + os << "\t EPOLLPRI: " << ev.epoll_pri << "\n"; + } + if (ev.epoll_out) { + os << "\t EPOLLOUT: " << ev.epoll_out << "\n"; + } + if (ev.epoll_rdnorm) { + os << "\t EPOLLRDNORM: " << ev.epoll_rdnorm << "\n"; + } + if (ev.epoll_rdband) { + os << "\t EPOLLRDBAND: " << ev.epoll_rdband << "\n"; + } + if (ev.epoll_wrnorm) { + os << "\t EPOLLWRNORM: " << ev.epoll_wrnorm << "\n"; + } + if (ev.epoll_wrband) { + os << "\t EPOLLWRBAND: " << ev.epoll_wrband << "\n"; + } + if (ev.epoll_msg) { + os << "\t EPOLLMSG: " << ev.epoll_msg << "\n"; + } + if (ev.epoll_err) { + os << "\t EPOLLERR: " << ev.epoll_err << "\n"; + } + if (ev.epoll_hup) { + os << "\t EPOLLHUP: " << ev.epoll_hup << "\n"; + } + if (ev.epoll_oneshot) { + os << "\t EPOLLONESHOT: " << ev.epoll_oneshot << "\n"; + } + if (ev.epoll_et) { + os << "\t EPOLLET: " << ev.epoll_et << "\n"; + } + return os; + } + + unsigned int epoll_in; + unsigned int epoll_pri; + unsigned int epoll_out; + unsigned int epoll_rdnorm; + unsigned int epoll_rdband; + unsigned int epoll_wrnorm; + unsigned int epoll_wrband; + unsigned int epoll_msg; + unsigned int epoll_err; + unsigned int epoll_hup; + unsigned int epoll_oneshot; + unsigned int epoll_et; + }; + + std::vector<DebugOutput*> debug_events_; + std::vector<Events> unregistered_fds_; + using EventCountsMap = std::unordered_map<int, Events>; + EventCountsMap event_counts_; + int64_t num_records_; + int64_t record_threshold_; + }; + + void ClearEventRecords() { event_recorder_.Clear(); } + void WriteEventRecords(ostream* os) const { (*os) << event_recorder_; } + + mutable EventRecorder event_recorder_; + +#endif + + private: + // Helper functions used in the destructor. + void CleanupFDToCBMap(); + void CleanupTimeToAlarmCBMap(); + + // The callback registered to the fds below. As the purpose of their + // registration is to wake the epoll server it just clears the pipe and + // returns. + std::unique_ptr<ReadPipeCallback> wake_cb_; + + // A pipe owned by the epoll server. The server will be registered to listen + // on read_fd_ and can be woken by Wake() which writes to write_fd_. + int read_fd_; + int write_fd_; + + // This boolean is checked to see if it is false at the top of the + // WaitForEventsAndExecuteCallbacks function. If not, then it either returns + // without doing work, and logs to ERROR, or aborts the program (in + // DEBUG mode). If so, then it sets the bool to true, does work, and + // sets it back to false when done. This catches unwanted recursion. + bool in_wait_for_events_and_execute_callbacks_; + + // Returns true when the SimpleEpollServer() is being destroyed. + bool in_shutdown_; + int64_t last_delay_in_usec_; +}; + +class EpollAlarmCallbackInterface { + public: + // Summary: + // Called when an alarm times out. Invalidates an AlarmRegToken. + // WARNING: If a token was saved to refer to an alarm callback, OnAlarm must + // delete it, as the reference is no longer valid. + // Returns: + // the unix time (in microseconds) at which this alarm should be signaled + // again, or 0 if the alarm should be removed. + virtual int64_t OnAlarm() = 0; + + // Summary: + // Called when the an alarm is registered. Invalidates an AlarmRegToken. + // Args: + // token: the iterator to the alarm registered in the alarm map. + // WARNING: this token becomes invalid when the alarm fires, is + // unregistered, or OnShutdown is called on that alarm. + // eps: the epoll server the alarm is registered with. + virtual void OnRegistration(const SimpleEpollServer::AlarmRegToken& token, + SimpleEpollServer* eps) = 0; + + // Summary: + // Called when the an alarm is unregistered. + // WARNING: It is not valid to unregister a callback and then use the token + // that was saved to refer to the callback. + virtual void OnUnregistration() = 0; + + // Summary: + // Called when the epoll server is shutting down. + // Invalidates the AlarmRegToken that was given when this alarm was + // registered. + virtual void OnShutdown(SimpleEpollServer* eps) = 0; + + virtual ~EpollAlarmCallbackInterface() {} + + protected: + EpollAlarmCallbackInterface() {} +}; + +// A simple alarm which unregisters itself on destruction. +// +// PLEASE NOTE: +// Any classes overriding these functions must either call the implementation +// of the parent class, or is must otherwise make sure that the 'registered_' +// boolean and the token, 'token_', are updated appropriately. +class EpollAlarm : public EpollAlarmCallbackInterface { + public: + EpollAlarm(); + + ~EpollAlarm() override; + + // Marks the alarm as unregistered and returns 0. The return value may be + // safely ignored by subclasses. + int64_t OnAlarm() override; + + // Marks the alarm as registered, and stores the token. + void OnRegistration(const SimpleEpollServer::AlarmRegToken& token, + SimpleEpollServer* eps) override; + + // Marks the alarm as unregistered. + void OnUnregistration() override; + + // Marks the alarm as unregistered. + void OnShutdown(SimpleEpollServer* eps) override; + + // If the alarm was registered, unregister it. + void UnregisterIfRegistered(); + + // Reregisters the alarm at specified time. + void ReregisterAlarm(int64_t timeout_time_in_us); + + bool registered() const { return registered_; } + + const SimpleEpollServer* eps() const { return eps_; } + + private: + SimpleEpollServer::AlarmRegToken token_; + SimpleEpollServer* eps_; + bool registered_; +}; + +} // namespace epoll_server + +#endif // QUICHE_EPOLL_SERVER_H_
diff --git a/epoll_server/simple_epoll_server_test.cc b/epoll_server/simple_epoll_server_test.cc new file mode 100644 index 0000000..140eb2b --- /dev/null +++ b/epoll_server/simple_epoll_server_test.cc
@@ -0,0 +1,2517 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Epoll tests which determine that the right things happen in the right order. +// Also lots of testing of individual functions. + +#include "net/third_party/quiche/src/epoll_server/simple_epoll_server.h" + +#include <errno.h> +#include <fcntl.h> +#include <netinet/in.h> +#include <sys/epoll.h> +#include <sys/socket.h> +#include <unistd.h> + +#include <algorithm> +#include <cstddef> +#include <cstdlib> +#include <cstring> +#include <hash_map> +#include <memory> +#include <string> +#include <utility> +#include <vector> + +#include "net/third_party/quiche/src/epoll_server/fake_simple_epoll_server.h" +#include "net/third_party/quiche/src/epoll_server/platform/api/epoll_address_test_utils.h" +#include "net/third_party/quiche/src/epoll_server/platform/api/epoll_expect_bug.h" +#include "net/third_party/quiche/src/epoll_server/platform/api/epoll_test.h" +#include "net/third_party/quiche/src/epoll_server/platform/api/epoll_thread.h" +#include "net/third_party/quiche/src/epoll_server/platform/api/epoll_time.h" + +namespace epoll_server { + +namespace test { + +namespace { + +const int kPageSize = 4096; +const int kMaxBufLen = 10000; + +// These are used to record what is happening. +enum { + CREATION, + REGISTRATION, + MODIFICATION, + EVENT, + UNREGISTRATION, + SHUTDOWN, + DESTRUCTION +}; + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +struct RecordEntry { + RecordEntry() : time(0), instance(nullptr), event_type(0), fd(0), data(0) {} + + RecordEntry(int64_t time, void* instance, int event_type, int fd, int data) + : time(time), + instance(instance), + event_type(event_type), + fd(fd), + data(data) {} + + int64_t time; + void* instance; + int event_type; + int fd; + int data; + + bool IsEqual(const RecordEntry *entry) const { + bool retval = true; + + if (instance != entry->instance) { + retval = false; + EPOLL_LOG(INFO) << " instance (" << instance << ") != entry->instance(" + << entry->instance << ")"; + } + if (event_type != entry->event_type) { + retval = false; + EPOLL_LOG(INFO) << " event_type (" << event_type + << ") != entry->event_type(" << entry->event_type << ")"; + } + if ( fd != entry->fd ) { + retval = false; + EPOLL_LOG(INFO) << " fd (" << fd << ") != entry->fd (" << entry->fd + << ")"; + } + if (data != entry->data) { + retval = false; + EPOLL_LOG(INFO) << " data (" << data << ") != entry->data(" << entry->data + << ")"; + } + return retval; + } +}; + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +class Recorder { + public: + void Record(void* instance, int event_type, int fd, int data) { + records_.push_back( + RecordEntry(WallTimeNowInUsec(), instance, event_type, fd, data)); + } + + const std::vector<RecordEntry> *records() const { return &records_; } + + bool IsEqual(const Recorder *recorder) const { + const std::vector<RecordEntry> *records = recorder->records(); + + if (records_.size() != records->size()) { + EPOLL_LOG(INFO) << "records_.size() (" << records_.size() + << ") != records->size() (" << records->size() << ")"; + return false; + } + for (size_t i = 0; i < std::min(records_.size(), records->size()); ++i) { + if (!records_[i].IsEqual(&(*records)[i])) { + EPOLL_LOG(INFO) << "entry in index: " << i + << " differs from recorder."; + return false; + } + } + return true; + } + + private: + std::vector<RecordEntry> records_; +}; + + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +class RecordingCB : public EpollCallbackInterface { + public: + RecordingCB() : recorder_(new Recorder()) { + recorder_->Record(this, CREATION, 0, 0); + } + + ~RecordingCB() override { + recorder_->Record(this, DESTRUCTION, 0, 0); + delete recorder_; + } + + void OnRegistration(SimpleEpollServer* eps, int fd, int event_mask) override { + recorder_->Record(this, REGISTRATION, fd, event_mask); + } + + void OnModification(int fd, int event_mask) override { + recorder_->Record(this, MODIFICATION, fd, event_mask); + } + + void OnEvent(int fd, EpollEvent* event) override { + recorder_->Record(this, EVENT, fd, event->in_events); + if (event->in_events & EPOLLIN) { + const int kLength = 1024; + char buf[kLength]; + read(fd, &buf, kLength); + } + } + + void OnUnregistration(int fd, bool replaced) override { + recorder_->Record(this, UNREGISTRATION, fd, replaced); + } + + void OnShutdown(SimpleEpollServer* eps, int fd) override { + if (fd >= 0) { + eps->UnregisterFD(fd); + } + recorder_->Record(this, SHUTDOWN, fd, 0); + } + + string Name() const override { return "RecordingCB"; } + + const Recorder* recorder() const { return recorder_; } + + protected: + Recorder* recorder_; +}; + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +// A simple test server that adds some test functions to SimpleEpollServer as +// well as allowing access to protected functions. +class EpollTestServer : public SimpleEpollServer { + public: + EpollTestServer() : SimpleEpollServer() {} + + ~EpollTestServer() override {} + + void CheckMapping(int fd, CB* cb) { + CBAndEventMask tmp; + tmp.fd = fd; + FDToCBMap::iterator fd_i = cb_map_.find(tmp); + CHECK(fd_i != cb_map_.end()); // Chokes CHECK_NE. + CHECK(fd_i->cb == cb); + } + + void CheckNotMapped(int fd) { + CBAndEventMask tmp; + tmp.fd = fd; + FDToCBMap::iterator fd_i = cb_map_.find(tmp); + CHECK(fd_i == cb_map_.end()); // Chokes CHECK_EQ. + } + + void CheckEventMask(int fd, int event_mask) { + CBAndEventMask tmp; + tmp.fd = fd; + FDToCBMap::iterator fd_i = cb_map_.find(tmp); + CHECK(cb_map_.end() != fd_i); // Chokes CHECK_NE. + CHECK_EQ(fd_i->event_mask, event_mask); + } + + void CheckNotRegistered(int fd) { + struct epoll_event ee; + memset(&ee, 0, sizeof(ee)); + // If the fd is registered, the epoll_ctl call would succeed (return 0) and + // the CHECK would fail. + CHECK(epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, fd, &ee)); + } + + size_t GetNumPendingAlarmsForTest() const { return alarm_map_.size(); } + + bool ContainsAlarm(AlarmCB* ac) { + return all_alarms_.find(ac) != all_alarms_.end(); + } + + using SimpleEpollServer::WaitForEventsAndCallHandleEvents; +}; + +class EpollFunctionTest : public EpollTest { + public: + EpollFunctionTest() + : fd_(-1), fd2_(-1), recorder_(nullptr), cb_(nullptr), ep_(nullptr) { + } + + ~EpollFunctionTest() override { + delete ep_; + delete cb_; + } + + void SetUp() override { + ep_ = new EpollTestServer(); + cb_ = new RecordingCB(); + // recorder_ is safe to use directly as we know it has the same scope as + // cb_ + recorder_ = cb_->recorder(); + + int pipe_fds[2]; + if (pipe(pipe_fds) < 0) { + PLOG(FATAL) << "pipe() failed"; + } + fd_ = pipe_fds[0]; + fd2_ = pipe_fds[1]; + } + + void TearDown() override { + close(fd_); + close(fd2_); + } + + void DeleteSimpleEpollServer() { + delete ep_; + ep_ = nullptr; + } + + int fd() { return fd_; } + int fd2() { return fd2_; } + EpollTestServer* ep() { return ep_; } + EpollCallbackInterface* cb() { return cb_; } + const Recorder* recorder() { return recorder_; } + + private: + int fd_; + int fd2_; + const Recorder *recorder_; + RecordingCB* cb_; + EpollTestServer* ep_; +}; + +TEST_F(EpollFunctionTest, TestUnconnectedSocket) { + int fd = socket(AddressFamilyUnderTest(), SOCK_STREAM, IPPROTO_TCP); + ep()->RegisterFD(fd, cb(), EPOLLIN | EPOLLOUT); + ep()->WaitForEventsAndExecuteCallbacks(); + + Recorder tmp; + tmp.Record(cb(), CREATION, 0, 0); + tmp.Record(cb(), REGISTRATION, fd, EPOLLIN | EPOLLOUT); + tmp.Record(cb(), EVENT, fd, EPOLLOUT | EPOLLHUP); + EXPECT_TRUE(recorder()->IsEqual(&tmp)); +} + +TEST_F(EpollFunctionTest, TestRegisterFD) { + // Check that the basic register works. + ep()->RegisterFD(fd(), cb(), EPOLLIN); + + // Make sure that the fd-CB mapping is there. + ep()->CheckMapping(fd(), cb()); + ep()->CheckEventMask(fd(), EPOLLIN); + + // Now make sure that if we register again, we stomp the old callback. + // Also make sure we handle O_NONBLOCK correctly + RecordingCB cb2; + ep()->RegisterFD(fd(), &cb2, EPOLLOUT | O_NONBLOCK); + ep()->CheckMapping(fd(), &cb2); + ep()->CheckEventMask(fd(), EPOLLOUT | O_NONBLOCK); + + // Clean up. + ep()->UnregisterFD(fd()); +} + +TEST_F(EpollFunctionTest, TestRegisterFDForWrite) { + ep()->RegisterFDForWrite(fd(), cb()); + ep()->CheckMapping(fd(), cb()); + ep()->CheckEventMask(fd(), EPOLLOUT); + + // Clean up. + ep()->UnregisterFD(fd()); +} + +TEST_F(EpollFunctionTest, TestRegisterFDForReadWrite) { + ep()->RegisterFDForReadWrite(fd(), cb()); + ep()->CheckMapping(fd(), cb()); + ep()->CheckEventMask(fd(), EPOLLIN | EPOLLOUT); + + // Clean up. + ep()->UnregisterFD(fd()); +} + +TEST_F(EpollFunctionTest, TestRegisterFDForRead) { + ep()->RegisterFDForRead(fd(), cb()); + ep()->CheckMapping(fd(), cb()); + ep()->CheckEventMask(fd(), EPOLLIN); + + ep()->UnregisterFD(fd()); +} + +TEST_F(EpollFunctionTest, TestUnregisterFD) { + ep()->RegisterFDForRead(fd(), cb()); + ep()->CheckMapping(fd(), cb()); + ep()->CheckEventMask(fd(), EPOLLIN); + + // Unregister and make sure that it's gone. + ep()->UnregisterFD(fd()); + ep()->CheckNotMapped(fd()); + ep()->CheckNotRegistered(fd()); + + // And make sure that unregistering something a second time doesn't cause + // crashes. + ep()->UnregisterFD(fd()); + ep()->CheckNotMapped(fd()); + ep()->CheckNotRegistered(fd()); +} + +TEST_F(EpollFunctionTest, TestModifyCallback) { + // Check that nothing terrible happens if we modify an unregistered fd. + ep()->ModifyCallback(fd(), EPOLLOUT); + ep()->CheckNotMapped(fd()); + ep()->CheckNotRegistered(fd()); + + // Check that the basic register works. + ep()->RegisterFD(fd(), cb(), EPOLLIN); + ep()->CheckMapping(fd(), cb()); + ep()->CheckEventMask(fd(), EPOLLIN); + + // Check that adding a signal swaps it out for the first. + ep()->ModifyCallback(fd(), EPOLLOUT); + ep()->CheckMapping(fd(), cb()); + ep()->CheckEventMask(fd(), EPOLLOUT); + + // Check that modifying from X to X works correctly. + ep()->ModifyCallback(fd(), EPOLLOUT); + ep()->CheckMapping(fd(), cb()); + ep()->CheckEventMask(fd(), EPOLLOUT); + + // Check that modifying from something to nothing works. + ep()->ModifyCallback(fd(), 0); + ep()->CheckMapping(fd(), cb()); + ep()->CheckEventMask(fd(), 0); + + ep()->UnregisterFD(fd()); +} + +TEST_F(EpollFunctionTest, TestStopRead) { + ep()->RegisterFDForReadWrite(fd(), cb()); + ep()->CheckMapping(fd(), cb()); + ep()->CheckEventMask(fd(), EPOLLIN | EPOLLOUT); + + // Unregister and make sure you only lose the read event. + ep()->StopRead(fd()); + ep()->CheckMapping(fd(), cb()); + ep()->CheckEventMask(fd(), EPOLLOUT); + + ep()->UnregisterFD(fd()); +} + +TEST_F(EpollFunctionTest, TestStartRead) { + ep()->RegisterFDForWrite(fd(), cb()); + ep()->CheckMapping(fd(), cb()); + ep()->CheckEventMask(fd(), EPOLLOUT); + + // Make sure that StartRead adds EPOLLIN and doesn't remove other signals. + ep()->StartRead(fd()); + ep()->CheckMapping(fd(), cb()); + ep()->CheckEventMask(fd(), EPOLLIN | EPOLLOUT); + + // Clean up. + ep()->UnregisterFD(fd()); +} + +TEST_F(EpollFunctionTest, TestStopWrite) { + ep()->RegisterFDForReadWrite(fd(), cb()); + ep()->CheckMapping(fd(), cb()); + ep()->CheckEventMask(fd(), EPOLLIN | EPOLLOUT); + + // Unregister write and make sure you only lose the write event. + ep()->StopWrite(fd()); + ep()->CheckMapping(fd(), cb()); + ep()->CheckEventMask(fd(), EPOLLIN); + + ep()->UnregisterFD(fd()); +} + +TEST_F(EpollFunctionTest, TestStartWrite) { + ep()->RegisterFDForRead(fd(), cb()); + ep()->CheckMapping(fd(), cb()); + ep()->CheckEventMask(fd(), EPOLLIN); + + // Make sure that StartWrite adds EPOLLOUT and doesn't remove other + // signals. + ep()->StartWrite(fd()); + ep()->CheckMapping(fd(), cb()); + ep()->CheckEventMask(fd(), EPOLLIN | EPOLLOUT); + + // Clean up. + ep()->UnregisterFD(fd()); +} + +TEST_F(EpollFunctionTest, TestSet_timeout_in_us) { + // Check that set works with various values. There's a separate test below + // to make sure the values are used properly. + ep()->set_timeout_in_us(10); + EXPECT_EQ(10, ep()->timeout_in_us_for_test()); + + ep()->set_timeout_in_us(-1); + EXPECT_EQ(-1, ep()->timeout_in_us_for_test()); +} + +TEST_F(EpollFunctionTest, TestHandleEvent) { + const std::vector<RecordEntry> *records = recorder()->records(); + + // Test that nothing bad happens if the FD is not in the map. + ep()->HandleEvent(fd(), EPOLLOUT); + ep()->CallReadyListCallbacks(); + + ep()->RegisterFD(fd(), cb(), 0); + ep()->CheckMapping(fd(), cb()); + ep()->CheckEventMask(fd(), 0); + + // At this point we should have creation and registration recorded. + EXPECT_EQ(2, records->size()); + + // Call handle event and make sure something was recorded. + ep()->HandleEvent(fd(), EPOLLOUT); + ep()->CallReadyListCallbacks(); + EXPECT_EQ(3, records->size()); + + // Call handle event and make sure something was recorded. + ep()->HandleEvent(fd(), EPOLLIN | O_NONBLOCK); + ep()->CallReadyListCallbacks(); + EXPECT_EQ(4, records->size()); + + Recorder tmp; + tmp.Record(cb(), CREATION, 0, 0); + tmp.Record(cb(), REGISTRATION, fd(), 0); + tmp.Record(cb(), EVENT, fd(), EPOLLOUT); + tmp.Record(cb(), EVENT, fd(), EPOLLIN | O_NONBLOCK); + + EXPECT_TRUE(recorder()->IsEqual(&tmp)); + ep()->UnregisterFD(fd()); +} + +TEST_F(EpollFunctionTest, TestNumFDsRegistered) { + EXPECT_EQ(0, ep()->NumFDsRegistered()); + + ep()->RegisterFD(fd(), cb(), 0); + EXPECT_EQ(1, ep()->NumFDsRegistered()); + + ep()->RegisterFD(fd2(), cb(), 0); + EXPECT_EQ(2, ep()->NumFDsRegistered()); + + ep()->RegisterFD(fd2(), cb(), 0); + EXPECT_EQ(2, ep()->NumFDsRegistered()); + + ep()->UnregisterFD(fd2()); + EXPECT_EQ(1, ep()->NumFDsRegistered()); + + ep()->UnregisterFD(fd()); + EXPECT_EQ(0, ep()->NumFDsRegistered()); +} + +// Check all of the individual signals and 1-2 combinations. +TEST_F(EpollFunctionTest, TestEventMaskToString) { + string test; + + test = SimpleEpollServer::EventMaskToString(EPOLLIN); + EXPECT_EQ(test, "EPOLLIN "); + + test = SimpleEpollServer::EventMaskToString(EPOLLOUT); + EXPECT_EQ(test, "EPOLLOUT "); + + test = SimpleEpollServer::EventMaskToString(EPOLLPRI); + EXPECT_EQ(test, "EPOLLPRI "); + + test = SimpleEpollServer::EventMaskToString(EPOLLERR); + EXPECT_EQ(test, "EPOLLERR "); + + test = SimpleEpollServer::EventMaskToString(EPOLLHUP); + EXPECT_EQ(test, "EPOLLHUP "); + + test = SimpleEpollServer::EventMaskToString(EPOLLHUP | EPOLLIN); + EXPECT_EQ(test, "EPOLLIN EPOLLHUP "); + + test = SimpleEpollServer::EventMaskToString(EPOLLIN | EPOLLOUT); + EXPECT_EQ(test, "EPOLLIN EPOLLOUT "); +} + +class TestAlarm : public EpollAlarmCallbackInterface { + public: + TestAlarm() + : time_before_next_alarm_(-1), + was_called_(false), + num_called_(0), + absolute_time_(false), + onshutdown_called_(false), + has_token_(false), + eps_(nullptr) { + } + ~TestAlarm() override { + } + int64_t OnAlarm() override { + has_token_ = false; + was_called_ = true; + ++num_called_; + if (time_before_next_alarm_ < 0) { + return 0; + } + if (absolute_time_) { + return time_before_next_alarm_; + } else { + return WallTimeNowInUsec() + time_before_next_alarm_; + } + } + + void OnShutdown(SimpleEpollServer* eps) override { + onshutdown_called_ = true; + has_token_ = false; + } + void OnRegistration(const SimpleEpollServer::AlarmRegToken& token, + SimpleEpollServer* eps) override { + has_token_ = true; + last_token_ = token; + eps_ = eps; + } + void OnUnregistration() override { + has_token_ = false; + } + + void UnregisterIfRegistered(SimpleEpollServer* eps) { + if (has_token_) { + eps->UnregisterAlarm(last_token_); + } + } + + void ReregisterAlarm(int64_t timeout_in_us) { + CHECK(has_token_); + eps_->ReregisterAlarm(last_token_, timeout_in_us); + } + + void Reset() { + time_before_next_alarm_ = -1; + was_called_ = false; + absolute_time_ = false; + } + + bool was_called() const { return was_called_; } + int num_called() const { return num_called_; } + + void set_time_before_next_alarm(int64_t time) { + time_before_next_alarm_ = time; + } + void set_absolute_time(bool absolute) { + absolute_time_ = absolute; + } + bool onshutdown_called() { return onshutdown_called_; } + + protected: + int64_t time_before_next_alarm_; + bool was_called_; + int num_called_; + // Is time_before_next_alarm relative to the current time or absolute? + bool absolute_time_; + bool onshutdown_called_; + bool has_token_; + SimpleEpollServer::AlarmRegToken last_token_; + SimpleEpollServer* eps_; +}; + +class TestChildAlarm; + +// This node unregister all other alarms when it receives +// OnShutdown() from any one child. +class TestParentAlarm { + public: + void OnShutdown(TestChildAlarm* child, SimpleEpollServer* eps) { + // Unregister + for (ChildTokenMap::const_iterator it = child_tokens_.begin(); + it != child_tokens_.end(); ++it) { + if (it->first != child) { + eps->UnregisterAlarm(it->second); + } + } + child_tokens_.clear(); + } + + void OnRegistration(TestChildAlarm* child, + const SimpleEpollServer::AlarmRegToken& token) { + child_tokens_[child] = token; + } + + protected: + typedef __gnu_cxx::hash_map<TestChildAlarm*, SimpleEpollServer::AlarmRegToken> + ChildTokenMap; + + ChildTokenMap child_tokens_; +}; + +class TestChildAlarm : public TestAlarm { + public: + void set_parent(TestParentAlarm* tp) { parent_ = tp; } + void OnShutdown(SimpleEpollServer* eps) override { + onshutdown_called_ = true; + // Inform parent of shutdown + parent_->OnShutdown(this, eps); + } + void OnRegistration(const SimpleEpollServer::AlarmRegToken& token, + SimpleEpollServer* eps) override { + parent_->OnRegistration(this, token); + } + + protected: + TestParentAlarm* parent_; +}; + +class TestAlarmThatRegistersAnotherAlarm : public TestAlarm { + public: + TestAlarmThatRegistersAnotherAlarm() + : alarm_(nullptr), + reg_time_delta_usec_(0), + eps_to_register_(nullptr), + has_reg_alarm_(false) {} + void SetRegisterAlarm(TestAlarm* alarm, int64_t time_delta_usec, + SimpleEpollServer* eps) { + alarm_ = alarm; + reg_time_delta_usec_ = time_delta_usec; + has_reg_alarm_ = true; + eps_to_register_ = eps; + } + int64_t OnAlarm() override { + if (has_reg_alarm_) { + eps_to_register_->RegisterAlarm( + eps_to_register_->ApproximateNowInUsec() + reg_time_delta_usec_, + alarm_); + has_reg_alarm_ = false; + } + return TestAlarm::OnAlarm(); + } + + protected: + TestAlarm* alarm_; + int64_t reg_time_delta_usec_; + SimpleEpollServer* eps_to_register_; + bool has_reg_alarm_; +}; + +class TestAlarmThatRegistersAndReregistersAnotherAlarm : public TestAlarm { + public: + TestAlarmThatRegistersAndReregistersAnotherAlarm() + : alarm_(nullptr), + reg_time_delta_usec_(0), + reregister_time_delta_usec_(0), + eps_to_register_(nullptr), + has_reg_alarm_(false) {} + void SetRegisterAndReregisterAlarm(TestAlarm* alarm, int64_t time_delta_usec, + int64_t reregister_delta_usec, + SimpleEpollServer* eps) { + alarm_ = alarm; + reg_time_delta_usec_ = time_delta_usec; + reregister_time_delta_usec_ = reregister_delta_usec; + has_reg_alarm_ = true; + eps_to_register_ = eps; + } + int64_t OnAlarm() override { + if (has_reg_alarm_) { + eps_to_register_->RegisterAlarm( + eps_to_register_->ApproximateNowInUsec() + reg_time_delta_usec_, + alarm_); + alarm_->ReregisterAlarm(eps_to_register_->ApproximateNowInUsec() + + reregister_time_delta_usec_); + has_reg_alarm_ = false; + } + return TestAlarm::OnAlarm(); + } + + protected: + TestAlarm* alarm_; + int64_t reg_time_delta_usec_; + int64_t reregister_time_delta_usec_; + SimpleEpollServer* eps_to_register_; + bool has_reg_alarm_; +}; + +class TestAlarmThatUnregistersAnotherAlarm : public TestAlarm { + public: + TestAlarmThatUnregistersAnotherAlarm() + : alarm_(nullptr), eps_to_register_(nullptr), has_unreg_alarm_(false) {} + void SetUnregisterAlarm(TestAlarm* alarm, SimpleEpollServer* eps) { + alarm_ = alarm; + has_unreg_alarm_ = true; + eps_to_register_ = eps; + } + int64_t OnAlarm() override { + if (has_unreg_alarm_) { + has_unreg_alarm_ = false; + alarm_->UnregisterIfRegistered(eps_to_register_); + } + return TestAlarm::OnAlarm(); + } + + protected: + TestAlarm* alarm_; + SimpleEpollServer* eps_to_register_; + bool has_unreg_alarm_; +}; + +class TestAlarmUnregister : public TestAlarm { + public: + TestAlarmUnregister() + : onunregistration_called_(false), + iterator_token_(nullptr) { + } + ~TestAlarmUnregister() override { + delete iterator_token_; + } + + void OnShutdown(SimpleEpollServer* eps) override { + onshutdown_called_ = true; + } + + int64_t OnAlarm() override { + delete iterator_token_; + iterator_token_ = nullptr; + + return TestAlarm::OnAlarm(); + } + + void OnRegistration(const SimpleEpollServer::AlarmRegToken& token, + SimpleEpollServer* eps) override { + // Multiple iterator tokens are not maintained by this code, + // so we should have reset the iterator_token in OnAlarm or + // OnUnregistration. + CHECK(iterator_token_ == nullptr); + iterator_token_ = new SimpleEpollServer::AlarmRegToken(token); + } + void OnUnregistration() override { + delete iterator_token_; + iterator_token_ = nullptr; + // Make sure that this alarm was not already unregistered. + CHECK(onunregistration_called_ == false); + onunregistration_called_ = true; + } + + bool onunregistration_called() { return onunregistration_called_; } + // Returns true if the token has been filled in with the saved iterator + // and false if it has not. + bool get_token(SimpleEpollServer::AlarmRegToken* token) { + if (iterator_token_ != nullptr) { + *token = *iterator_token_; + return true; + } else { + return false; + } + } + + protected: + bool onunregistration_called_; + SimpleEpollServer::AlarmRegToken* iterator_token_; +}; + +void WaitForAlarm(SimpleEpollServer* eps, const TestAlarm& alarm) { + for (int i = 0; i < 5; ++i) { + // Ideally we would only have to call this once but it could wake up a bit + // early and so not call the alarm. If it wakes up early several times + // there is something wrong. + eps->WaitForEventsAndExecuteCallbacks(); + if (alarm.was_called()) { + break; + } + } +} + +// Check a couple of alarm times to make sure they're falling within a +// reasonable range. +TEST(SimpleEpollServerTest, TestAlarms) { + EpollTestServer ep; + TestAlarm alarm; + + int alarm_time = 10; + + // Register an alarm and make sure we wait long enough to hit it. + ep.set_timeout_in_us(alarm_time * 1000 * 2); + ep.RegisterAlarm(WallTimeNowInUsec() + alarm_time, &alarm); + EXPECT_EQ(1, ep.GetNumPendingAlarmsForTest()); + WaitForAlarm(&ep, alarm); + EXPECT_TRUE(alarm.was_called()); + EXPECT_EQ(0, ep.GetNumPendingAlarmsForTest()); + alarm.Reset(); + + // Test a different time just to be careful. + alarm_time = 20; + ep.set_timeout_in_us(alarm_time * 1000 * 2); + ep.RegisterAlarm(WallTimeNowInUsec() + alarm_time, &alarm); + EXPECT_EQ(1, ep.GetNumPendingAlarmsForTest()); + WaitForAlarm(&ep, alarm); + EXPECT_TRUE(alarm.was_called()); + alarm.Reset(); + + // The alarm was a one-time thing. Make sure that we don't hit it again. + EXPECT_EQ(0, ep.GetNumPendingAlarmsForTest()); + ep.WaitForEventsAndExecuteCallbacks(); + EXPECT_FALSE(alarm.was_called()); + alarm.Reset(); +} + +// Same as above, but using RegisterAlarmApproximateDelta. +TEST(SimpleEpollServerTest, TestRegisterAlarmApproximateDelta) { + EpollTestServer ep; + TestAlarm alarm; + + int alarm_time = 10; + + // Register an alarm and make sure we wait long enough to hit it. + ep.set_timeout_in_us(alarm_time * 1000 * 2); + ep.RegisterAlarmApproximateDelta(alarm_time * 1000, &alarm); + EXPECT_EQ(1, ep.GetNumPendingAlarmsForTest()); + WaitForAlarm(&ep, alarm); + EXPECT_TRUE(alarm.was_called()); + EXPECT_EQ(0, ep.GetNumPendingAlarmsForTest()); + alarm.Reset(); + int64_t first_now = ep.ApproximateNowInUsec(); + EXPECT_LT(0, first_now); + + // Test a different time just to be careful. + alarm_time = 20; + ep.set_timeout_in_us(alarm_time * 1000 * 2); + ep.RegisterAlarmApproximateDelta(alarm_time * 1000, &alarm); + EXPECT_EQ(1, ep.GetNumPendingAlarmsForTest()); + WaitForAlarm(&ep, alarm); + EXPECT_TRUE(alarm.was_called()); + alarm.Reset(); + int64_t second_now = ep.ApproximateNowInUsec(); + + EXPECT_LT(first_now, second_now); + + + // The alarm was a one-time thing. Make sure that we don't hit it again. + EXPECT_EQ(0, ep.GetNumPendingAlarmsForTest()); + ep.WaitForEventsAndExecuteCallbacks(); + EXPECT_FALSE(alarm.was_called()); + alarm.Reset(); +} + +TEST(SimpleEpollServerTest, TestAlarmsWithInfiniteWait) { + EpollTestServer ep; + TestAlarm alarm; + + int alarm_time = 10; + + // Register an alarm and make sure we wait long enough to hit it. + ep.set_timeout_in_us(-1); + ep.RegisterAlarm(WallTimeNowInUsec() + alarm_time, &alarm); + EXPECT_EQ(1, ep.GetNumPendingAlarmsForTest()); + WaitForAlarm(&ep, alarm); + EXPECT_TRUE(alarm.was_called()); + EXPECT_EQ(0, ep.GetNumPendingAlarmsForTest()); + alarm.Reset(); +} + +// In this test we have an alarm that when fires gets re-registered +// at almost the same time at which it fires. Here, we want to make +// sure that when the alarm gets re-registered we do not call OnAlarm() +// on the same Alarm object again, until we have called +// WaitForEventsAndExecuteCallbacks(). A poor implementation of epoll +// server alarm handling can potentially cause OnAlarm() to be called +// multiple times. We make sure that the epoll server is not going in +// an infinite loop by checking that OnAlarm() is called exactly once +// on the alarm object that got registered again. +TEST(SimpleEpollServerTest, TestAlarmsThatGetReRegisteredAreNotCalledTwice) { + // This alarm would get registered again + TestAlarm alarm; + TestAlarm alarm2; + EpollTestServer ep; + ep.set_timeout_in_us(-1); + + int64_t alarm_time = 10; + int64_t abs_time = WallTimeNowInUsec() + alarm_time * 1000; + + // This will make the alarm re-register when OnAlarm is called. + alarm.set_absolute_time(true); + alarm.set_time_before_next_alarm(abs_time + 2); + + // Register two alarms and make sure we wait long enough to hit it. + ep.RegisterAlarm(abs_time, &alarm); + ep.RegisterAlarm(abs_time, &alarm2); + EXPECT_EQ(2, ep.GetNumPendingAlarmsForTest()); + + WaitForAlarm(&ep, alarm); + + EXPECT_TRUE(alarm.was_called()); + // Make sure that alarm is called only once. + EXPECT_EQ(1, alarm.num_called()); + EXPECT_EQ(1, ep.GetNumPendingAlarmsForTest()); + alarm.Reset(); +} + +// Here we make sure that when one alarm unregisters another alarm +// (that is supposed to be registered again because its OnAlarm +// returned > 0), the alarm thats supposed to be unregistered does +// actually gets unregistered. +TEST(SimpleEpollServerTest, TestAlarmsOneOnAlarmUnRegistersAnotherAlarm) { + TestAlarm alarm; + TestAlarmThatUnregistersAnotherAlarm alarm2; + EpollTestServer ep; + ep.set_timeout_in_us(-1); + + int64_t alarm_time = 1; + int64_t abs_time = WallTimeNowInUsec() + alarm_time * 1000; + + // This will make the alarm re-register when OnAlarm is called. + alarm.set_absolute_time(true); + alarm.set_time_before_next_alarm(abs_time + 2); + + + // Register two alarms and make sure we wait long enough to hit it. + ep.RegisterAlarm(abs_time, &alarm); + // This would cause us to unregister alarm when OnAlarm is called + // on alarm2. + alarm2.SetUnregisterAlarm(&alarm, &ep); + ep.RegisterAlarm(abs_time + 1, &alarm2); + EXPECT_EQ(2, ep.GetNumPendingAlarmsForTest()); + + WaitForAlarm(&ep, alarm); + + EXPECT_TRUE(alarm.was_called()); + // Make sure that alarm is called only once. + EXPECT_EQ(1, alarm.num_called()); + EXPECT_EQ(0, ep.GetNumPendingAlarmsForTest()); + alarm.Reset(); +} + +// Check a couple of alarm times to make sure they're falling within a +// reasonable range. +TEST(SimpleEpollServerTest, TestRepeatAlarms) { + EpollTestServer ep; + TestAlarm alarm; + + int alarm_time = 20; + + // Register an alarm and make sure we wait long enough to hit it. + ep.set_timeout_in_us(alarm_time * 1000 * 2); + alarm.set_time_before_next_alarm(1000*alarm_time); + ep.RegisterAlarm(WallTimeNowInUsec() + alarm_time, &alarm); + EXPECT_EQ(1, ep.GetNumPendingAlarmsForTest()); + + WaitForAlarm(&ep, alarm); + // When we wake up it should be because the Alarm has been called, and has + // registered itself to be called again. + + // Make sure the first alarm was called properly. + EXPECT_TRUE(alarm.was_called()); + + // Resetting means that the alarm is no longer a recurring alarm. It will be + // called once more and then stop. + alarm.Reset(); + + // Make sure the alarm is called one final time. + EXPECT_EQ(1, ep.GetNumPendingAlarmsForTest()); + ep.set_timeout_in_us(alarm_time * 1000 * 2); + WaitForAlarm(&ep, alarm); + + EXPECT_TRUE(alarm.was_called()); + alarm.Reset(); + + // The alarm was a one-time thing. Make sure that we don't hit it again. + EXPECT_EQ(0, ep.GetNumPendingAlarmsForTest()); + ep.WaitForEventsAndExecuteCallbacks(); + EXPECT_FALSE(alarm.was_called()); +} + +// Verify that an alarm that repeats itself in the past works properly. +TEST(SimpleEpollServerTest, TestRepeatAlarmInPast) { + EpollTestServer ep; + TestAlarm alarm; + + int64_t alarm_time = 20; + int64_t abs_time = WallTimeNowInUsec() + alarm_time * 1000; + + // Make the alarm re-register in the past when OnAlarm is called. + alarm.set_absolute_time(true); + alarm.set_time_before_next_alarm(abs_time - 1000); + + // Register the alarm and make sure we wait long enough to hit it. + ep.set_timeout_in_us(alarm_time * 1000 * 2); + ep.RegisterAlarm(abs_time, &alarm); + EXPECT_EQ(1, ep.GetNumPendingAlarmsForTest()); + + WaitForAlarm(&ep, alarm); + // When we wake up it should be because the Alarm has been called, and has + // registered itself to be called again. + + // Make sure the first alarm was called properly. + EXPECT_TRUE(alarm.was_called()); + + // Resetting means that the alarm is no longer a recurring alarm. It will be + // called once more and then stop. + alarm.Reset(); + + // Make sure the alarm is called one final time. + EXPECT_EQ(1, ep.GetNumPendingAlarmsForTest()); + ep.set_timeout_in_us(alarm_time * 1000 * 2); + WaitForAlarm(&ep, alarm); + + EXPECT_TRUE(alarm.was_called()); + alarm.Reset(); + + // The alarm was a one-time thing. Make sure that we don't hit it again. + EXPECT_EQ(0, ep.GetNumPendingAlarmsForTest()); + ep.WaitForEventsAndExecuteCallbacks(); + EXPECT_FALSE(alarm.was_called()); +} + +class EpollTestAlarms : public SimpleEpollServer { + public: + EpollTestAlarms() : SimpleEpollServer() {} + + inline int64_t NowInUsec() const override { return time_; } + + void CallAndReregisterAlarmEvents() override { + recorded_now_in_us_ = NowInUsec(); + SimpleEpollServer::CallAndReregisterAlarmEvents(); + } + + void set_time(int64_t time) { time_ = time; } + + size_t GetNumPendingAlarmsForTest() const { return alarm_map_.size(); } + + private: + int64_t time_; +}; + +// Test multiple interleaving alarms to make sure they work right. +// Pattern is roughly: +// time: 15 20 30 40 +// alarm: A B A' C +TEST(SimpleEpollServerTest, TestMultipleAlarms) { + EpollTestAlarms ep; + TestAlarm alarmA; + TestAlarm alarmB; + TestAlarm alarmC; + + ep.set_timeout_in_us(50 * 1000 * 2); + alarmA.set_time_before_next_alarm(1000 * 30); + alarmA.set_absolute_time(true); + ep.RegisterAlarm(15 * 1000, &alarmA); + ep.RegisterAlarm(20 * 1000, &alarmB); + ep.RegisterAlarm(40 * 1000, &alarmC); + + ep.set_time(15 * 1000); + ep.CallAndReregisterAlarmEvents(); // A + EXPECT_TRUE(alarmA.was_called()); + EXPECT_FALSE(alarmB.was_called()); + EXPECT_FALSE(alarmC.was_called()); + alarmA.Reset(); // Unregister A in the future. + + ep.set_time(20 * 1000); + ep.CallAndReregisterAlarmEvents(); // B + EXPECT_FALSE(alarmA.was_called()); + EXPECT_TRUE(alarmB.was_called()); + EXPECT_FALSE(alarmC.was_called()); + alarmB.Reset(); + + ep.set_time(30 * 1000); + ep.CallAndReregisterAlarmEvents(); // A + EXPECT_TRUE(alarmA.was_called()); + EXPECT_FALSE(alarmB.was_called()); + EXPECT_FALSE(alarmC.was_called()); + alarmA.Reset(); + + ep.set_time(40 * 1000); + ep.CallAndReregisterAlarmEvents(); // C + EXPECT_FALSE(alarmA.was_called()); + EXPECT_FALSE(alarmB.was_called()); + EXPECT_TRUE(alarmC.was_called()); + alarmC.Reset(); + + ep.CallAndReregisterAlarmEvents(); // None. + EXPECT_FALSE(alarmA.was_called()); + EXPECT_FALSE(alarmB.was_called()); + EXPECT_FALSE(alarmC.was_called()); +} + +TEST(SimpleEpollServerTest, TestAlarmOnShutdown) { + TestAlarm alarm1; + { + EpollTestServer ep; + const int64_t now = WallTimeNowInUsec(); + ep.RegisterAlarm(now + 5000, &alarm1); + } + + EXPECT_TRUE(alarm1.onshutdown_called()); +} + +// Tests that if we have multiple alarms +// OnShutdown then we handle them properly. +TEST(SimpleEpollServerTest, TestMultipleAlarmOnShutdown) { + TestAlarm alarm1; + TestAlarm alarm2; + TestAlarm alarm3; + { + EpollTestServer ep; + const int64_t now = WallTimeNowInUsec(); + ep.RegisterAlarm(now + 5000, &alarm1); + ep.RegisterAlarm(now + 9000, &alarm2); + ep.RegisterAlarm(now + 9000, &alarm3); + } + + EXPECT_TRUE(alarm1.onshutdown_called()); + EXPECT_TRUE(alarm2.onshutdown_called()); + EXPECT_TRUE(alarm3.onshutdown_called()); +} +TEST(SimpleEpollServerTest, TestMultipleAlarmUnregistrationOnShutdown) { + TestParentAlarm tp; + TestChildAlarm alarm1; + TestChildAlarm alarm2; + alarm1.set_parent(&tp); + alarm2.set_parent(&tp); + { + EpollTestServer ep; + const int64_t now = WallTimeNowInUsec(); + ep.RegisterAlarm(now + 5000, &alarm1); + ep.RegisterAlarm(now + 9000, &alarm2); + } + + EXPECT_TRUE(alarm1.onshutdown_called()); + EXPECT_FALSE(alarm2.onshutdown_called()); +} + +// Check an alarm set in the past runs right away. +TEST(SimpleEpollServerTest, TestPastAlarm) { + EpollTestServer ep; + TestAlarm alarm; + + // Register the alarm and make sure we wait long enough to hit it. + ep.set_timeout_in_us(1000 * 2); + ep.RegisterAlarm(WallTimeNowInUsec() - 1000, &alarm); + EXPECT_EQ(1, ep.GetNumPendingAlarmsForTest()); + ep.WaitForEventsAndExecuteCallbacks(); + EXPECT_TRUE(alarm.was_called()); + EXPECT_EQ(0, ep.GetNumPendingAlarmsForTest()); + alarm.Reset(); +} + +// Test Unregistering of Alarms +TEST(SimpleEpollServerTest, TestUnregisterAlarm) { + EpollTestServer ep; + SimpleEpollServer::AlarmRegToken temptok; + + TestAlarmUnregister alarm1; + TestAlarmUnregister alarm2; + + ep.RegisterAlarm(WallTimeNowInUsec() + 5 * 1000, &alarm1); + ep.RegisterAlarm(WallTimeNowInUsec() + 13 * 1000, &alarm2); + + // Unregister an alarm. + if (alarm2.get_token(&temptok)) { + ep.UnregisterAlarm(temptok); + } + EXPECT_EQ(1, ep.GetNumPendingAlarmsForTest()); + EXPECT_TRUE(alarm2.onunregistration_called()); + + if (alarm1.get_token(&temptok)) { + ep.UnregisterAlarm(temptok); + } + EXPECT_EQ(0, ep.GetNumPendingAlarmsForTest()); + EXPECT_TRUE(alarm1.onunregistration_called()); +} + +// Test Reregistering of Alarms +TEST(SimpleEpollServerTest, TestReregisterAlarm) { + EpollTestAlarms ep; + SimpleEpollServer::AlarmRegToken token; + + TestAlarmUnregister alarm; + ep.set_time(1000); + ep.RegisterAlarm(5000, &alarm); + + EXPECT_EQ(1, ep.GetNumPendingAlarmsForTest()); + ASSERT_TRUE(alarm.get_token(&token)); + ep.ReregisterAlarm(token, 6000); + EXPECT_EQ(1, ep.GetNumPendingAlarmsForTest()); + + ep.set_time(5000); + ep.set_timeout_in_us(0); + ep.CallAndReregisterAlarmEvents(); + EXPECT_FALSE(alarm.was_called()); + + ep.set_time(6000); + ep.CallAndReregisterAlarmEvents(); + EXPECT_TRUE(alarm.was_called()); +} + +TEST(SimpleEpollServerTest, TestReregisterDeferredAlarm) { + EpollTestAlarms ep; + ep.set_timeout_in_us(0); + + TestAlarm alarm; + TestAlarmThatRegistersAndReregistersAnotherAlarm register_alarm; + // Register the alarm in the past so it is added as a deferred alarm. + register_alarm.SetRegisterAndReregisterAlarm(&alarm, -500, 500, &ep); + ep.set_time(1000); + ep.RegisterAlarm(1000, ®ister_alarm); + // Call reregister twice, first to run register_alarm and second to run any + // scheduled deferred alarms. + ep.CallAndReregisterAlarmEvents(); + ep.CallAndReregisterAlarmEvents(); + + EXPECT_EQ(1, ep.GetNumPendingAlarmsForTest()); + EXPECT_FALSE(alarm.was_called()); + + ep.set_time(1500); + ep.CallAndReregisterAlarmEvents(); + EXPECT_TRUE(alarm.was_called()); +} + +// Check if an alarm fired and got reregistered, you are able to +// unregister the second registration. +TEST(SimpleEpollServerTest, TestFiredReregisteredAlarm) { + EpollTestAlarms ep; + TestAlarmUnregister alarmA; + + SimpleEpollServer::AlarmRegToken first_token; + SimpleEpollServer::AlarmRegToken second_token; + bool found; + + ep.set_timeout_in_us(50 * 1000 * 2); + alarmA.set_time_before_next_alarm(1000 * 30); + alarmA.set_absolute_time(true); + +// Alarm A first fires at 15, then 30 + ep.RegisterAlarm(15 * 1000, &alarmA); + + found = alarmA.get_token(&first_token); + EXPECT_TRUE(found); + + ep.set_time(15 * 1000); + ep.CallAndReregisterAlarmEvents(); // A + EXPECT_TRUE(alarmA.was_called()); + + alarmA.Reset(); + + found = alarmA.get_token(&second_token); + EXPECT_TRUE(found); + if (found) { + ep.UnregisterAlarm(second_token); + } + + ep.set_time(30 * 1000); + ep.CallAndReregisterAlarmEvents(); // A + + alarmA.Reset(); +} + +// Here we make sure that one alarm can unregister another alarm +// in OnShutdown(). +TEST(SimpleEpollServerTest, TestAlarmCanUnregistersAnotherAlarmOnShutdown) { + TestAlarmThatUnregistersAnotherAlarm alarm1; + TestAlarm alarm2; + { + EpollTestServer ep; + // Register two alarms and make alarm1 is placed in queue in front of alarm2 + // so that when the queue is cleared, alarm1 is processed first. + const int64_t now = WallTimeNowInUsec(); + ep.RegisterAlarm(now + 5000, &alarm1); + ep.RegisterAlarm(now + 9000, &alarm2); + alarm1.SetUnregisterAlarm(&alarm2, &ep); + EXPECT_EQ(2, ep.GetNumPendingAlarmsForTest()); + } +} + +class TestAlarmRegisterAnotherAlarmShutdown : public TestAlarmUnregister { + public: + TestAlarmRegisterAnotherAlarmShutdown(EpollAlarmCallbackInterface* alarm2, + int64_t when) + : alarm2_(alarm2), when_(when) {} + void OnShutdown(SimpleEpollServer* eps) override { + TestAlarmUnregister::OnShutdown(eps); + eps->RegisterAlarm(when_, alarm2_); + } + + private: + EpollAlarmCallbackInterface* alarm2_; + int64_t when_; +}; + +// This tests that alarm registers another alarm when shutting down. +// The two cases are: new alarm comes before and after the alarm being +// notified by OnShutdown() +TEST(SimpleEpollServerTest, AlarmRegistersAnotherAlarmOnShutdownBeforeSelf) { + TestAlarm alarm2; + int64_t alarm_time = WallTimeNowInUsec() + 5000; + TestAlarmRegisterAnotherAlarmShutdown alarm1(&alarm2, alarm_time - 1000); + { + EpollTestAlarms ep; + ep.RegisterAlarm(alarm_time, &alarm1); + } + EXPECT_TRUE(alarm1.onshutdown_called()); + EXPECT_FALSE(alarm2.onshutdown_called()); +} + +TEST(SimpleEpollServerTest, AlarmRegistersAnotherAlarmOnShutdownAfterSelf) { + TestAlarm alarm2; + int64_t alarm_time = WallTimeNowInUsec() + 5000; + TestAlarmRegisterAnotherAlarmShutdown alarm1(&alarm2, alarm_time + 1000); + { + EpollTestAlarms ep; + ep.RegisterAlarm(alarm_time, &alarm1); + } + EXPECT_TRUE(alarm1.onshutdown_called()); + EXPECT_TRUE(alarm2.onshutdown_called()); +} + +TEST(SimpleEpollServerTest, TestWrite) { + SimpleEpollServer ep; + ep.set_timeout_in_us(1); + char data[kPageSize] = {0}; + + int pipe_fds[2]; + if (pipe(pipe_fds) < 0) { + EPOLL_PLOG(FATAL) << "pipe() failed"; + } + int read_fd = pipe_fds[0]; + int write_fd = pipe_fds[1]; + + RecordingCB recording_cb; + const Recorder* recorder = recording_cb.recorder(); + const std::vector<RecordEntry> *records = recorder->records(); + + // Register to listen to write events. + ep.RegisterFD(write_fd, &recording_cb, EPOLLOUT | O_NONBLOCK); + // At this point the recorder should have the creation and registration + // events. + EXPECT_EQ(2, records->size()); + + // Fill up the pipe. + int written = 1; + for (int i = 0; i < 17 && written > 0 ; ++i) { + written = write(write_fd, &data, kPageSize); + } + EXPECT_LT(written, 0); + + // There should be no new events as the pipe is not available for writing. + ep.WaitForEventsAndExecuteCallbacks(); + EXPECT_EQ(2, records->size()); + + // Now read data from the pipe to make it writable again. This time the + // we should get an EPOLLOUT event. + int size = read(read_fd, &data, kPageSize); + EXPECT_EQ(kPageSize, size); + ep.WaitForEventsAndExecuteCallbacks(); + EXPECT_EQ(3, records->size()); + + // Now unsubscribe from writable events (which adds a modification record) + // and wait to verify that no event records are added. + ep.StopWrite(write_fd); + ep.WaitForEventsAndExecuteCallbacks(); + EXPECT_EQ(4, records->size()); + + // We had the right number of events all along. Make sure they were actually + // the right events. + Recorder tmp; + tmp.Record(&recording_cb, CREATION, 0, 0); + tmp.Record(&recording_cb, REGISTRATION, write_fd, EPOLLOUT | O_NONBLOCK); + tmp.Record(&recording_cb, EVENT, write_fd, EPOLLOUT); + tmp.Record(&recording_cb, MODIFICATION, write_fd, O_NONBLOCK); + + EXPECT_TRUE(recorder->IsEqual(&tmp)); + ep.UnregisterFD(write_fd); + + close(read_fd); + close(write_fd); +} + +TEST(SimpleEpollServerTest, TestReadWrite) { + SimpleEpollServer ep; + ep.set_timeout_in_us(1); + char data[kPageSize] = {0}; + + int pipe_fds[2]; + if (pipe(pipe_fds) < 0) { + EPOLL_PLOG(FATAL) << "pipe() failed"; + } + int read_fd = pipe_fds[0]; + int write_fd = pipe_fds[1]; + + RecordingCB recording_cb; + const Recorder* recorder = recording_cb.recorder(); + const std::vector<RecordEntry> *records = recorder->records(); + + // Register to listen to read and write events. + ep.RegisterFDForReadWrite(read_fd, &recording_cb); + // At this point the recorder should have the creation and registration + // events. + EXPECT_EQ(2, records->size()); + + int written = write(write_fd, &data, kPageSize); + EXPECT_EQ(kPageSize, written); + + ep.WaitForEventsAndExecuteCallbacks(); + ep.UnregisterFD(read_fd); + + close(read_fd); + close(write_fd); +} + +TEST(SimpleEpollServerTest, TestMultipleFDs) { + SimpleEpollServer ep; + ep.set_timeout_in_us(1); + char data = 'x'; + + int pipe_one[2]; + if (pipe(pipe_one) < 0) { + EPOLL_PLOG(FATAL) << "pipe() failed"; + } + int pipe_two[2]; + if (pipe(pipe_two) < 0) { + EPOLL_PLOG(FATAL) << "pipe() failed"; + } + + RecordingCB recording_cb_one; + const Recorder* recorder_one = recording_cb_one.recorder(); + const std::vector<RecordEntry> *records_one = recorder_one->records(); + + RecordingCB recording_cb_two; + const Recorder* recorder_two = recording_cb_two.recorder(); + const std::vector<RecordEntry> *records_two = recorder_two->records(); + + // Register to listen to read events for both pipes + ep.RegisterFDForRead(pipe_one[0], &recording_cb_one); + ep.RegisterFDForRead(pipe_two[0], &recording_cb_two); + + + EXPECT_EQ(2, records_one->size()); + EXPECT_EQ(2, records_two->size()); + + write(pipe_one[1], &data, 1); + ep.WaitForEventsAndExecuteCallbacks(); + EXPECT_EQ(3, records_one->size()); + EXPECT_EQ(2, records_two->size()); + + + write(pipe_two[1], &data, 1); + ep.WaitForEventsAndExecuteCallbacks(); + EXPECT_EQ(3, records_one->size()); + EXPECT_EQ(3, records_two->size()); + + write(pipe_one[1], &data, 1); + write(pipe_two[1], &data, 1); + ep.WaitForEventsAndExecuteCallbacks(); + EXPECT_EQ(4, records_one->size()); + EXPECT_EQ(4, records_two->size()); + + ep.WaitForEventsAndExecuteCallbacks(); + ep.UnregisterFD(pipe_one[0]); + ep.UnregisterFD(pipe_two[0]); + close(pipe_one[0]); + close(pipe_one[1]); + close(pipe_two[0]); + close(pipe_two[1]); +} + +// Check that the SimpleEpollServer calls OnShutdown for any registered FDs. +TEST(SimpleEpollServerTest, TestFDOnShutdown) { + int pipe_fds[2]; + if (pipe(pipe_fds) < 0) { + EPOLL_PLOG(FATAL) << "pipe() failed"; + } + int read_fd = pipe_fds[0]; + int write_fd = pipe_fds[1]; + + RecordingCB recording_cb1; + RecordingCB recording_cb2; + const Recorder* recorder1 = recording_cb1.recorder(); + const Recorder* recorder2 = recording_cb2.recorder(); + + { + SimpleEpollServer ep; + ep.set_timeout_in_us(1); + + // Register to listen to write events. + ep.RegisterFD(write_fd, &recording_cb1, EPOLLOUT | O_NONBLOCK); + ep.RegisterFD(read_fd, &recording_cb2, EPOLLIN | O_NONBLOCK); + } + + // Make sure OnShutdown was called for both callbacks. + Recorder write_recorder; + write_recorder.Record(&recording_cb1, CREATION, 0, 0); + write_recorder.Record( + &recording_cb1, REGISTRATION, write_fd, EPOLLOUT | O_NONBLOCK); + write_recorder.Record(&recording_cb1, UNREGISTRATION, write_fd, false); + write_recorder.Record(&recording_cb1, SHUTDOWN, write_fd, 0); + EXPECT_TRUE(recorder1->IsEqual(&write_recorder)); + + Recorder read_recorder; + read_recorder.Record(&recording_cb2, CREATION, 0, 0); + read_recorder.Record( + &recording_cb2, REGISTRATION, read_fd, EPOLLIN | O_NONBLOCK); + read_recorder.Record(&recording_cb2, UNREGISTRATION, read_fd, false); + read_recorder.Record(&recording_cb2, SHUTDOWN, read_fd, 0); + EXPECT_TRUE(recorder2->IsEqual(&read_recorder)); + + close(read_fd); + close(write_fd); +} + +class UnregisterCB : public EpollCallbackInterface { + public: + explicit UnregisterCB(int fd) + : eps_(nullptr), fd_(fd), onshutdown_called_(false) { + } + + ~UnregisterCB() override { + } + + void OnShutdown(SimpleEpollServer* eps, int fd) override { + eps_->UnregisterFD(fd_); + eps_->UnregisterFD(fd); + onshutdown_called_ = true; + eps_ = nullptr; + } + + void set_epollserver(SimpleEpollServer* eps) { eps_ = eps; } + bool onshutdown_called() { return onshutdown_called_; } + + void OnRegistration(SimpleEpollServer* eps, int fd, int event_mask) override { + } + void OnModification(int fd, int event_mask) override {} + void OnEvent(int fd, EpollEvent* event) override {} + void OnUnregistration(int fd, bool replaced) override {} + + string Name() const override { return "UnregisterCB"; } + + protected: + SimpleEpollServer* eps_; + int fd_; + bool onshutdown_called_; +}; + +// Check that unregistering fds in OnShutdown works cleanly. +TEST(SimpleEpollServerTest, TestUnregisteringFDsOnShutdown) { + int pipe_fds[2]; + if (pipe(pipe_fds) < 0) { + EPOLL_PLOG(FATAL) << "pipe() failed"; + } + int read_fd = pipe_fds[0]; + int write_fd = pipe_fds[1]; + + UnregisterCB unreg_cb1(read_fd); + UnregisterCB unreg_cb2(write_fd); + + { + SimpleEpollServer ep; + ep.set_timeout_in_us(1); + + unreg_cb1.set_epollserver(&ep); + unreg_cb2.set_epollserver(&ep); + + // Register to listen to write events. + ep.RegisterFD(write_fd, &unreg_cb1, EPOLLOUT | O_NONBLOCK); + ep.RegisterFD(read_fd, &unreg_cb2, EPOLLIN | O_NONBLOCK); + } + + // Make sure at least one onshutdown was called. + EXPECT_TRUE(unreg_cb1.onshutdown_called() || + unreg_cb2.onshutdown_called()); + // Make sure that both onshutdowns were not called. + EXPECT_TRUE(!(unreg_cb1.onshutdown_called() && + unreg_cb2.onshutdown_called())); + + close(read_fd); + close(write_fd); +} + +TEST(SimpleEpollServerTest, TestFDsAndAlarms) { + SimpleEpollServer ep; + ep.set_timeout_in_us(5); + char data = 'x'; + + int pipe_fds[2]; + if (pipe(pipe_fds) < 0) { + EPOLL_PLOG(FATAL) << "pipe() failed"; + } + + RecordingCB recording_cb; + const Recorder* recorder = recording_cb.recorder(); + const std::vector<RecordEntry> *records = recorder->records(); + + TestAlarm alarm; + + ep.RegisterFDForRead(pipe_fds[0], &recording_cb); + + EXPECT_EQ(2, records->size()); + EXPECT_FALSE(alarm.was_called()); + + // Write to the pipe and set a longish alarm so we get a read event followed + // by an alarm event. + int written = write(pipe_fds[1], &data, 1); + EXPECT_EQ(1, written); + ep.WaitForEventsAndExecuteCallbacks(); + EXPECT_EQ(3, records->size()); + EXPECT_FALSE(alarm.was_called()); + ep.RegisterAlarm(WallTimeNowInUsec() + 1000, &alarm); + WaitForAlarm(&ep, alarm); + EXPECT_EQ(3, records->size()); + EXPECT_TRUE(alarm.was_called()); + alarm.Reset(); + + // Now set a short alarm so the alarm and the read event are called together. + ep.RegisterAlarm(WallTimeNowInUsec(), &alarm); + written = write(pipe_fds[1], &data, 1); + EXPECT_EQ(1, written); + ep.WaitForEventsAndExecuteCallbacks(); + EXPECT_TRUE(alarm.was_called()); + EXPECT_EQ(4, records->size()); + + ep.UnregisterFD(pipe_fds[0]); + + close(pipe_fds[0]); + close(pipe_fds[1]); +} + +class EpollReader: public EpollCallbackInterface { + public: + explicit EpollReader(int len) + : len_(0), + expected_len_(len), + done_reading_(false) { + memset(&buf_, 0, kMaxBufLen); + } + + ~EpollReader() override {} + + void OnRegistration(SimpleEpollServer* eps, int fd, int event_mask) override { + } + + void OnModification(int fd, int event_mask) override {} + + void OnEvent(int fd, EpollEvent* event) override { + if (event->in_events & EPOLLIN) { + len_ += read(fd, &buf_ + len_, kMaxBufLen - len_); + } + + // If we have finished reading... + if (event->in_events & EPOLLHUP) { + CHECK_EQ(len_, expected_len_); + done_reading_ = true; + } + } + + void OnUnregistration(int fd, bool replaced) override {} + + void OnShutdown(SimpleEpollServer* eps, int fd) override { + // None of the current tests involve having active callbacks when the + // server shuts down. + EPOLL_LOG(FATAL); + } + + string Name() const override { return "EpollReader"; } + + // Returns true if the data in buf is the same as buf_, false otherwise. + bool CheckOutput(char* buf, int len) { + if (len != len_) { + return false; + } + return !memcmp(buf, buf_, len); + } + + bool done_reading() { return done_reading_; } + + protected: + int len_; + int expected_len_; + char buf_[kMaxBufLen]; + bool done_reading_; +}; + +void TestPipe(char *test_message, int len) { + int pipe_fds[2]; + if (pipe(pipe_fds) < 0) { + PLOG(FATAL) << "pipe failed()"; + } + int reader_pipe = pipe_fds[0]; + int writer_pipe = pipe_fds[1]; + int child_pid; + memset(test_message, 'x', len); + + switch (child_pid = fork()) { + case 0: { // Child will send message. + const char *message = test_message; + int size; + close(reader_pipe); + while ((size = write(writer_pipe, message, len)) > 0) { + message += size; + len -= size; + if (len == 0) { + break; + } + } + if (len > 0) { + PLOG(FATAL) << "write() failed"; + } + close(writer_pipe); + + _exit(0); + } + case -1: + PLOG(FATAL) << "fork() failed"; + break; + default: { // Parent will receive message. + close(writer_pipe); + auto ep = absl::make_unique<SimpleEpollServer>(); + ep->set_timeout_in_us(1); + EpollReader reader(len); + ep->RegisterFD(reader_pipe, &reader, EPOLLIN); + + int64_t start_ms = WallTimeNowInUsec() / 1000; + // Loop until we're either done reading, or have waited ~10 us. + while (!reader.done_reading() && + (WallTimeNowInUsec() / 1000 - start_ms) < 10000) { + ep->WaitForEventsAndExecuteCallbacks(); + } + ep->UnregisterFD(reader_pipe); + CHECK(reader.CheckOutput(test_message, len)); + break; + } + } + + close(reader_pipe); + close(writer_pipe); +} + +TEST(SimpleEpollServerTest, TestSmallPipe) { + char buf[kMaxBufLen]; + TestPipe(buf, 10); +} + +TEST(SimpleEpollServerTest, TestLargePipe) { + char buf[kMaxBufLen]; + TestPipe(buf, kMaxBufLen); +} + +// Tests RegisterFDForRead as well as StopRead. +TEST(SimpleEpollServerTest, TestRead) { + SimpleEpollServer ep; + ep.set_timeout_in_us(1); + int len = 1; + + int pipe_fds[2]; + if (pipe(pipe_fds) < 0) { + EPOLL_PLOG(FATAL) << "pipe() failed"; + } + int read_fd = pipe_fds[0]; + int write_fd = pipe_fds[1]; + + auto reader = absl::make_unique<EpollReader>(len); + + // Check that registering a FD for read alerts us when there is data to be + // read. + ep.RegisterFDForRead(read_fd, reader.get()); + char data = 'a'; + int size = write(write_fd, &data, 1); + EXPECT_EQ(1, size); + ep.WaitForEventsAndExecuteCallbacks(); + EXPECT_TRUE(reader->CheckOutput(&data, len)); + + // Remove the callback for read events, write to the pipe and make sure that + // we did not read more data. + ep.StopRead(read_fd); + size = write(write_fd, &data, len); + EXPECT_EQ(1, size); + // The wait will return after timeout. + ep.WaitForEventsAndExecuteCallbacks(); + EXPECT_TRUE(reader->CheckOutput(&data, len)); + ep.UnregisterFD(read_fd); + + close(read_fd); + close(write_fd); +} + +class EdgeTriggerCB : public EpollCallbackInterface { + public: + EdgeTriggerCB(int read_size, int write_size, char write_char, char peer_char) + : eps_(nullptr), + read_buf_(read_size), + write_buf_(write_size, write_char), + peer_char_(peer_char) { + Reset(); + } + + ~EdgeTriggerCB() override {} + + void Reset() { + CHECK(eps_ == nullptr); + bytes_read_ = 0; + bytes_written_ = 0; + can_read_ = false; + will_read_ = false; + can_write_ = false; + will_write_ = false; + read_closed_ = false; + write_closed_ = false; + } + + void ResetByteCounts() { + bytes_read_ = bytes_written_ = 0; + } + + void set_will_read(bool will_read) { will_read_ = will_read; } + + void set_will_write(bool will_write) { will_write_ = will_write; } + + bool can_write() const { return can_write_; } + + int bytes_read() const { return bytes_read_; } + + int bytes_written() const { return bytes_written_; } + + void OnRegistration(SimpleEpollServer* eps, int fd, int event_mask) override { + EXPECT_TRUE(eps_ == nullptr); + eps_ = eps; + Initialize(fd, event_mask); + } + + void OnModification(int fd, int event_mask) override { + EXPECT_TRUE(eps_ != nullptr); + if (event_mask & EPOLLET) { + Initialize(fd, event_mask); + } else { + eps_->SetFDNotReady(fd); + } + } + + void OnEvent(int fd, EpollEvent* event) override { + const int event_mask = event->in_events; + if (event_mask & (EPOLLHUP | EPOLLERR)) { + write_closed_ = true; + return; + } + if (will_read_ && event_mask & EPOLLIN) { + EXPECT_FALSE(read_closed_); + int read_size = read_buf_.size(); + memset(&read_buf_[0], 0, read_size); + int len = recv(fd, &read_buf_[0], read_size, MSG_DONTWAIT); + // Update the readiness states + can_read_ = (len == read_size); + + if (len > 0) { + bytes_read_ += len; + EPOLL_VLOG(1) << "fd: " << fd << ", read " << len + << ", total: " << bytes_read_; + // Now check the bytes read + EXPECT_TRUE(CheckReadBuffer(len)); + } else if (len < 0) { + EPOLL_VLOG(1) << "fd: " << fd << " read hit EAGAIN"; + EXPECT_EQ(EAGAIN, errno) << strerror(errno); + can_read_ = false; + } else { + read_closed_ = true; + } + } + if (will_write_ && event_mask & EPOLLOUT) { + // Write side close/full close can only detected by EPOLLHUP, which is + // caused by EPIPE. + EXPECT_FALSE(write_closed_); + int write_size = write_buf_.size(); + int len = send(fd, &write_buf_[0], write_size, MSG_DONTWAIT); + can_write_ = (len == write_size); + if (len > 0) { + bytes_written_ += len; + EPOLL_VLOG(1) << "fd: " << fd << ", write " << len + << ", total: " << bytes_written_; + } else { + EPOLL_VLOG(1) << "fd: " << fd << " write hit EAGAIN"; + EXPECT_EQ(EAGAIN, errno) << strerror(errno); + can_write_ = false; + } + } + // Since we can only get on the ready list once, wait till we confirm both + // read and write side continuation state and set the correct event mask + // for the ready list. + event->out_ready_mask = can_read_ ? EPOLLIN : 0; + if (can_write_) { + event->out_ready_mask |= EPOLLOUT; + } + } + + void OnUnregistration(int fd, bool replaced) override { + EXPECT_TRUE(eps_ != nullptr); + eps_ = nullptr; + } + + void OnShutdown(SimpleEpollServer* eps, int fd) override { + // None of the current tests involve having active callbacks when the + // server shuts down. + EPOLL_LOG(FATAL); + } + + string Name() const override { return "EdgeTriggerCB"; } + + private: + SimpleEpollServer* eps_; + std::vector<char> read_buf_; + int bytes_read_; + std::vector<char> write_buf_; + int bytes_written_; + char peer_char_; // The char we expected to read. + bool can_read_; + bool will_read_; + bool can_write_; + bool will_write_; + bool read_closed_; + bool write_closed_; + + void Initialize(int fd, int event_mask) { + CHECK(eps_); + can_read_ = can_write_ = false; + if (event_mask & EPOLLET) { + int events = 0; + if (event_mask & EPOLLIN) { + events |= EPOLLIN; + can_read_ = true; + } + if (event_mask & EPOLLOUT) { + events |= EPOLLOUT; + can_write_ = true; + } + eps_->SetFDReady(fd, events); + } + } + + bool CheckReadBuffer(int len) const { + for (int i = 0; i < len; ++i) { + if (peer_char_ != read_buf_[i]) { + return false; + } + } + return true; + } +}; + +// Test adding and removing from the ready list. +TEST(SimpleEpollServerTest, TestReadyList) { + SimpleEpollServer ep; + int pipe_fds[2]; + if (pipe(pipe_fds) < 0) { + EPOLL_PLOG(FATAL) << "pipe() failed"; + } + + // Just use any CB will do, since we never wait on epoll events. + EdgeTriggerCB reader1(0, 0, 0, 0); + EdgeTriggerCB reader2(0, 0, 0, 0); + + ep.RegisterFD(pipe_fds[0], &reader1, EPOLLIN); + ep.RegisterFD(pipe_fds[1], &reader2, EPOLLOUT); + + // Adding fds that are registered with eps + EXPECT_FALSE(ep.IsFDReady(pipe_fds[0])); + EXPECT_FALSE(ep.IsFDReady(pipe_fds[1])); + + ep.SetFDReady(pipe_fds[0], EPOLLIN); + EXPECT_TRUE(ep.IsFDReady(pipe_fds[0])); + EXPECT_FALSE(ep.IsFDReady(pipe_fds[1])); + EXPECT_EQ(1, ep.ReadyListSize()); + ep.SetFDReady(pipe_fds[1], EPOLLOUT); + EXPECT_TRUE(ep.IsFDReady(pipe_fds[0])); + EXPECT_TRUE(ep.IsFDReady(pipe_fds[1])); + EXPECT_EQ(2, ep.ReadyListSize()); + + // Now check that SetFDNotReady doesn't affect other fds + ep.SetFDNotReady(pipe_fds[0]); + EXPECT_FALSE(ep.IsFDReady(pipe_fds[0])); + EXPECT_TRUE(ep.IsFDReady(pipe_fds[1])); + EXPECT_EQ(1, ep.ReadyListSize()); + + ep.UnregisterFD(pipe_fds[0]); + ep.UnregisterFD(pipe_fds[1]); + EXPECT_EQ(0, ep.ReadyListSize()); + + // Now try adding them when they are not registered, and it shouldn't work. + ep.SetFDReady(pipe_fds[0], EPOLLIN); + EXPECT_FALSE(ep.IsFDReady(pipe_fds[0])); + EXPECT_EQ(0, ep.ReadyListSize()); + + close(pipe_fds[0]); + close(pipe_fds[1]); +} + +class EPSWaitThread : public EpollThread { + public: + explicit EPSWaitThread(SimpleEpollServer* eps) + : EpollThread("EPSWait"), eps_(eps), done_(false) {} + + void Run() override { + eps_->WaitForEventsAndExecuteCallbacks(); + } + + bool done() { return done_; } + private: + SimpleEpollServer* eps_; + bool done_; +}; + +TEST(EpollServerTest, TestWake) { + SimpleEpollServer eps; + eps.set_timeout_in_us(-1); + EPSWaitThread eps_thread(&eps); + eps_thread.Start(); + + EXPECT_FALSE(eps_thread.done()); + eps.Wake(); + eps_thread.Join(); +} + +class UnRegisterWhileProcessingCB: public EpollCallbackInterface { + public: + explicit UnRegisterWhileProcessingCB(int fd) : eps_(nullptr), fd_(fd) {} + + ~UnRegisterWhileProcessingCB() override { + } + + void OnShutdown(SimpleEpollServer* eps, int fd) override {} + + void set_epoll_server(SimpleEpollServer* eps) { eps_ = eps; } + void OnRegistration(SimpleEpollServer* eps, int fd, int event_mask) override { + } + void OnModification(int fd, int event_mask) override {} + void OnEvent(int fd, EpollEvent* event) override { + // This should cause no problems. + eps_->UnregisterFD(fd_); + } + void OnUnregistration(int fd, bool replaced) override {} + string Name() const override { return "UnRegisterWhileProcessingCB"; } + + protected: + SimpleEpollServer* eps_; + int fd_; +}; + +class RegisterWhileProcessingCB: public EpollCallbackInterface { + public: + RegisterWhileProcessingCB(int fd, EpollCallbackInterface* cb) + : eps_(nullptr), fd_(fd), cb_(cb) {} + + ~RegisterWhileProcessingCB() override { + } + + void OnShutdown(SimpleEpollServer* eps, int fd) override {} + + void set_epoll_server(SimpleEpollServer* eps) { eps_ = eps; } + void OnRegistration(SimpleEpollServer* eps, int fd, int event_mask) override { + } + void OnModification(int fd, int event_mask) override {} + void OnEvent(int fd, EpollEvent* event) override { + // This should cause no problems. + eps_->RegisterFDForReadWrite(fd_, cb_); + } + void OnUnregistration(int fd, bool replaced) override {} + string Name() const override { return "RegisterWhileProcessingCB"; } + + protected: + SimpleEpollServer* eps_; + int fd_; + EpollCallbackInterface* cb_; +}; + +// Nothing bad should happen when we do this. We're -only- +// testing that nothing bad occurs in this test. +TEST(SimpleEpollServerTest, NothingBadWhenUnRegisteringFDWhileProcessingIt) { + UnRegisterWhileProcessingCB cb(0); + { + FakeSimpleEpollServer epoll_server; + cb.set_epoll_server(&epoll_server); + epoll_server.RegisterFDForReadWrite(0, &cb); + epoll_event ee; + ee.data.fd = 0; + epoll_server.AddEvent(0, ee); + epoll_server.AdvanceBy(1); + epoll_server.WaitForEventsAndExecuteCallbacks(); + } +} + +// +// testing that nothing bad occurs in this test. +TEST(SimpleEpollServerTest, + NoEventsDeliveredForFdsOfUnregisteredCBsWithReRegdFD) { + // events: fd0, fd1, fd2 + // fd0 -> unreg fd2 + // fd1 -> reg fd2 + // fd2 -> no event should be seen + RecordingCB recorder_cb; + UnRegisterWhileProcessingCB unreg_cb(-3); + RegisterWhileProcessingCB reg_other_cb(-3, &recorder_cb); + { + FakeSimpleEpollServer epoll_server; + unreg_cb.set_epoll_server(&epoll_server); + reg_other_cb.set_epoll_server(&epoll_server); + epoll_server.RegisterFDForReadWrite(-1, &unreg_cb); + epoll_server.RegisterFDForReadWrite(-2, ®_other_cb); + epoll_server.RegisterFDForReadWrite(-3, &recorder_cb); + + epoll_event ee; + ee.events = EPOLLIN; // asserted for all events for this test. + + // Note that these events are in 'backwards' order in terms of time. + // Currently, the SimpleEpollServer code invokes the CBs from last delivered + // to first delivered, so this is to be sure that we invoke the CB for -1 + // before -2, before -3. + ee.data.fd = -1; + epoll_server.AddEvent(2, ee); + ee.data.fd = -2; + epoll_server.AddEvent(1, ee); + ee.data.fd = -3; + epoll_server.AddEvent(0, ee); + + epoll_server.AdvanceBy(5); + epoll_server.WaitForEventsAndExecuteCallbacks(); + } + + Recorder correct_recorder; + correct_recorder.Record(&recorder_cb, CREATION, 0, 0); + correct_recorder.Record(&recorder_cb, REGISTRATION, -3, + EPOLLIN | EPOLLOUT); + correct_recorder.Record(&recorder_cb, UNREGISTRATION, -3, 0); + correct_recorder.Record(&recorder_cb, REGISTRATION, -3, + EPOLLIN | EPOLLOUT); + correct_recorder.Record(&recorder_cb, SHUTDOWN, -3, 0); + + EXPECT_TRUE(correct_recorder.IsEqual(recorder_cb.recorder())); +} + +class ReRegWhileReadyListOnEvent: public EpollCallbackInterface { + public: + explicit ReRegWhileReadyListOnEvent(int fd) : eps_(nullptr) {} + + void OnShutdown(SimpleEpollServer* eps, int fd) override {} + + void set_epoll_server(SimpleEpollServer* eps) { eps_ = eps; } + void OnRegistration(SimpleEpollServer* eps, int fd, int event_mask) override { + } + void OnModification(int fd, int event_mask) override {} + void OnEvent(int fd, EpollEvent* event) override { + // This should cause no problems. + eps_->UnregisterFD(fd); + eps_->RegisterFDForReadWrite(fd, this); + eps_->UnregisterFD(fd); + } + void OnUnregistration(int fd, bool replaced) override {} + string Name() const override { return "ReRegWhileReadyListOnEvent"; } + + protected: + SimpleEpollServer* eps_; +}; + +// Nothing bad should happen when we do this. We're -only- +// testing that nothing bad occurs in this test. +TEST(SimpleEpollServerTest, + NothingBadWhenReRegisteringFDWhileProcessingFromReadyList) { + ReRegWhileReadyListOnEvent cb(0); + { + FakeSimpleEpollServer epoll_server; + cb.set_epoll_server(&epoll_server); + epoll_server.RegisterFDForReadWrite(0, &cb); + epoll_event ee; + ee.data.fd = 0; + epoll_server.AddEvent(0, ee); + epoll_server.AdvanceBy(1); + epoll_server.WaitForEventsAndExecuteCallbacks(); + } +} + +class UnRegEverythingReadyListOnEvent: public EpollCallbackInterface { + public: + UnRegEverythingReadyListOnEvent() : eps_(nullptr), fd_(0), fd_range_(0) {} + + void set_fd(int fd) { fd_ = fd; } + void set_fd_range(int fd_range) { fd_range_ = fd_range; } + void set_num_called(int* num_called) { num_called_ = num_called; } + + void OnShutdown(SimpleEpollServer* eps, int fd) override {} + + void set_epoll_server(SimpleEpollServer* eps) { eps_ = eps; } + void OnRegistration(SimpleEpollServer* eps, int fd, int event_mask) override { + eps->SetFDReady(fd, EPOLLIN); + } + void OnModification(int fd, int event_mask) override {} + void OnEvent(int fd, EpollEvent* event) override { + // This should cause no problems. + CHECK(num_called_ != nullptr); + ++(*num_called_); + // Note that we're iterating from -fd_range + 1 -> 0. + // We do this because there is an FD installed into the + // epollserver somewhere in the low numbers. + // Using negative FD numbers (which are guaranteed to not + // exist in the epoll-server) ensures that we will not + // come in conflict with the preexisting FD. + for (int i = -fd_range_ + 1; i <= 0; ++i) { + eps_->UnregisterFD(i); + } + } + void OnUnregistration(int fd, bool replaced) override {} + string Name() const override { return "UnRegEverythingReadyListOnEvent"; } + + protected: + SimpleEpollServer* eps_; + int fd_; + int fd_range_; + int* num_called_; +}; + +TEST(SimpleEpollServerTest, + NothingBadWhenUnRegisteredWhileProcessingFromReadyList) { + UnRegEverythingReadyListOnEvent callbacks[32]; + int num_called = 0; + { + FakeSimpleEpollServer epoll_server; + for (size_t i = 0; i < ABSL_ARRAYSIZE(callbacks); ++i) { + callbacks[i].set_fd(-i); + callbacks[i].set_fd_range(ABSL_ARRAYSIZE(callbacks)); + callbacks[i].set_num_called(&num_called); + callbacks[i].set_epoll_server(&epoll_server); + epoll_server.RegisterFDForReadWrite(0, &callbacks[i]); + epoll_event ee; + ee.data.fd = -i; + epoll_server.AddEvent(0, ee); + } + epoll_server.AdvanceBy(1); + epoll_server.WaitForEventsAndExecuteCallbacks(); + epoll_server.WaitForEventsAndExecuteCallbacks(); + } + EXPECT_EQ(1, num_called); +} + +TEST(SimpleEpollServerTest, TestThatVerifyReadyListWorksWithNothingInList) { + FakeSimpleEpollServer epoll_server; + epoll_server.VerifyReadyList(); +} + +TEST(SimpleEpollServerTest, TestThatVerifyReadyListWorksWithStuffInLists) { + FakeSimpleEpollServer epoll_server; + epoll_server.VerifyReadyList(); +} + +TEST(SimpleEpollServerTest, + ApproximateNowInUsAccurateOutideOfWaitForEventsAndExecuteCallbacks) { + FakeSimpleEpollServer epoll_server; + epoll_server.AdvanceBy(1232); + EXPECT_EQ(epoll_server.ApproximateNowInUsec(), epoll_server.NowInUsec()); + epoll_server.AdvanceBy(1111); + EXPECT_EQ(epoll_server.ApproximateNowInUsec(), epoll_server.NowInUsec()); +} + +class ApproximateNowInUsecTestCB: public EpollCallbackInterface { + public: + ApproximateNowInUsecTestCB() : feps_(nullptr), called_(false) {} + + void OnRegistration(SimpleEpollServer* eps, int fd, int event_mask) override { + } + void OnModification(int fd, int event_mask) override {} + void OnEvent(int fd, EpollEvent* event) override { + EXPECT_EQ(feps_->ApproximateNowInUsec(), feps_->NowInUsec()); + feps_->AdvanceBy(1111); + EXPECT_EQ(1 * 1111 + feps_->ApproximateNowInUsec(), feps_->NowInUsec()); + feps_->AdvanceBy(1111); + EXPECT_EQ(2 * 1111 + feps_->ApproximateNowInUsec(), feps_->NowInUsec()); + called_ = true; + } + void OnUnregistration(int fd, bool replaced) override {} + void OnShutdown(SimpleEpollServer* eps, int fd) override {} + string Name() const override { return "ApproximateNowInUsecTestCB"; } + + void set_fakeepollserver(FakeSimpleEpollServer* feps) { feps_ = feps; } + bool called() const { return called_; } + + protected: + FakeSimpleEpollServer* feps_; + bool called_; +}; + +TEST(SimpleEpollServerTest, + ApproximateNowInUsApproximateInsideOfWaitForEventsAndExecuteCallbacks) { + int dummy_fd = 11111; + ApproximateNowInUsecTestCB aniutcb; + { + FakeSimpleEpollServer epoll_server; + aniutcb.set_fakeepollserver(&epoll_server); + + epoll_server.RegisterFD(dummy_fd, &aniutcb, EPOLLIN); + epoll_event ee; + ee.data.fd = dummy_fd; + ee.events = EPOLLIN; + epoll_server.AddEvent(10242, ee); + epoll_server.set_timeout_in_us(-1); + epoll_server.AdvanceByAndWaitForEventsAndExecuteCallbacks(20000); + EXPECT_TRUE(aniutcb.called()); + } +} + +// A mock epoll server that also simulates kernel delay in scheduling epoll +// events. +class FakeEpollServerWithDelay : public FakeSimpleEpollServer { + public: + FakeEpollServerWithDelay() : FakeSimpleEpollServer(), delay(0) {} + + int delay; + + protected: + int epoll_wait_impl(int epfd, struct epoll_event* events, int max_events, + int timeout_in_ms) override { + int out = FakeSimpleEpollServer::epoll_wait_impl(epfd, events, max_events, + timeout_in_ms); + AdvanceBy(delay); + return out; + } +}; + +// A callback that records the epoll event's delay. +class RecordDelayOnEvent: public EpollCallbackInterface { + public: + RecordDelayOnEvent() : last_delay(-1), eps_(nullptr) {} + + ~RecordDelayOnEvent() override { + } + + void OnShutdown(SimpleEpollServer* eps, int fd) override {} + + string Name() const override { + return "RecordDelayOnEvent"; + } + + void set_epoll_server(SimpleEpollServer* eps) { eps_ = eps; } + void OnRegistration(SimpleEpollServer* eps, int fd, int event_mask) override { + } + void OnModification(int fd, int event_mask) override {} + void OnEvent(int fd, EpollEvent* event) override { + last_delay = eps_->LastDelayInUsec(); + } + void OnUnregistration(int fd, bool replaced) override {} + + int64_t last_delay; + + protected: + SimpleEpollServer* eps_; +}; + +// Tests that an epoll callback sees the correct delay for its event when it +// calls LastDelayInUsec(). +TEST(EpollServerTest, TestLastDelay) { + RecordDelayOnEvent cb; + FakeEpollServerWithDelay epoll_server; + + cb.set_epoll_server(&epoll_server); + + epoll_server.RegisterFDForReadWrite(0, &cb); + epoll_event ee; + ee.data.fd = 0; + + // Inject delay, and confirm that it's reported. + epoll_server.set_timeout_in_us(5000); + epoll_server.delay = 6000; + epoll_server.AddEvent(0, ee); + epoll_server.AdvanceBy(1); + epoll_server.WaitForEventsAndExecuteCallbacks(); + EXPECT_EQ(cb.last_delay, 1000); + + // Fire an event before the timeout ends, and confirm that reported delay + // isn't negative. + epoll_server.set_timeout_in_us(5000); + epoll_server.delay = 0; + epoll_server.AddEvent(0, ee); + epoll_server.AdvanceBy(1); + epoll_server.WaitForEventsAndExecuteCallbacks(); + EXPECT_EQ(cb.last_delay, 0); + + // Wait forever until an event fires, and confirm there's no reported delay. + epoll_server.set_timeout_in_us(-1); + epoll_server.delay = 6000; + epoll_server.AddEvent(0, ee); + epoll_server.AdvanceBy(1); + epoll_server.WaitForEventsAndExecuteCallbacks(); + EXPECT_EQ(cb.last_delay, 0); +} + +TEST(SimpleEpollServerAlarmTest, TestShutdown) { + std::unique_ptr<SimpleEpollServer> eps(new SimpleEpollServer); + EpollAlarm alarm1; + EpollAlarm alarm2; + + eps->RegisterAlarmApproximateDelta(10000000, &alarm1); + eps->RegisterAlarmApproximateDelta(10000000, &alarm2); + + alarm2.UnregisterIfRegistered(); + EXPECT_FALSE(alarm2.registered()); + eps = nullptr; + + EXPECT_FALSE(alarm1.registered()); +} + +TEST(SimpleEpollServerAlarmTest, TestUnregister) { + SimpleEpollServer eps; + EpollAlarm alarm; + + eps.RegisterAlarmApproximateDelta(10000000, &alarm); + EXPECT_TRUE(alarm.registered()); + + alarm.UnregisterIfRegistered(); + EXPECT_FALSE(alarm.registered()); + + alarm.UnregisterIfRegistered(); + EXPECT_FALSE(alarm.registered()); +} + +TEST(SimpleEpollServerAlarmTest, TestUnregisterOnDestruction) { + EpollTestServer eps; + std::unique_ptr<EpollAlarm> alarm(new EpollAlarm()); + EpollAlarm* alarm_ptr = alarm.get(); + + eps.RegisterAlarmApproximateDelta(10000000, alarm.get()); + EXPECT_TRUE(eps.ContainsAlarm(alarm_ptr)); + alarm = nullptr; + EXPECT_EQ(0, eps.GetNumPendingAlarmsForTest()); +} + +TEST(SimpleEpollServerAlarmTest, TestUnregisterOnAlarm) { + EpollTestServer eps; + EpollAlarm alarm; + + eps.RegisterAlarmApproximateDelta(1, &alarm); + EXPECT_TRUE(eps.ContainsAlarm(&alarm)); + + while (alarm.registered()) { + eps.WaitForEventsAndExecuteCallbacks(); + } + EXPECT_FALSE(eps.ContainsAlarm(&alarm)); +} + +TEST(SimpleEpollServerAlarmTest, TestReregisterAlarm) { + EpollTestAlarms ep; + + EpollAlarm alarm; + ep.set_time(1000); + ep.RegisterAlarm(5000, &alarm); + + EXPECT_EQ(1, ep.GetNumPendingAlarmsForTest()); + alarm.ReregisterAlarm(6000); + EXPECT_EQ(1, ep.GetNumPendingAlarmsForTest()); + + ep.set_time(5000); + ep.set_timeout_in_us(0); + ep.CallAndReregisterAlarmEvents(); + EXPECT_EQ(1, ep.GetNumPendingAlarmsForTest()); + + ep.set_time(6000); + ep.CallAndReregisterAlarmEvents(); + EXPECT_EQ(0, ep.GetNumPendingAlarmsForTest()); +} + +TEST(SimpleEpollServerAlarmTest, TestThatSameAlarmCanNotBeRegisteredTwice) { + TestAlarm alarm; + SimpleEpollServer epoll_server; + epoll_server.RegisterAlarm(1, &alarm); + EXPECT_EPOLL_BUG(epoll_server.RegisterAlarm(1, &alarm), + "Alarm already exists"); +} + +} // namespace + +} // namespace test + +} // namespace epoll_server