Skip to content

Commit c8ef03e

Browse files
committed
libutil/file-descriptor: handle EAGAIN in read/write operations
We now see exception beeing thrown when remote building in master because of writing to a non-blocking file descriptor from our json logger. > #0 0x00007f2ea97aea9c in __pthread_kill_implementation () from /nix/store/wn7v2vhyyyi6clcyn0s9ixvl7d4d87ic-glibc-2.40-36/lib/libc.so.6 > #1 0x00007f2ea975c576 in raise () from /nix/store/wn7v2vhyyyi6clcyn0s9ixvl7d4d87ic-glibc-2.40-36/lib/libc.so.6 > #2 0x00007f2ea9744935 in abort () from /nix/store/wn7v2vhyyyi6clcyn0s9ixvl7d4d87ic-glibc-2.40-36/lib/libc.so.6 > #3 0x00007f2ea99e8c2b in __gnu_cxx::__verbose_terminate_handler() [clone .cold] () from /nix/store/ybjcla5bhj8g1y84998pn4a2drfxybkv-gcc-13.3.0-lib/lib/libstdc++.so.6 > #4 0x00007f2ea99f820a in __cxxabiv1::__terminate(void (*)()) () from /nix/store/ybjcla5bhj8g1y84998pn4a2drfxybkv-gcc-13.3.0-lib/lib/libstdc++.so.6 > #5 0x00007f2ea99f8275 in std::terminate() () from /nix/store/ybjcla5bhj8g1y84998pn4a2drfxybkv-gcc-13.3.0-lib/lib/libstdc++.so.6 > #6 0x00007f2ea99f84c7 in __cxa_throw () from /nix/store/ybjcla5bhj8g1y84998pn4a2drfxybkv-gcc-13.3.0-lib/lib/libstdc++.so.6 > #7 0x00007f2eaa5035c2 in nix::writeFull (fd=2, s=..., allowInterrupts=true) at ../unix/file-descriptor.cc:43 > #8 0x00007f2eaa5633c4 in nix::JSONLogger::write (this=this@entry=0x249a7d40, json=...) at /nix/store/4krab2h0hd4wvxxmscxrw21pl77j4i7j-gcc-13.3.0/include/c++/13.3.0/bits/char_traits.h:358 > NixOS#9 0x00007f2eaa5658d7 in nix::JSONLogger::logEI (this=<optimized out>, ei=...) at ../logging.cc:242 > NixOS#10 0x00007f2ea9c5d048 in nix::Logger::logEI (ei=..., lvl=nix::lvlError, this=0x249a7d40) at /nix/store/a7cq5bqh0ryvnkv4m19ffchnvi8l9qx6-nix-util-2.27.0-dev/include/nix/logging.hh:108 > NixOS#11 nix::handleExceptions (programName="nix", fun=...) at ../shared.cc:343 > NixOS#12 0x0000000000465b1f in main (argc=<optimized out>, argv=<optimized out>) at /nix/store/4krab2h0hd4wvxxmscxrw21pl77j4i7j-gcc-13.3.0/include/c++/13.3.0/bits/allocator.h:163 > (gdb) frame 10 > NixOS#10 0x00007f2ea9c5d048 in nix::Logger::logEI (ei=..., lvl=nix::lvlError, this=0x249a7d40) at /nix/store/a7cq5bqh0ryvnkv4m19ffchnvi8l9qx6-nix-util-2.27.0-dev/include/nix/logging.hh:108 > 108 logEI(ei); So far only drainFD sets the non-blocking flag on a "readable" file descriptor, while this is a "writeable" file descriptor. It's not clear to me yet, why we see logs after that point, but it's also not that bad to handle EAGAIN in read/write functions after all.
1 parent 5df1975 commit c8ef03e

File tree

1 file changed

+40
-4
lines changed

1 file changed

+40
-4
lines changed

src/libutil/unix/file-descriptor.cc

+40-4
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,27 @@
55

66
#include <fcntl.h>
77
#include <unistd.h>
8+
#include <poll.h>
89

910
namespace nix {
1011

12+
namespace {
13+
14+
// This function is needed to handle non-blocking reads/writes. This is needed in the buildhook
15+
// somehow the json logger file descriptor ends up beeing non-blocking, where it shouldn't be.
16+
// TODO: get rid of buildhook
17+
void pollFD(int fd, int events)
18+
{
19+
struct pollfd pfd;
20+
pfd.fd = fd;
21+
pfd.events = events;
22+
int ret = poll(&pfd, 1, -1);
23+
if (ret == -1) {
24+
throw SysError("poll on file descriptor failed");
25+
}
26+
}
27+
}
28+
1129
std::string readFile(int fd)
1230
{
1331
struct stat st;
@@ -17,14 +35,18 @@ std::string readFile(int fd)
1735
return drainFD(fd, true, st.st_size);
1836
}
1937

20-
2138
void readFull(int fd, char * buf, size_t count)
2239
{
2340
while (count) {
2441
checkInterrupt();
2542
ssize_t res = read(fd, buf, count);
2643
if (res == -1) {
27-
if (errno == EINTR) continue;
44+
switch (errno) {
45+
case EINTR: continue;
46+
case EAGAIN:
47+
pollFD(fd, POLLIN);
48+
continue;
49+
}
2850
throw SysError("reading from file");
2951
}
3052
if (res == 0) throw EndOfFile("unexpected end-of-file");
@@ -39,8 +61,15 @@ void writeFull(int fd, std::string_view s, bool allowInterrupts)
3961
while (!s.empty()) {
4062
if (allowInterrupts) checkInterrupt();
4163
ssize_t res = write(fd, s.data(), s.size());
42-
if (res == -1 && errno != EINTR)
64+
if (res == -1) {
65+
switch (errno) {
66+
case EINTR: continue;
67+
case EAGAIN:
68+
pollFD(fd, POLLOUT);
69+
continue;
70+
}
4371
throw SysError("writing to file");
72+
}
4473
if (res > 0)
4574
s.remove_prefix(res);
4675
}
@@ -56,8 +85,15 @@ std::string readLine(int fd, bool eofOk)
5685
// FIXME: inefficient
5786
ssize_t rd = read(fd, &ch, 1);
5887
if (rd == -1) {
59-
if (errno != EINTR)
88+
switch (errno) {
89+
case EINTR: continue;
90+
case EAGAIN: {
91+
pollFD(fd, POLLIN);
92+
continue;
93+
}
94+
default:
6095
throw SysError("reading a line");
96+
}
6197
} else if (rd == 0) {
6298
if (eofOk)
6399
return s;

0 commit comments

Comments
 (0)