Files
Jade Lovelace 69ba3c92db fix: Terminate daemons properly on Ctrl-C on macOS
This was an absolute nightmare to diagnose. It turns out there's a
kernel bug: poll with events = POLLHUP will receive an event for NOT
POLLHUP internally in the kernel, delete their event subscription, and
then not receive events for any HUP later. lol! lmao!!

We choose to use plain old EVFILT_READ because the watched fd can be
either a socket or a pipe and it's preferable to eat some spurious
wakeups than have separate paths for those. The alternative is using
EVFILT_SOCK, a private API that's existed for years and which netty
uses for its sockets, but that doesn't work on pipes.

Fixes: https://git.lix.systems/lix-project/lix/issues/729
Change-Id: If72b5d7a39f00320a9acccdbe81121cdb1a04c45
2025-05-01 12:22:27 -07:00

67 lines
1.6 KiB
C++

#include "lix/libutil/monitor-fd.hh"
#include "lix/libutil/error.hh"
#include "lix/libutil/file-descriptor.hh"
#include <atomic>
#include <future>
#include <gtest/gtest.h>
#include <sys/socket.h>
using namespace std::literals::chrono_literals;
namespace nix {
TEST(MonitorFdHup, works)
{
int socks[2];
int rv = socketpair(AF_UNIX, SOCK_STREAM, 0, socks);
if (rv) {
throw SysError("socketpair");
}
auto sock1 = AutoCloseFD{socks[0]};
auto sock2 = AutoCloseFD{socks[1]};
std::promise<void> called;
MonitorFdHup monitor(sock1.get(), [&called]() { called.set_value(); });
sock2.close();
// 10 seconds should certainly do it.
auto status = called.get_future().wait_for(10s);
ASSERT_EQ(status, std::future_status::ready);
}
// Ensures that it also works with pipes.
TEST(MonitorFdHup, works_with_pipes)
{
Pipe pipes{};
pipes.create();
std::promise<void> called;
MonitorFdHup monitor(pipes.readSide.get(), [&called]() { called.set_value(); });
pipes.writeSide.close();
// 10 seconds should certainly do it.
auto status = called.get_future().wait_for(10s);
ASSERT_EQ(status, std::future_status::ready);
}
// Ensures that destroying the MonitorFdHup causes it to actually go away.
TEST(MonitorFdHup, destroys_safely)
{
int socks[2];
int rv = socketpair(AF_UNIX, SOCK_STREAM, 0, socks);
if (rv) {
throw SysError("socketpair");
}
auto sock1 = AutoCloseFD{socks[0]};
auto sock2 = AutoCloseFD{socks[1]};
{
MonitorFdHup monitor(sock1.get(), []() { abort(); });
}
}
}