Nix  2.93.0-dev
Lix: A modern, delicious implementation of the Nix package manager; unstable internal interfaces
Loading...
Searching...
No Matches
monitor-fd.hh
Go to the documentation of this file.
1#pragma once
3
4#include <thread>
5#include <atomic>
6
7#include <poll.h>
8#include <sys/types.h>
9#include <unistd.h>
10
11#include "lix/libutil/error.hh"
13#include "lix/libutil/signals.hh"
15
16namespace nix {
17
18
20{
21private:
22 std::thread thread;
26 Pipe terminatePipe;
27 std::atomic_bool quit = false;
28
29public:
30 MonitorFdHup(int fd)
31 {
32 terminatePipe.create();
33 auto &quit_ = this->quit;
34 int terminateFd = terminatePipe.readSide.get();
35 thread = std::thread([fd, terminateFd, &quit_]() {
36 setCurrentThreadName("MonitorFdHup");
37 while (!quit_) {
38 /* Wait indefinitely until a POLLHUP occurs. */
39 struct pollfd fds[2];
40 fds[0].fd = fd;
41 // There is a POSIX violation on macOS: you have to listen for
42 // at least POLLHUP to receive HUP events for a FD. POSIX says
43 // this is not so, and you should just receive them regardless,
44 // however, as of our testing on macOS 14.5, the events do not
45 // get delivered in such a case.
46 //
47 // This is allegedly filed as rdar://37537852.
48 //
49 // Relevant code, which backs this up:
50 // https://github.com/apple-oss-distributions/xnu/blob/94d3b452840153a99b38a3a9659680b2a006908e/bsd/kern/sys_generic.c#L1751-L1758
51 fds[0].events = POLLHUP;
52 fds[1].fd = terminateFd;
53 fds[1].events = POLLIN;
54
55 auto count = poll(fds, 2, -1);
56 if (count == -1) {
57 if (errno == EINTR || errno == EAGAIN) {
58 // These are best dealt with by just trying again.
59 continue;
60 } else {
61 throw SysError("in MonitorFdHup poll()");
62 }
63 }
64 /* This shouldn't happen, but can on macOS due to a bug.
65 See rdar://37550628.
66
67 This may eventually need a delay or further
68 coordination with the main thread if spinning proves
69 too harmful.
70 */
71 if (count == 0) continue;
72 if (fds[0].revents & POLLHUP) {
73 triggerInterrupt();
74 break;
75 }
76 // No reason to actually look at the pipe FD if that's what
77 // woke us, the only thing that actually matters is the quit
78 // flag.
79 if (quit_) {
80 break;
81 }
82 // On macOS, it is possible (although not observed on macOS
83 // 14.5) that in some limited cases on buggy kernel versions,
84 // all the non-POLLHUP events for the socket get delivered.
85 // Sleeping avoids pointlessly spinning a thread on those.
86 //
87 // N.B. excessive delay on this can cause the daemon connection
88 // thread to live longer than the client and lead to
89 // synchronization problems if clients assume that the server
90 // thread has released its temporary gc roots, etc.
91 // See https://github.com/NixOS/nix/pull/12714#discussion_r2009265904
92 usleep(1'000);
93 }
94 });
95 };
96
98 {
99 quit = true;
100 // Poke the thread out of its poll wait
101 writeFull(terminatePipe.writeSide.get(), "*", false);
102 if (thread.joinable()) {
103 thread.join();
104 }
105 }
106};
107
108
109}
Definition monitor-fd.hh:20
Definition file-descriptor.hh:61
Definition error.hh:208
This file defines two main structs/classes used in nix error handling.