blob: 0cb213bccb0745544ba2535b98372a385aad3369 [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 <time.h>
#include <zxtest/zxtest.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.
timespec now() {
timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return ts;
}
timespec delta(const timespec& before, const timespec& after) {
timespec ts = after;
ts.tv_sec -= before.tv_sec;
if (ts.tv_nsec < before.tv_nsec) {
ts.tv_sec -= 1;
ts.tv_nsec += 1000000000ll;
}
ts.tv_nsec -= before.tv_nsec;
return ts;
}
TEST(Poll, PollZeroFds) {
timespec before = now();
// Ten millisecond timeout.
int timeout = 10;
int ret = poll(nullptr, 0, timeout);
timespec after = now();
// poll should have returned 0.
ASSERT_EQ(ret, 0);
// Time should have advanced, at least a millisecond.
ASSERT_TRUE(before.tv_sec < after.tv_sec ||
(before.tv_sec == after.tv_sec && before.tv_nsec <= after.tv_nsec));
timespec interval = delta(before, after);
ASSERT_TRUE(interval.tv_sec > 0 || interval.tv_nsec >= 1000000ll);
}
TEST(Poll, PpollZeroFds) {
timespec before = now();
// Ten millisecond timeout.
const timespec timeout = {0, 10 * 1000 * 1000};
int ret = ppoll(nullptr, 0, &timeout, nullptr);
timespec after = now();
// poll should have returned 0.
ASSERT_EQ(ret, 0);
// Time should have advanced, at least a millisecond.
ASSERT_TRUE(before.tv_sec < after.tv_sec ||
(before.tv_sec == after.tv_sec && before.tv_nsec <= after.tv_nsec));
timespec interval = delta(before, after);
ASSERT_TRUE(interval.tv_sec > 0 || interval.tv_nsec >= 1000000ll);
}
} // namespace