blob: c88a765ff4c80222c41520349f5b9e6a4e8b44da [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 <poll.h>
#include <sys/socket.h>
#include <unistd.h>
#include <thread>
#include <fbl/unique_fd.h>
#include <zxtest/zxtest.h>
#include "predicates.h"
namespace {
// While POSIX doesn't spell out the behavior of polling on 0 fds
// specifically, we guarantee that it is equivalent to a sleep. There
// are a number of reasons for this choice.
//
// 1. This matches many people's mental model of the operation. That
// is: poll returns before its timeout if one of the given fds has
// an event, or if a signal occurs; when here are no fds to have
// events, there can be no events!
//
// 2. This allows the idiom of poll(NULL, 0, timeout) to work on fdio.
// Even if the historical motivations for this idiom on other
// UNIX-like systems do not apply to fdio, the source compatibility
// is desirable.
//
// 3. This matches the behavior of Zircon's zx_object_wait_many. This
// is desirable both from an implementation perspective, and for
// the sake of those reasoning about waiting on things in different
// forms on Fuchsia.
//
// 4. Generally, we have biased towards providing a posix-lite
// interface that, when ambiguities arise, resembles Linux more
// than other systems. Given that fdio provides ppoll, which
// historically was a Linux-only syscall (recent FreeBSDs also have
// it), it is most consistent, and most useful to our clients, to
// do what Linux does in its poll semantics.
// These tests use poll/ppoll to sleep, and check both the return
// value, and that the an appropriate amount of time has passed.
//
// Since the point of these tests is not to establish that the
// underlying primitives sleep for the correct amount of time, the
// assertion about the amount of time advanced is pretty loose. We
// instruct poll/ppoll sleep for 10 milliseconds, and assert that at
// least 1 millisecond has elapsed. With those values, it is
// implausible that execution fell straight through the body of
// poll/ppoll without blocking, while also allowing this test to not
// have to reason about the precise guarantees made by the monotonic
// clock or blocking system calls.
TEST(Poll, PollZeroFds) {
constexpr std::chrono::duration minimum_duration = std::chrono::milliseconds(1);
const auto begin = std::chrono::steady_clock::now();
ASSERT_EQ(poll(nullptr, 0, std::chrono::milliseconds(minimum_duration).count()), 0, "%s",
strerror(errno));
ASSERT_GE(std::chrono::steady_clock::now() - begin, minimum_duration);
}
TEST(Poll, PPollZeroFds) {
constexpr std::chrono::duration minimum_duration = std::chrono::milliseconds(1);
const struct timespec timeout = {
.tv_nsec = std::chrono::nanoseconds(minimum_duration).count(),
};
const auto begin = std::chrono::steady_clock::now();
ASSERT_SUCCESS(ppoll(nullptr, 0, &timeout, nullptr));
ASSERT_GE(std::chrono::steady_clock::now() - begin, minimum_duration);
}
TEST(Poll, PPollNegativeTimeout) {
{
const struct timespec timeout_ts = {
.tv_sec = -1,
};
ASSERT_EQ(ppoll(nullptr, 0, &timeout_ts, nullptr), -1);
ASSERT_ERRNO(EINVAL);
}
{
const struct timespec timeout_ts = {
.tv_nsec = -1,
};
ASSERT_EQ(ppoll(nullptr, 0, &timeout_ts, nullptr), -1);
ASSERT_ERRNO(EINVAL);
}
}
TEST(Poll, PPollInvalidTimeout) {
const struct timespec timeout_ts = {
.tv_nsec = 1000000000,
};
ASSERT_EQ(ppoll(nullptr, 0, &timeout_ts, nullptr), -1);
ASSERT_ERRNO(EINVAL);
}
class Pipe : public zxtest::Test {
protected:
void SetUp() override {
int fds[2];
ASSERT_SUCCESS(pipe(fds));
for (size_t i = 0; i < fds_.size(); ++i) {
fds_[i].reset(fds[i]);
}
}
void PPollTimespec(const struct timespec* ts) {
std::thread t([this]() {
// Sleep to try to ensure the write happens after the poll.
std::this_thread::sleep_for(std::chrono::milliseconds(5));
constexpr char message[] = "hello";
EXPECT_EQ(write(fds()[1].get(), message, sizeof(message)), ssize_t(sizeof(message)));
});
struct pollfd pfds[] = {{
.fd = fds()[0].get(),
.events = POLLIN,
}};
EXPECT_EQ(ppoll(pfds, std::size(pfds), ts, nullptr), 1);
t.join();
}
const std::array<fbl::unique_fd, 2>& fds() { return fds_; }
private:
std::array<fbl::unique_fd, 2> fds_;
};
TEST_F(Pipe, PPoll) { PPollTimespec(nullptr); }
TEST_F(Pipe, PPollOverflow) {
constexpr unsigned int nanoseconds_in_seconds = 1000000000;
const struct timespec ts = {
.tv_sec = UINT64_MAX / nanoseconds_in_seconds,
.tv_nsec = UINT64_MAX % nanoseconds_in_seconds,
};
PPollTimespec(&ts);
}
TEST_F(Pipe, PPollImmediateTimeout) {
struct timespec timeout = {};
struct pollfd pfds[] = {{
.fd = fds()[0].get(),
.events = POLLIN,
}};
EXPECT_SUCCESS(ppoll(pfds, std::size(pfds), &timeout, nullptr));
}
TEST_F(Pipe, Pipe) {
constexpr int kTimeout = 10000;
{
char c;
EXPECT_EQ(write(fds()[1].get(), &c, sizeof(c)), ssize_t(sizeof(c)), "%s", strerror(errno));
struct pollfd pfds[] = {{
.fd = fds()[0].get(),
.events = POLLIN,
},
{
.fd = fds()[1].get(),
.events = POLLOUT,
}};
int n = poll(pfds, std::size(pfds), kTimeout);
EXPECT_GE(n, 0, "%s", strerror(errno));
EXPECT_EQ(n, ssize_t(std::size(pfds)));
EXPECT_EQ(pfds[0].revents, POLLIN);
EXPECT_EQ(pfds[1].revents, POLLOUT);
}
{
ASSERT_SUCCESS(close(fds()[1].get()));
struct pollfd pfds[] = {
{
.fd = fds()[0].get(),
#if defined(__Fuchsia__)
// TODO(https://fxbug.dev/42123845): For Linux parity, pipe wait_begin needs to always wait
// on ZXIO_SIGNAL_PEER_CLOSED and wait_end needs to set POLLHUP on seeing this.
.events = POLLIN,
#endif
},
{
.fd = fds()[1].get(),
}};
int n = poll(pfds, std::size(pfds), kTimeout);
EXPECT_GE(n, 0, "%s", strerror(errno));
EXPECT_EQ(n, ssize_t(std::size(pfds)));
EXPECT_EQ(pfds[0].revents,
#if defined(__Fuchsia__)
// TODO(https://fxbug.dev/42123845): For Linux parity, pipe wait_begin needs to always
// wait on ZXIO_SIGNAL_PEER_CLOSED and wait_end needs to set POLLHUP on seeing this.
POLLIN
#else
POLLHUP
#endif
);
EXPECT_EQ(pfds[1].revents, POLLNVAL);
}
}
TEST(Poll, ManyPipesNoEvents) {
int pipe_one_fds[2];
ASSERT_SUCCESS(pipe(pipe_one_fds));
fbl::unique_fd pipe_one_read_end(pipe_one_fds[0]);
fbl::unique_fd pipe_one_write_end(pipe_one_fds[1]);
int pipe_two_fds[2];
ASSERT_SUCCESS(pipe(pipe_two_fds));
fbl::unique_fd pipe_two_read_end(pipe_two_fds[0]);
fbl::unique_fd pipe_two_write_end(pipe_two_fds[1]);
struct pollfd pfds[] = {
{
.fd = pipe_one_write_end.get(),
.events = 0,
},
{
.fd = pipe_two_write_end.get(),
.events = 0,
},
};
EXPECT_EQ(poll(pfds, std::size(pfds), 5), 0, "error: %s", strerror(errno));
}
} // namespace