blob: 91413c350dd4f7fc0fbdace0e2bdf44cfe47a677 [file] [log] [blame]
// Copyright 2020 The Fuchsia 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 <fcntl.h>
#include <limits.h>
#include <sys/eventfd.h>
#include <sys/types.h>
#include <unistd.h>
#include <fbl/unique_fd.h>
#include <zxtest/zxtest.h>
TEST(EventFDTest, Unsupported) {
EXPECT_EQ(-1, eventfd(0, 39840));
ASSERT_EQ(EINVAL, errno, "errno incorrect");
}
TEST(EventFDTest, Smoke) {
fbl::unique_fd fd(eventfd(0, 0));
EXPECT_TRUE(fd.is_valid());
eventfd_t value = 7;
EXPECT_EQ(0, eventfd_write(fd.get(), value));
EXPECT_EQ(0, eventfd_read(fd.get(), &value));
EXPECT_EQ(7, value);
value = 8;
EXPECT_EQ(0, eventfd_write(fd.get(), value));
value = 3;
EXPECT_EQ(0, eventfd_write(fd.get(), value));
EXPECT_EQ(0, eventfd_read(fd.get(), &value));
EXPECT_EQ(11, value);
ASSERT_EQ(0, fcntl(fd.get(), F_SETFL, fcntl(fd.get(), F_GETFL) | O_NONBLOCK));
EXPECT_EQ(-1, eventfd_read(fd.get(), &value));
ASSERT_EQ(EAGAIN, errno, "errno incorrect");
}
TEST(EventFDTest, SmokeSemaphore) {
fbl::unique_fd fd(eventfd(0, EFD_SEMAPHORE));
EXPECT_TRUE(fd.is_valid());
eventfd_t value = 7;
EXPECT_EQ(0, eventfd_write(fd.get(), value));
EXPECT_EQ(0, eventfd_read(fd.get(), &value));
EXPECT_EQ(1, value);
// The event should now have a 6.
value = 3;
EXPECT_EQ(0, eventfd_write(fd.get(), value));
// The event should now have a 9.
for (size_t i = 0; i < 9; ++i) {
value = 424;
EXPECT_EQ(0, eventfd_read(fd.get(), &value));
EXPECT_EQ(1, value);
}
// The event should now have a 0.
ASSERT_EQ(0, fcntl(fd.get(), F_SETFL, fcntl(fd.get(), F_GETFL) | O_NONBLOCK));
EXPECT_EQ(-1, eventfd_read(fd.get(), &value));
ASSERT_EQ(EAGAIN, errno, "errno incorrect");
}
TEST(EventFDTest, InitialValue) {
fbl::unique_fd fd(eventfd(343, 0));
EXPECT_TRUE(fd.is_valid());
eventfd_t value = 5464;
EXPECT_EQ(0, eventfd_read(fd.get(), &value));
EXPECT_EQ(343, value);
}
TEST(EventFDTest, Cloexec) {
fbl::unique_fd fd(eventfd(0, EFD_CLOEXEC));
EXPECT_TRUE(fd.is_valid());
int flags = fcntl(fd.get(), F_GETFL);
EXPECT_FALSE(flags & FD_CLOEXEC);
flags = fcntl(fd.get(), F_GETFD);
EXPECT_TRUE(flags & FD_CLOEXEC);
}
TEST(EventFDTest, NonBlock) {
fbl::unique_fd fd(eventfd(0, EFD_NONBLOCK));
EXPECT_TRUE(fd.is_valid());
int flags = fcntl(fd.get(), F_GETFL);
EXPECT_TRUE(flags & O_NONBLOCK);
}
TEST(EventFDTest, WriteLimits) {
fbl::unique_fd fd(eventfd(0, EFD_NONBLOCK));
EXPECT_TRUE(fd.is_valid());
EXPECT_EQ(-1, eventfd_write(fd.get(), UINT64_MAX));
ASSERT_EQ(EINVAL, errno, "errno incorrect");
EXPECT_EQ(0, eventfd_write(fd.get(), UINT64_MAX - 5));
EXPECT_EQ(0, eventfd_write(fd.get(), 3));
EXPECT_EQ(-1, eventfd_write(fd.get(), 10));
ASSERT_EQ(EAGAIN, errno, "errno incorrect");
EXPECT_EQ(-1, eventfd_write(fd.get(), 2));
ASSERT_EQ(EAGAIN, errno, "errno incorrect");
EXPECT_EQ(0, eventfd_write(fd.get(), 1));
eventfd_t value = 5464;
EXPECT_EQ(0, eventfd_read(fd.get(), &value));
EXPECT_EQ(UINT64_MAX - 1, value);
}
static void check_signals(int fd, bool* out_is_readable, bool* out_is_writable) {
fd_set rfds;
FD_ZERO(&rfds);
FD_SET(fd, &rfds);
fd_set wfds;
FD_ZERO(&wfds);
FD_SET(fd, &wfds);
struct timeval timeout = {};
EXPECT_LT(0, select(fd + 1, &rfds, &wfds, nullptr, &timeout));
*out_is_readable = FD_ISSET(fd, &rfds);
*out_is_writable = FD_ISSET(fd, &wfds);
}
TEST(EventFDTest, Signals) {
fbl::unique_fd fd(eventfd(0, EFD_NONBLOCK));
EXPECT_TRUE(fd.is_valid());
bool is_readable = false;
bool is_writable = false;
ASSERT_NO_FAILURES(check_signals(fd.get(), &is_readable, &is_writable));
EXPECT_FALSE(is_readable);
EXPECT_TRUE(is_writable);
EXPECT_EQ(0, eventfd_write(fd.get(), 75));
ASSERT_NO_FAILURES(check_signals(fd.get(), &is_readable, &is_writable));
EXPECT_TRUE(is_readable);
EXPECT_TRUE(is_writable);
EXPECT_EQ(0, eventfd_write(fd.get(), UINT64_MAX - 76));
ASSERT_NO_FAILURES(check_signals(fd.get(), &is_readable, &is_writable));
EXPECT_TRUE(is_readable);
EXPECT_FALSE(is_writable);
eventfd_t value = 5464;
EXPECT_EQ(0, eventfd_read(fd.get(), &value));
EXPECT_EQ(UINT64_MAX - 1, value);
ASSERT_NO_FAILURES(check_signals(fd.get(), &is_readable, &is_writable));
EXPECT_FALSE(is_readable);
EXPECT_TRUE(is_writable);
EXPECT_EQ(0, eventfd_write(fd.get(), 95));
ASSERT_NO_FAILURES(check_signals(fd.get(), &is_readable, &is_writable));
EXPECT_TRUE(is_readable);
EXPECT_TRUE(is_writable);
EXPECT_EQ(-1, eventfd_write(fd.get(), UINT64_MAX));
ASSERT_EQ(EINVAL, errno, "errno incorrect");
ASSERT_NO_FAILURES(check_signals(fd.get(), &is_readable, &is_writable));
EXPECT_TRUE(is_readable);
EXPECT_TRUE(is_writable);
EXPECT_EQ(-1, eventfd_write(fd.get(), UINT64_MAX - 1));
ASSERT_EQ(EAGAIN, errno, "errno incorrect");
ASSERT_NO_FAILURES(check_signals(fd.get(), &is_readable, &is_writable));
EXPECT_TRUE(is_readable);
#ifdef __Fuchsia__
// We get a different result than Linux here becaues we model blocking and
// non-blocking I/O more uniformly. Linux appears to block the write that
// would overflow while still having |select| report the eventfd as writable.
// The way we set things up, |select| and |write| need to give consistent
// views (or else a write that tries to block on an overflow would spin hot),
// which means we have |select| report the eventfd as non-writable here.
EXPECT_FALSE(is_writable);
#else
EXPECT_TRUE(is_writable);
#endif
value = 5464;
EXPECT_EQ(0, eventfd_read(fd.get(), &value));
EXPECT_EQ(95, value);
ASSERT_NO_FAILURES(check_signals(fd.get(), &is_readable, &is_writable));
EXPECT_FALSE(is_readable);
EXPECT_TRUE(is_writable);
}
TEST(EventFDTest, SemaphoreSignals) {
fbl::unique_fd fd(eventfd(0, EFD_SEMAPHORE | EFD_NONBLOCK));
EXPECT_TRUE(fd.is_valid());
EXPECT_EQ(0, eventfd_write(fd.get(), UINT64_MAX - 1));
bool is_readable = false;
bool is_writable = false;
ASSERT_NO_FAILURES(check_signals(fd.get(), &is_readable, &is_writable));
EXPECT_TRUE(is_readable);
EXPECT_FALSE(is_writable);
eventfd_t value = 5464;
EXPECT_EQ(0, eventfd_read(fd.get(), &value));
EXPECT_EQ(1, value);
ASSERT_NO_FAILURES(check_signals(fd.get(), &is_readable, &is_writable));
EXPECT_TRUE(is_readable);
EXPECT_TRUE(is_writable);
EXPECT_EQ(0, eventfd_read(fd.get(), &value));
EXPECT_EQ(1, value);
ASSERT_NO_FAILURES(check_signals(fd.get(), &is_readable, &is_writable));
EXPECT_TRUE(is_readable);
EXPECT_TRUE(is_writable);
EXPECT_EQ(-1, eventfd_write(fd.get(), 12));
ASSERT_EQ(EAGAIN, errno, "errno incorrect");
ASSERT_NO_FAILURES(check_signals(fd.get(), &is_readable, &is_writable));
EXPECT_TRUE(is_readable);
#ifdef __Fuchsia__
// We get a different result than Linux here becaues we model blocking and
// non-blocking I/O more uniformly. Linux appears to block the write that
// would overflow while still having |select| report the eventfd as writable.
// The way we set things up, |select| and |write| need to give consistent
// views (or else a write that tries to block on an overflow would spin hot),
// which means we have |select| report the eventfd as non-writable here.
EXPECT_FALSE(is_writable);
#else
EXPECT_TRUE(is_writable);
#endif
EXPECT_EQ(0, eventfd_read(fd.get(), &value));
EXPECT_EQ(1, value);
ASSERT_NO_FAILURES(check_signals(fd.get(), &is_readable, &is_writable));
EXPECT_TRUE(is_readable);
EXPECT_TRUE(is_writable);
}
TEST(EventFDTest, BufferLimits) {
fbl::unique_fd fd(eventfd(42, EFD_SEMAPHORE | EFD_NONBLOCK));
EXPECT_TRUE(fd.is_valid());
char buffer[64];
memset(buffer, 0, sizeof(buffer));
EXPECT_EQ(-1, read(fd.get(), buffer, 7));
ASSERT_EQ(EINVAL, errno, "errno incorrect");
EXPECT_EQ(-1, write(fd.get(), buffer, 7));
ASSERT_EQ(EINVAL, errno, "errno incorrect");
}