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
45 lines
912 B
C++
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();
|
|
};
|
|
|
|
|
|
}
|