// 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 <pthread.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/epoll.h> #include <sys/stat.h> #include <sys/types.h> #include <unistd.h> #include "gtest/gtest.h" #include "test/util/epoll_util.h" #include "test/util/eventfd_util.h" #include "test/util/test_util.h" #include "test/util/thread_util.h" namespace gvisor { namespace testing { namespace { TEST(EventfdTest, Nonblock) { FileDescriptor efd = ASSERT_NO_ERRNO_AND_VALUE(NewEventFD(0, EFD_NONBLOCK | EFD_SEMAPHORE)); uint64_t l; ASSERT_THAT(read(efd.get(), &l, sizeof(l)), SyscallFailsWithErrno(EAGAIN)); l = 1; ASSERT_THAT(write(efd.get(), &l, sizeof(l)), SyscallSucceeds()); l = 0; ASSERT_THAT(read(efd.get(), &l, sizeof(l)), SyscallSucceeds()); EXPECT_EQ(l, 1); ASSERT_THAT(read(efd.get(), &l, sizeof(l)), SyscallFailsWithErrno(EAGAIN)); } void* read_three_times(void* arg) { int efd = *reinterpret_cast<int*>(arg); uint64_t l; EXPECT_THAT(read(efd, &l, sizeof(l)), SyscallSucceedsWithValue(sizeof(l))); EXPECT_THAT(read(efd, &l, sizeof(l)), SyscallSucceedsWithValue(sizeof(l))); EXPECT_THAT(read(efd, &l, sizeof(l)), SyscallSucceedsWithValue(sizeof(l))); return nullptr; } TEST(EventfdTest, BlockingWrite) { FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(NewEventFD(0, EFD_SEMAPHORE)); int efd = fd.get(); pthread_t p; ASSERT_THAT(pthread_create(&p, nullptr, read_three_times, reinterpret_cast<void*>(&efd)), SyscallSucceeds()); uint64_t l = 1; ASSERT_THAT(write(efd, &l, sizeof(l)), SyscallSucceeds()); EXPECT_EQ(l, 1); ASSERT_THAT(write(efd, &l, sizeof(l)), SyscallSucceeds()); EXPECT_EQ(l, 1); ASSERT_THAT(write(efd, &l, sizeof(l)), SyscallSucceeds()); EXPECT_EQ(l, 1); ASSERT_THAT(pthread_join(p, nullptr), SyscallSucceeds()); } TEST(EventfdTest, SmallWrite) { FileDescriptor efd = ASSERT_NO_ERRNO_AND_VALUE(NewEventFD(0, EFD_NONBLOCK | EFD_SEMAPHORE)); uint64_t l = 16; ASSERT_THAT(write(efd.get(), &l, 4), SyscallFailsWithErrno(EINVAL)); } TEST(EventfdTest, SmallRead) { FileDescriptor efd = ASSERT_NO_ERRNO_AND_VALUE(NewEventFD(0, EFD_NONBLOCK | EFD_SEMAPHORE)); uint64_t l = 1; ASSERT_THAT(write(efd.get(), &l, sizeof(l)), SyscallSucceeds()); l = 0; ASSERT_THAT(read(efd.get(), &l, 4), SyscallFailsWithErrno(EINVAL)); } TEST(EventfdTest, BigWrite) { FileDescriptor efd = ASSERT_NO_ERRNO_AND_VALUE(NewEventFD(0, EFD_NONBLOCK | EFD_SEMAPHORE)); uint64_t big[16]; big[0] = 16; ASSERT_THAT(write(efd.get(), big, sizeof(big)), SyscallSucceeds()); } TEST(EventfdTest, BigRead) { FileDescriptor efd = ASSERT_NO_ERRNO_AND_VALUE(NewEventFD(0, EFD_NONBLOCK | EFD_SEMAPHORE)); uint64_t l = 1; ASSERT_THAT(write(efd.get(), &l, sizeof(l)), SyscallSucceeds()); uint64_t big[16]; ASSERT_THAT(read(efd.get(), big, sizeof(big)), SyscallSucceeds()); EXPECT_EQ(big[0], 1); } TEST(EventfdTest, BigWriteBigRead) { FileDescriptor efd = ASSERT_NO_ERRNO_AND_VALUE(NewEventFD(0, EFD_NONBLOCK | EFD_SEMAPHORE)); uint64_t l[16]; l[0] = 16; ASSERT_THAT(write(efd.get(), l, sizeof(l)), SyscallSucceeds()); ASSERT_THAT(read(efd.get(), l, sizeof(l)), SyscallSucceeds()); EXPECT_EQ(l[0], 1); } TEST(EventfdTest, SpliceFromPipePartialSucceeds) { int pipes[2]; ASSERT_THAT(pipe2(pipes, O_NONBLOCK), SyscallSucceeds()); const FileDescriptor pipe_rfd(pipes[0]); const FileDescriptor pipe_wfd(pipes[1]); constexpr uint64_t kVal{1}; FileDescriptor efd = ASSERT_NO_ERRNO_AND_VALUE(NewEventFD(0, EFD_NONBLOCK)); uint64_t event_array[2]; event_array[0] = kVal; event_array[1] = kVal; ASSERT_THAT(write(pipe_wfd.get(), event_array, sizeof(event_array)), SyscallSucceedsWithValue(sizeof(event_array))); EXPECT_THAT(splice(pipe_rfd.get(), /*__offin=*/nullptr, efd.get(), /*__offout=*/nullptr, sizeof(event_array[0]) + 1, SPLICE_F_NONBLOCK), SyscallSucceedsWithValue(sizeof(event_array[0]))); uint64_t val; ASSERT_THAT(read(efd.get(), &val, sizeof(val)), SyscallSucceedsWithValue(sizeof(val))); EXPECT_EQ(val, kVal); } // NotifyNonZero is inherently racy, so random save is disabled. TEST(EventfdTest, NotifyNonZero_NoRandomSave) { // Waits will time out at 10 seconds. constexpr int kEpollTimeoutMs = 10000; // Create an eventfd descriptor. FileDescriptor efd = ASSERT_NO_ERRNO_AND_VALUE(NewEventFD(7, EFD_NONBLOCK | EFD_SEMAPHORE)); // Create an epoll fd to listen to efd. FileDescriptor epollfd = ASSERT_NO_ERRNO_AND_VALUE(NewEpollFD()); // Add efd to epoll. ASSERT_NO_ERRNO( RegisterEpollFD(epollfd.get(), efd.get(), EPOLLIN | EPOLLET, efd.get())); // Use epoll to get a value from efd. struct epoll_event out_ev; int wait_out = epoll_wait(epollfd.get(), &out_ev, 1, kEpollTimeoutMs); EXPECT_EQ(wait_out, 1); EXPECT_EQ(efd.get(), out_ev.data.fd); uint64_t val = 0; ASSERT_THAT(read(efd.get(), &val, sizeof(val)), SyscallSucceeds()); EXPECT_EQ(val, 1); // Start a thread that, after this thread blocks on epoll_wait, will write to // efd. This is racy -- it's possible that this write will happen after // epoll_wait times out. ScopedThread t([&efd] { sleep(5); uint64_t val = 1; EXPECT_THAT(write(efd.get(), &val, sizeof(val)), SyscallSucceedsWithValue(sizeof(val))); }); // epoll_wait should return once the thread writes. wait_out = epoll_wait(epollfd.get(), &out_ev, 1, kEpollTimeoutMs); EXPECT_EQ(wait_out, 1); EXPECT_EQ(efd.get(), out_ev.data.fd); val = 0; ASSERT_THAT(read(efd.get(), &val, sizeof(val)), SyscallSucceeds()); EXPECT_EQ(val, 1); } } // namespace } // namespace testing } // namespace gvisor