summaryrefslogtreecommitdiffhomepage
path: root/test/syscalls/linux/epoll.cc
diff options
context:
space:
mode:
Diffstat (limited to 'test/syscalls/linux/epoll.cc')
-rw-r--r--test/syscalls/linux/epoll.cc445
1 files changed, 0 insertions, 445 deletions
diff --git a/test/syscalls/linux/epoll.cc b/test/syscalls/linux/epoll.cc
deleted file mode 100644
index 8a72ef10a..000000000
--- a/test/syscalls/linux/epoll.cc
+++ /dev/null
@@ -1,445 +0,0 @@
-// Copyright 2018 The gVisor Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include <errno.h>
-#include <limits.h>
-#include <pthread.h>
-#include <signal.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/epoll.h>
-#include <sys/eventfd.h>
-#include <time.h>
-#include <unistd.h>
-
-#include "gtest/gtest.h"
-#include "test/util/epoll_util.h"
-#include "test/util/eventfd_util.h"
-#include "test/util/file_descriptor.h"
-#include "test/util/posix_error.h"
-#include "test/util/test_util.h"
-
-namespace gvisor {
-namespace testing {
-
-namespace {
-
-constexpr int kFDsPerEpoll = 3;
-constexpr uint64_t kMagicConstant = 0x0102030405060708;
-
-TEST(EpollTest, AllWritable) {
- auto epollfd = ASSERT_NO_ERRNO_AND_VALUE(NewEpollFD());
- std::vector<FileDescriptor> eventfds;
- for (int i = 0; i < kFDsPerEpoll; i++) {
- eventfds.push_back(ASSERT_NO_ERRNO_AND_VALUE(NewEventFD()));
- ASSERT_NO_ERRNO(RegisterEpollFD(epollfd.get(), eventfds[i].get(),
- EPOLLIN | EPOLLOUT, kMagicConstant + i));
- }
-
- struct epoll_event result[kFDsPerEpoll];
- ASSERT_THAT(RetryEINTR(epoll_wait)(epollfd.get(), result, kFDsPerEpoll, -1),
- SyscallSucceedsWithValue(kFDsPerEpoll));
- for (int i = 0; i < kFDsPerEpoll; i++) {
- ASSERT_EQ(result[i].events, EPOLLOUT);
- }
-}
-
-TEST(EpollTest, LastReadable) {
- auto epollfd = ASSERT_NO_ERRNO_AND_VALUE(NewEpollFD());
- std::vector<FileDescriptor> eventfds;
- for (int i = 0; i < kFDsPerEpoll; i++) {
- eventfds.push_back(ASSERT_NO_ERRNO_AND_VALUE(NewEventFD()));
- ASSERT_NO_ERRNO(RegisterEpollFD(epollfd.get(), eventfds[i].get(),
- EPOLLIN | EPOLLOUT, kMagicConstant + i));
- }
-
- uint64_t tmp = 1;
- ASSERT_THAT(WriteFd(eventfds[kFDsPerEpoll - 1].get(), &tmp, sizeof(tmp)),
- SyscallSucceedsWithValue(sizeof(tmp)));
-
- struct epoll_event result[kFDsPerEpoll];
- ASSERT_THAT(RetryEINTR(epoll_wait)(epollfd.get(), result, kFDsPerEpoll, -1),
- SyscallSucceedsWithValue(kFDsPerEpoll));
-
- int i;
- for (i = 0; i < kFDsPerEpoll - 1; i++) {
- EXPECT_EQ(result[i].events, EPOLLOUT);
- }
- EXPECT_EQ(result[i].events, EPOLLOUT | EPOLLIN);
- EXPECT_EQ(result[i].data.u64, kMagicConstant + i);
-}
-
-TEST(EpollTest, LastNonWritable) {
- auto epollfd = ASSERT_NO_ERRNO_AND_VALUE(NewEpollFD());
- std::vector<FileDescriptor> eventfds;
- for (int i = 0; i < kFDsPerEpoll; i++) {
- eventfds.push_back(ASSERT_NO_ERRNO_AND_VALUE(NewEventFD()));
- ASSERT_NO_ERRNO(RegisterEpollFD(epollfd.get(), eventfds[i].get(),
- EPOLLIN | EPOLLOUT, kMagicConstant + i));
- }
-
- // Write the maximum value to the event fd so that writing to it again would
- // block.
- uint64_t tmp = ULLONG_MAX - 1;
- ASSERT_THAT(WriteFd(eventfds[kFDsPerEpoll - 1].get(), &tmp, sizeof(tmp)),
- SyscallSucceedsWithValue(sizeof(tmp)));
-
- struct epoll_event result[kFDsPerEpoll];
- ASSERT_THAT(RetryEINTR(epoll_wait)(epollfd.get(), result, kFDsPerEpoll, -1),
- SyscallSucceedsWithValue(kFDsPerEpoll));
-
- int i;
- for (i = 0; i < kFDsPerEpoll - 1; i++) {
- EXPECT_EQ(result[i].events, EPOLLOUT);
- }
- EXPECT_EQ(result[i].events, EPOLLIN);
- EXPECT_THAT(ReadFd(eventfds[kFDsPerEpoll - 1].get(), &tmp, sizeof(tmp)),
- sizeof(tmp));
- EXPECT_THAT(RetryEINTR(epoll_wait)(epollfd.get(), result, kFDsPerEpoll, -1),
- SyscallSucceedsWithValue(kFDsPerEpoll));
-
- for (i = 0; i < kFDsPerEpoll; i++) {
- EXPECT_EQ(result[i].events, EPOLLOUT);
- }
-}
-
-TEST(EpollTest, Timeout_NoRandomSave) {
- auto epollfd = ASSERT_NO_ERRNO_AND_VALUE(NewEpollFD());
- std::vector<FileDescriptor> eventfds;
- for (int i = 0; i < kFDsPerEpoll; i++) {
- eventfds.push_back(ASSERT_NO_ERRNO_AND_VALUE(NewEventFD()));
- ASSERT_NO_ERRNO(RegisterEpollFD(epollfd.get(), eventfds[i].get(), EPOLLIN,
- kMagicConstant + i));
- }
-
- constexpr int kTimeoutMs = 200;
- struct timespec begin;
- struct timespec end;
- struct epoll_event result[kFDsPerEpoll];
-
- {
- const DisableSave ds; // Timing-related.
- EXPECT_THAT(clock_gettime(CLOCK_MONOTONIC, &begin), SyscallSucceeds());
-
- ASSERT_THAT(
- RetryEINTR(epoll_wait)(epollfd.get(), result, kFDsPerEpoll, kTimeoutMs),
- SyscallSucceedsWithValue(0));
- EXPECT_THAT(clock_gettime(CLOCK_MONOTONIC, &end), SyscallSucceeds());
- }
-
- // Check the lower bound on the timeout. Checking for an upper bound is
- // fragile because Linux can overrun the timeout due to scheduling delays.
- EXPECT_GT(ms_elapsed(begin, end), kTimeoutMs - 1);
-}
-
-void* writer(void* arg) {
- int fd = *reinterpret_cast<int*>(arg);
- uint64_t tmp = 1;
-
- usleep(200000);
- if (WriteFd(fd, &tmp, sizeof(tmp)) != sizeof(tmp)) {
- fprintf(stderr, "writer failed: errno %s\n", strerror(errno));
- }
-
- return nullptr;
-}
-
-TEST(EpollTest, WaitThenUnblock) {
- auto epollfd = ASSERT_NO_ERRNO_AND_VALUE(NewEpollFD());
- std::vector<FileDescriptor> eventfds;
- for (int i = 0; i < kFDsPerEpoll; i++) {
- eventfds.push_back(ASSERT_NO_ERRNO_AND_VALUE(NewEventFD()));
- ASSERT_NO_ERRNO(RegisterEpollFD(epollfd.get(), eventfds[i].get(), EPOLLIN,
- kMagicConstant + i));
- }
-
- // Fire off a thread that will make at least one of the event fds readable.
- pthread_t thread;
- int make_readable = eventfds[0].get();
- ASSERT_THAT(pthread_create(&thread, nullptr, writer, &make_readable),
- SyscallSucceedsWithValue(0));
-
- struct epoll_event result[kFDsPerEpoll];
- EXPECT_THAT(RetryEINTR(epoll_wait)(epollfd.get(), result, kFDsPerEpoll, -1),
- SyscallSucceedsWithValue(1));
- EXPECT_THAT(pthread_detach(thread), SyscallSucceeds());
-}
-
-void sighandler(int s) {}
-
-void* signaler(void* arg) {
- pthread_t* t = reinterpret_cast<pthread_t*>(arg);
- // Repeatedly send the real-time signal until we are detached, because it's
- // difficult to know exactly when epoll_wait on another thread (which this
- // is intending to interrupt) has started blocking.
- while (1) {
- usleep(200000);
- pthread_kill(*t, SIGRTMIN);
- }
- return nullptr;
-}
-
-TEST(EpollTest, UnblockWithSignal) {
- auto epollfd = ASSERT_NO_ERRNO_AND_VALUE(NewEpollFD());
- std::vector<FileDescriptor> eventfds;
- for (int i = 0; i < kFDsPerEpoll; i++) {
- eventfds.push_back(ASSERT_NO_ERRNO_AND_VALUE(NewEventFD()));
- ASSERT_NO_ERRNO(RegisterEpollFD(epollfd.get(), eventfds[i].get(), EPOLLIN,
- kMagicConstant + i));
- }
-
- signal(SIGRTMIN, sighandler);
- // Unblock the real time signals that InitGoogle blocks :(
- sigset_t unblock;
- sigemptyset(&unblock);
- sigaddset(&unblock, SIGRTMIN);
- ASSERT_THAT(sigprocmask(SIG_UNBLOCK, &unblock, nullptr), SyscallSucceeds());
-
- pthread_t thread;
- pthread_t cur = pthread_self();
- ASSERT_THAT(pthread_create(&thread, nullptr, signaler, &cur),
- SyscallSucceedsWithValue(0));
-
- struct epoll_event result[kFDsPerEpoll];
- EXPECT_THAT(epoll_wait(epollfd.get(), result, kFDsPerEpoll, -1),
- SyscallFailsWithErrno(EINTR));
- EXPECT_THAT(pthread_cancel(thread), SyscallSucceeds());
- EXPECT_THAT(pthread_detach(thread), SyscallSucceeds());
-}
-
-TEST(EpollTest, TimeoutNoFds) {
- auto epollfd = ASSERT_NO_ERRNO_AND_VALUE(NewEpollFD());
- struct epoll_event result[kFDsPerEpoll];
- EXPECT_THAT(RetryEINTR(epoll_wait)(epollfd.get(), result, kFDsPerEpoll, 100),
- SyscallSucceedsWithValue(0));
-}
-
-struct addr_ctx {
- int epollfd;
- int eventfd;
-};
-
-void* fd_adder(void* arg) {
- struct addr_ctx* actx = reinterpret_cast<struct addr_ctx*>(arg);
- struct epoll_event event;
- event.events = EPOLLIN | EPOLLOUT;
- event.data.u64 = 0xdeadbeeffacefeed;
-
- usleep(200000);
- if (epoll_ctl(actx->epollfd, EPOLL_CTL_ADD, actx->eventfd, &event) == -1) {
- fprintf(stderr, "epoll_ctl failed: %s\n", strerror(errno));
- }
-
- return nullptr;
-}
-
-TEST(EpollTest, UnblockWithNewFD) {
- auto epollfd = ASSERT_NO_ERRNO_AND_VALUE(NewEpollFD());
- auto eventfd = ASSERT_NO_ERRNO_AND_VALUE(NewEventFD());
-
- pthread_t thread;
- struct addr_ctx actx = {epollfd.get(), eventfd.get()};
- ASSERT_THAT(pthread_create(&thread, nullptr, fd_adder, &actx),
- SyscallSucceedsWithValue(0));
-
- struct epoll_event result[kFDsPerEpoll];
- // Wait while no FDs are ready, but after 200ms fd_adder will add a ready FD
- // to epoll which will wake us up.
- EXPECT_THAT(RetryEINTR(epoll_wait)(epollfd.get(), result, kFDsPerEpoll, -1),
- SyscallSucceedsWithValue(1));
- EXPECT_THAT(pthread_detach(thread), SyscallSucceeds());
- EXPECT_EQ(result[0].data.u64, 0xdeadbeeffacefeed);
-}
-
-TEST(EpollTest, Oneshot) {
- auto epollfd = ASSERT_NO_ERRNO_AND_VALUE(NewEpollFD());
- std::vector<FileDescriptor> eventfds;
- for (int i = 0; i < kFDsPerEpoll; i++) {
- eventfds.push_back(ASSERT_NO_ERRNO_AND_VALUE(NewEventFD()));
- ASSERT_NO_ERRNO(RegisterEpollFD(epollfd.get(), eventfds[i].get(), EPOLLIN,
- kMagicConstant + i));
- }
-
- struct epoll_event event;
- event.events = EPOLLOUT | EPOLLONESHOT;
- event.data.u64 = kMagicConstant;
- ASSERT_THAT(
- epoll_ctl(epollfd.get(), EPOLL_CTL_MOD, eventfds[0].get(), &event),
- SyscallSucceeds());
-
- struct epoll_event result[kFDsPerEpoll];
- // One-shot entry means that the first epoll_wait should succeed.
- ASSERT_THAT(RetryEINTR(epoll_wait)(epollfd.get(), result, kFDsPerEpoll, -1),
- SyscallSucceedsWithValue(1));
- EXPECT_EQ(result[0].data.u64, kMagicConstant);
-
- // One-shot entry means that the second epoll_wait should timeout.
- EXPECT_THAT(RetryEINTR(epoll_wait)(epollfd.get(), result, kFDsPerEpoll, 100),
- SyscallSucceedsWithValue(0));
-}
-
-TEST(EpollTest, EdgeTriggered_NoRandomSave) {
- // Test edge-triggered entry: make it edge-triggered, first wait should
- // return it, second one should time out, make it writable again, third wait
- // should return it, fourth wait should timeout.
- auto epollfd = ASSERT_NO_ERRNO_AND_VALUE(NewEpollFD());
- auto eventfd = ASSERT_NO_ERRNO_AND_VALUE(NewEventFD());
- ASSERT_NO_ERRNO(RegisterEpollFD(epollfd.get(), eventfd.get(),
- EPOLLOUT | EPOLLET, kMagicConstant));
-
- struct epoll_event result[kFDsPerEpoll];
-
- {
- const DisableSave ds; // May trigger spurious event.
-
- // Edge-triggered entry means that the first epoll_wait should return the
- // event.
- ASSERT_THAT(epoll_wait(epollfd.get(), result, kFDsPerEpoll, -1),
- SyscallSucceedsWithValue(1));
- EXPECT_EQ(result[0].data.u64, kMagicConstant);
-
- // Edge-triggered entry means that the second epoll_wait should time out.
- ASSERT_THAT(epoll_wait(epollfd.get(), result, kFDsPerEpoll, 100),
- SyscallSucceedsWithValue(0));
- }
-
- uint64_t tmp = ULLONG_MAX - 1;
-
- // Make an fd non-writable.
- ASSERT_THAT(WriteFd(eventfd.get(), &tmp, sizeof(tmp)),
- SyscallSucceedsWithValue(sizeof(tmp)));
-
- // Make the same fd non-writable to trigger a change, which will trigger an
- // edge-triggered event.
- ASSERT_THAT(ReadFd(eventfd.get(), &tmp, sizeof(tmp)),
- SyscallSucceedsWithValue(sizeof(tmp)));
-
- {
- const DisableSave ds; // May trigger spurious event.
-
- // An edge-triggered event should now be returned.
- ASSERT_THAT(epoll_wait(epollfd.get(), result, kFDsPerEpoll, -1),
- SyscallSucceedsWithValue(1));
- EXPECT_EQ(result[0].data.u64, kMagicConstant);
-
- // The edge-triggered event had been consumed above, we don't expect to
- // get it again.
- ASSERT_THAT(epoll_wait(epollfd.get(), result, kFDsPerEpoll, 100),
- SyscallSucceedsWithValue(0));
- }
-}
-
-TEST(EpollTest, OneshotAndEdgeTriggered) {
- auto epollfd = ASSERT_NO_ERRNO_AND_VALUE(NewEpollFD());
- auto eventfd = ASSERT_NO_ERRNO_AND_VALUE(NewEventFD());
- ASSERT_NO_ERRNO(RegisterEpollFD(epollfd.get(), eventfd.get(),
- EPOLLOUT | EPOLLET | EPOLLONESHOT,
- kMagicConstant));
-
- struct epoll_event result[kFDsPerEpoll];
- // First time one shot edge-triggered entry means that epoll_wait should
- // return the event.
- ASSERT_THAT(RetryEINTR(epoll_wait)(epollfd.get(), result, kFDsPerEpoll, -1),
- SyscallSucceedsWithValue(1));
- EXPECT_EQ(result[0].data.u64, kMagicConstant);
-
- // Edge-triggered entry means that the second epoll_wait should time out.
- ASSERT_THAT(RetryEINTR(epoll_wait)(epollfd.get(), result, kFDsPerEpoll, 100),
- SyscallSucceedsWithValue(0));
-
- uint64_t tmp = ULLONG_MAX - 1;
- // Make an fd non-writable.
- ASSERT_THAT(WriteFd(eventfd.get(), &tmp, sizeof(tmp)),
- SyscallSucceedsWithValue(sizeof(tmp)));
- // Make the same fd non-writable to trigger a change, which will not trigger
- // an edge-triggered event because we've also included EPOLLONESHOT.
- ASSERT_THAT(ReadFd(eventfd.get(), &tmp, sizeof(tmp)),
- SyscallSucceedsWithValue(sizeof(tmp)));
- ASSERT_THAT(RetryEINTR(epoll_wait)(epollfd.get(), result, kFDsPerEpoll, 100),
- SyscallSucceedsWithValue(0));
-}
-
-TEST(EpollTest, CycleOfOneDisallowed) {
- auto epollfd = ASSERT_NO_ERRNO_AND_VALUE(NewEpollFD());
-
- struct epoll_event event;
- event.events = EPOLLOUT;
- event.data.u64 = kMagicConstant;
-
- ASSERT_THAT(epoll_ctl(epollfd.get(), EPOLL_CTL_ADD, epollfd.get(), &event),
- SyscallFailsWithErrno(EINVAL));
-}
-
-TEST(EpollTest, CycleOfThreeDisallowed) {
- auto epollfd = ASSERT_NO_ERRNO_AND_VALUE(NewEpollFD());
- auto epollfd1 = ASSERT_NO_ERRNO_AND_VALUE(NewEpollFD());
- auto epollfd2 = ASSERT_NO_ERRNO_AND_VALUE(NewEpollFD());
-
- ASSERT_NO_ERRNO(
- RegisterEpollFD(epollfd.get(), epollfd1.get(), EPOLLIN, kMagicConstant));
- ASSERT_NO_ERRNO(
- RegisterEpollFD(epollfd1.get(), epollfd2.get(), EPOLLIN, kMagicConstant));
-
- struct epoll_event event;
- event.events = EPOLLIN;
- event.data.u64 = kMagicConstant;
- EXPECT_THAT(epoll_ctl(epollfd2.get(), EPOLL_CTL_ADD, epollfd.get(), &event),
- SyscallFailsWithErrno(ELOOP));
-}
-
-TEST(EpollTest, CloseFile) {
- auto epollfd = ASSERT_NO_ERRNO_AND_VALUE(NewEpollFD());
- auto eventfd = ASSERT_NO_ERRNO_AND_VALUE(NewEventFD());
- ASSERT_NO_ERRNO(
- RegisterEpollFD(epollfd.get(), eventfd.get(), EPOLLOUT, kMagicConstant));
-
- struct epoll_event result[kFDsPerEpoll];
- ASSERT_THAT(RetryEINTR(epoll_wait)(epollfd.get(), result, kFDsPerEpoll, -1),
- SyscallSucceedsWithValue(1));
- EXPECT_EQ(result[0].data.u64, kMagicConstant);
-
- // Close the event fd early.
- eventfd.reset();
-
- EXPECT_THAT(RetryEINTR(epoll_wait)(epollfd.get(), result, kFDsPerEpoll, 100),
- SyscallSucceedsWithValue(0));
-}
-
-TEST(EpollTest, PipeReaderHupAfterWriterClosed) {
- auto epollfd = ASSERT_NO_ERRNO_AND_VALUE(NewEpollFD());
- int pipefds[2];
- ASSERT_THAT(pipe(pipefds), SyscallSucceeds());
- FileDescriptor rfd(pipefds[0]);
- FileDescriptor wfd(pipefds[1]);
-
- ASSERT_NO_ERRNO(RegisterEpollFD(epollfd.get(), rfd.get(), 0, kMagicConstant));
- struct epoll_event result[kFDsPerEpoll];
- // Initially, rfd should not generate any events of interest.
- ASSERT_THAT(epoll_wait(epollfd.get(), result, kFDsPerEpoll, 0),
- SyscallSucceedsWithValue(0));
- // Close the write end of the pipe.
- wfd.reset();
- // rfd should now generate EPOLLHUP, which EPOLL_CTL_ADD unconditionally adds
- // to the set of events of interest.
- ASSERT_THAT(epoll_wait(epollfd.get(), result, kFDsPerEpoll, 0),
- SyscallSucceedsWithValue(1));
- EXPECT_EQ(result[0].events, EPOLLHUP);
- EXPECT_EQ(result[0].data.u64, kMagicConstant);
-}
-
-} // namespace
-
-} // namespace testing
-} // namespace gvisor