From d3a628a8ec1d4b1a2ec080328a41c21e48f04d06 Mon Sep 17 00:00:00 2001 From: Dirk Engling Date: Thu, 29 Oct 2026 01:16:53 +0100 Subject: add wake-up tester that helped figuring out how to tickle hanging udp readers --- tests/udp_wake_probe.c | 42 ++++++++++++++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 10 deletions(-) (limited to 'tests/udp_wake_probe.c') diff --git a/tests/udp_wake_probe.c b/tests/udp_wake_probe.c index 8a0f7a8..e948c34 100644 --- a/tests/udp_wake_probe.c +++ b/tests/udp_wake_probe.c @@ -1,13 +1,17 @@ -/* Three ways to get a thread out of a blocking recvfrom, tested rather than - assumed: close the socket, shut it down, or give it a receive timeout. +/* Four ways to get a thread out of a blocking recvfrom, tested rather than + assumed: close the socket, shut it down, give it a receive timeout, or + send it a signal. - This decides how udp workers can be stopped. They sit in recvfrom and are - never joined today, and the sockets of a SO_REUSEPORT group all share one - address, so a worker cannot be singled out by sending it a datagram. + This decides how udp workers are stopped. The sockets of a SO_REUSEPORT + group all share one address, so a worker cannot be singled out by sending + it a datagram, and the two mechanisms differ in what they cost while the + tracker is running: a receive timeout wakes every worker on every tick + forever, a signal and a shutdown cost nothing until the one moment they + are used. cc -O2 -pthread -o /tmp/wake tests/udp_wake_probe.c && /tmp/wake - On Linux 6.1: close does not wake it, shutdown does, SO_RCVTIMEO does. */ + On Linux 6.1: close does not wake it, the other three do. */ #include #include #include @@ -15,9 +19,12 @@ #include #include #include +#include #include #include +#define WAKE_SIGNAL SIGUSR1 + static _Atomic int returned; static _Atomic int saved_errno; static int sock; @@ -31,6 +38,11 @@ static void *reader(void *args) { return NULL; } +/* A handler that does nothing is all it takes: the point is the return from + recvfrom with EINTR, not anything the handler does. SA_RESTART must stay + off or the kernel resumes the call instead of reporting it. */ +static void noop(int s) { (void)s; } + static int bind_udp(int timeout_ms) { struct sockaddr_in address; int s = socket(AF_INET, SOCK_DGRAM, 0); @@ -50,7 +62,7 @@ static int bind_udp(int timeout_ms) { return s; } -static void try_it(const char *name, int timeout_ms, int do_close, int do_shutdown) { +static void try_it(const char *name, int timeout_ms, int do_close, int do_shutdown, int do_signal) { pthread_t thread; int waited; @@ -62,6 +74,8 @@ static void try_it(const char *name, int timeout_ms, int do_close, int do_shutdo if (do_shutdown) shutdown(sock, SHUT_RD); + if (do_signal) + pthread_kill(thread, WAKE_SIGNAL); if (do_close) close(sock); @@ -92,8 +106,16 @@ static void try_it(const char *name, int timeout_ms, int do_close, int do_shutdo } int main(void) { - try_it("close() the socket", 0, 1, 0); - try_it("shutdown(SHUT_RD)", 0, 0, 1); - try_it("SO_RCVTIMEO 500 ms", 500, 0, 0); + struct sigaction sa; + + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = noop; + sa.sa_flags = 0; + sigaction(WAKE_SIGNAL, &sa, NULL); + + try_it("close() the socket", 0, 1, 0, 0); + try_it("shutdown(SHUT_RD)", 0, 0, 1, 0); + try_it("SO_RCVTIMEO 500 ms", 500, 0, 0, 0); + try_it("pthread_kill(SIGUSR1)", 0, 0, 0, 1); return 0; } -- cgit v1.3