aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorjade <lix@jade.fyi>2024-07-18 17:40:32 +0000
committerGerrit Code Review <gerrit@localhost>2024-07-18 17:40:32 +0000
commit5ee1e6ea9887a54f0af3a66528abc04b17611516 (patch)
tree913e95fb7149b0e308fb054a53c990bc77eac7ff /src
parent7b1abf81076c93a84becc310722013e44d850002 (diff)
parent702f02c31f80536ce3b5953a8501677f0bf4977b (diff)
Merge changes Ib20e9aa0,I178a038b,I29c7de04 into main
* changes: docs: document the actual comparison rules instead of lies daemon: remove workaround for macOS kernel bug that seems fixed daemon: fix a crash bug "FATAL: exception not rethrown"
Diffstat (limited to 'src')
-rw-r--r--src/libutil/monitor-fd.hh73
1 files changed, 52 insertions, 21 deletions
diff --git a/src/libutil/monitor-fd.hh b/src/libutil/monitor-fd.hh
index 228fb13f8..ea6e89017 100644
--- a/src/libutil/monitor-fd.hh
+++ b/src/libutil/monitor-fd.hh
@@ -10,6 +10,8 @@
#include <unistd.h>
#include <signal.h>
+#include "error.hh"
+#include "file-descriptor.hh"
#include "signals.hh"
namespace nix {
@@ -19,28 +21,46 @@ class MonitorFdHup
{
private:
std::thread thread;
+ /**
+ * Pipe used to interrupt the poll()ing in the monitoring thread.
+ */
+ Pipe terminatePipe;
+ std::atomic_bool quit = false;
public:
MonitorFdHup(int fd)
{
- thread = std::thread([fd]() {
- while (true) {
+ terminatePipe.create();
+ auto &quit_ = this->quit;
+ int terminateFd = terminatePipe.readSide.get();
+ thread = std::thread([fd, terminateFd, &quit_]() {
+ while (!quit_) {
/* Wait indefinitely until a POLLHUP occurs. */
- struct pollfd fds[1];
+ struct pollfd fds[2];
fds[0].fd = fd;
- /* Polling for no specific events (i.e. just waiting
- for an error/hangup) doesn't work on macOS
- anymore. So wait for read events and ignore
- them. */
- fds[0].events =
- #ifdef __APPLE__
- POLLRDNORM
- #else
- 0
- #endif
- ;
- auto count = poll(fds, 1, -1);
- if (count == -1) abort(); // can't happen
+ // There is a POSIX violation on macOS: you have to listen for
+ // at least POLLHUP to receive HUP events for a FD. POSIX says
+ // this is not so, and you should just receive them regardless,
+ // however, as of our testing on macOS 14.5, the events do not
+ // get delivered in such a case.
+ //
+ // This is allegedly filed as rdar://37537852.
+ //
+ // Relevant code, which backs this up:
+ // https://github.com/apple-oss-distributions/xnu/blob/94d3b452840153a99b38a3a9659680b2a006908e/bsd/kern/sys_generic.c#L1751-L1758
+ fds[0].events = POLLHUP;
+ fds[1].fd = terminateFd;
+ fds[1].events = POLLIN;
+
+ auto count = poll(fds, 2, -1);
+ if (count == -1) {
+ if (errno == EINTR || errno == EAGAIN) {
+ // These are best dealt with by just trying again.
+ continue;
+ } else {
+ throw SysError("in MonitorFdHup poll()");
+ }
+ }
/* This shouldn't happen, but can on macOS due to a bug.
See rdar://37550628.
@@ -53,9 +73,16 @@ public:
triggerInterrupt();
break;
}
- /* This will only happen on macOS. We sleep a bit to
- avoid waking up too often if the client is sending
- input. */
+ // No reason to actually look at the pipe FD if that's what
+ // woke us, the only thing that actually matters is the quit
+ // flag.
+ if (quit_) {
+ break;
+ }
+ // On macOS, it is possible (although not observed on macOS
+ // 14.5) that in some limited cases on buggy kernel versions,
+ // all the non-POLLHUP events for the socket get delivered.
+ // Sleeping avoids pointlessly spinning a thread on those.
sleep(1);
}
});
@@ -63,8 +90,12 @@ public:
~MonitorFdHup()
{
- pthread_cancel(thread.native_handle());
- thread.join();
+ quit = true;
+ // Poke the thread out of its poll wait
+ writeFull(terminatePipe.writeSide.get(), "*", false);
+ if (thread.joinable()) {
+ thread.join();
+ }
}
};