blob: a4903b7a55d39b46ecccbc2d6c6c9910add43d65 [file] [log] [blame]
// Copyright 2024 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 <signal.h>
#include <sys/signalfd.h>
#include <sys/wait.h>
#include <unistd.h>
#include <gtest/gtest.h>
namespace {
TEST(Signalfd, SignalDoesNotTriggerSpuriousWakeup) {
// Block SIGCHLD to ensure it is caught by the signalfd.
sigset_t block_mask;
sigemptyset(&block_mask);
sigaddset(&block_mask, SIGCHLD);
sigaddset(&block_mask, SIGTERM);
sigaddset(&block_mask, SIGINT);
ASSERT_NE(sigprocmask(SIG_BLOCK, &block_mask, nullptr), -1);
// Create a signalfd for termination signals (SIGTERM, SIGINT).
sigset_t term_mask;
sigemptyset(&term_mask);
sigaddset(&term_mask, SIGTERM);
sigaddset(&term_mask, SIGINT);
int sfd_term = signalfd(-1, &term_mask, 0);
ASSERT_NE(sfd_term, -1);
// Create a signalfd for the child signal (SIGCHLD).
sigset_t chld_mask;
sigemptyset(&chld_mask);
sigaddset(&chld_mask, SIGCHLD);
int sfd_chld = signalfd(-1, &chld_mask, 0);
ASSERT_NE(sfd_chld, -1);
// Create a simple child process that exits immediately and therefore generates a SIGCHLD.
pid_t pid = fork();
ASSERT_NE(pid, -1);
if (pid == 0) {
// Child process.
_exit(0);
}
// Parent process can poll both fds for events using an arbitrary timeout.
struct pollfd fds[2];
fds[0].fd = sfd_term;
fds[0].events = POLLIN;
fds[1].fd = sfd_chld;
fds[1].events = POLLIN;
int num_events = poll(fds, 2, 1000);
// Expect only one wakeup as only one signalfd cares about SIGCHLD.
ASSERT_EQ(num_events, 1);
// The SIGTERM/SIGINT signalfd should have no `revents`.
EXPECT_EQ(fds[0].revents, 0);
// The SIGCHLD should have `revents` for POLLIN as the signal was received.
EXPECT_EQ(fds[1].revents, POLLIN);
// Read from the signalfd to confirm it was SIGCHLD.
struct signalfd_siginfo ssi;
ssize_t bytes_read = read(sfd_chld, &ssi, sizeof(ssi));
ASSERT_EQ(static_cast<size_t>(bytes_read), sizeof(ssi));
EXPECT_EQ(ssi.ssi_signo, static_cast<uint32_t>(SIGCHLD));
// Clean up resources and child.
close(sfd_term);
close(sfd_chld);
waitpid(pid, nullptr, 0);
ASSERT_NE(sigprocmask(SIG_UNBLOCK, &block_mask, nullptr), -1);
}
} // namespace