diff --git a/tests/test_race_conditions.cxx b/tests/test_race_conditions.cxx index 7b06a30..1791152 100644 --- a/tests/test_race_conditions.cxx +++ b/tests/test_race_conditions.cxx @@ -3,6 +3,9 @@ // Author: Unai Blazquez #include +#include +#include +#include #include #include @@ -137,8 +140,28 @@ TEST(RaceConditionTest, ProducerSurvivesConsumerCrash) } ASSERT_GE(spy.count(), 2) << "Phase 1: producer should have delivered values"; - // "Crash" the consumer: stop + destroy. - consumer.stop(); + // Simulate a hard crash: force-close the consumer's server fd from + // outside its thread, causing accept() to fail with EBADF. This is + // what happens when the kernel reclaims fds on SIGKILL / abort(). + // + // We find the server fd by calling getsockname() on open fds and + // matching against our socket path. + for (int fd = 3; fd < 1024; ++fd) + { + struct sockaddr_un addr = {}; + socklen_t len = sizeof(addr); + if (getsockname(fd, reinterpret_cast(&addr), &len) == 0 && + addr.sun_family == AF_UNIX && + std::string(addr.sun_path) == sock) + { + ::close(fd); // Yank the fd — consumer thread crashes out of accept() + break; + } + } + + // Destructor calls stop(), which joins the (now-exited) thread and + // cleans up. In a real crash no cleanup runs, but we can't leak + // threads in a test process. } // Phase 2: producer is still running with no consumer (sends will fail).