Skip to content

Commit 0110a86

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 0110a86

File tree

1 file changed

+36
-4
lines changed

1 file changed

+36
-4
lines changed

src/libutil/unix/file-descriptor.cc

+36-4
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,23 @@
55

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

910
namespace nix {
1011

12+
namespace {
13+
void pollFD(int fd, int events)
14+
{
15+
struct pollfd pfd;
16+
pfd.fd = fd;
17+
pfd.events = events;
18+
int ret = poll(&pfd, 1, -1);
19+
if (ret == -1) {
20+
throw SysError("poll on file descriptor failed");
21+
}
22+
}
23+
}
24+
1125
std::string readFile(int fd)
1226
{
1327
struct stat st;
@@ -17,14 +31,18 @@ std::string readFile(int fd)
1731
return drainFD(fd, true, st.st_size);
1832
}
1933

20-
2134
void readFull(int fd, char * buf, size_t count)
2235
{
2336
while (count) {
2437
checkInterrupt();
2538
ssize_t res = read(fd, buf, count);
2639
if (res == -1) {
27-
if (errno == EINTR) continue;
40+
switch (errno) {
41+
case EINTR: continue;
42+
case EAGAIN:
43+
pollFD(fd, POLLIN);
44+
continue;
45+
}
2846
throw SysError("reading from file");
2947
}
3048
if (res == 0) throw EndOfFile("unexpected end-of-file");
@@ -39,8 +57,15 @@ void writeFull(int fd, std::string_view s, bool allowInterrupts)
3957
while (!s.empty()) {
4058
if (allowInterrupts) checkInterrupt();
4159
ssize_t res = write(fd, s.data(), s.size());
42-
if (res == -1 && errno != EINTR)
60+
if (res == -1) {
61+
switch (errno) {
62+
case EINTR: continue;
63+
case EAGAIN:
64+
pollFD(fd, POLLOUT);
65+
continue;
66+
}
4367
throw SysError("writing to file");
68+
}
4469
if (res > 0)
4570
s.remove_prefix(res);
4671
}
@@ -56,8 +81,15 @@ std::string readLine(int fd, bool eofOk)
5681
// FIXME: inefficient
5782
ssize_t rd = read(fd, &ch, 1);
5883
if (rd == -1) {
59-
if (errno != EINTR)
84+
switch (errno) {
85+
case EINTR: continue;
86+
case EAGAIN: {
87+
pollFD(fd, POLLIN);
88+
continue;
89+
}
90+
default:
6091
throw SysError("reading a line");
92+
}
6193
} else if (rd == 0) {
6294
if (eofOk)
6395
return s;

0 commit comments

Comments
 (0)