Files
lix/lix/libutil/monitor-fd.hh
T
Jade Lovelace 9a8f111af2 darwin: fix repeated delivery of MonitorFdHup callbacks
My lix build failed today with this result:

```
lix> [----------] 3 tests from MonitorFdHup
lix> [ RUN      ] MonitorFdHup.works
lix> [       OK ] MonitorFdHup.works (0 ms)
lix> [ RUN      ] MonitorFdHup.works_with_pipes
lix> stderr:
lix> Using configuration: seed=6402097764877502971
lix> libc++abi: terminating due to uncaught exception of type std::__1::future_error: The state of the promise has already been set.
lix> 4/5 lix:check / libstore-unit-tests        OK              1.10s
lix> 5/5 lix:check / libexpr-unit-tests         OK              1.12s
lix> Summary of Failures:
lix> 3/5 lix:check / libutil-unit-tests  FAIL            0.93s   killed by signal 6 SIGABRT
lix> Ok:                4
lix> Fail:              1
lix> Full log written to /nix/var/nix/builds/nix-build-lix-2.94.0-dev-pre20250711-65ef28d.drv-0/source/build/meson-logs/testlog.txt
```

I had a response best described as "wtf". I think the cause of this
problem is that there's a race condition with the test in which the loop
gets gone around again a second time because it's triggered by the
terminate fd (and I guess the flags remained what they were before?
seems reasonable), and this is probably racing with the quit atomic
being first to break out of the loop.

I don't know how many hundreds of lixes I've compiled without my test
failing, but this is definitely a bug. I don't think this affects actual
usage as the only impact is repeat delivery of Ctrl-C which is harmless
and which users do regularly.

Change-Id: I60da81d4ac2e79052cd323b5171f9d8bd0aa6783
2025-07-11 19:41:18 -07:00

45 lines
912 B
C++

#pragma once
///@file Observes a file descriptor for hang-up events and notifies a
/// callback.
#include <thread>
#include <atomic>
#include <poll.h>
#include <sys/types.h>
#include <unistd.h>
#include "lix/libutil/error.hh"
#include "lix/libutil/file-descriptor.hh"
#include "lix/libutil/signals.hh"
#include "lix/libutil/thread-name.hh"
namespace nix {
/**
* Observes a file descriptor for hang-up events and notifies a callback if any show up.
*
* The callback will be called at most once.
*/
class MonitorFdHup
{
private:
std::thread thread;
/**
* Pipe used to interrupt the poll()ing in the monitoring thread.
*/
Pipe terminatePipe;
std::atomic_bool quit = false;
std::function<void()> callback;
void runThread(int watchFd, int terminateFd);
public:
MonitorFdHup(int fd, std::function<void()> callback = nix::triggerInterrupt);
~MonitorFdHup();
};
}