diff options
Diffstat (limited to 'test/syscalls/linux/epoll.cc')
-rw-r--r-- | test/syscalls/linux/epoll.cc | 445 |
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 |