summaryrefslogtreecommitdiff
path: root/tests/udp_wake_probe.c
diff options
context:
space:
mode:
authorDirk Engling <erdgeist@erdgeist.org>2026-10-28 17:11:33 +0100
committerDirk Engling <erdgeist@erdgeist.org>2026-10-28 17:11:33 +0100
commit9ec918187ab65488ebcce427f5a14a6b38e87b78 (patch)
tree4110ae8b03a67628cd020675159b64c765fb4f07 /tests/udp_wake_probe.c
parentf3f640b6bcf10ba463fc81cec97cfa212c520cfe (diff)
Add load tests to sanitize check the trackerlogic, based on udp requests
Diffstat (limited to 'tests/udp_wake_probe.c')
-rw-r--r--tests/udp_wake_probe.c99
1 files changed, 99 insertions, 0 deletions
diff --git a/tests/udp_wake_probe.c b/tests/udp_wake_probe.c
new file mode 100644
index 0000000..8a0f7a8
--- /dev/null
+++ b/tests/udp_wake_probe.c
@@ -0,0 +1,99 @@
1/* Three ways to get a thread out of a blocking recvfrom, tested rather than
2 assumed: close the socket, shut it down, or give it a receive timeout.
3
4 This decides how udp workers can be stopped. They sit in recvfrom and are
5 never joined today, and the sockets of a SO_REUSEPORT group all share one
6 address, so a worker cannot be singled out by sending it a datagram.
7
8 cc -O2 -pthread -o /tmp/wake tests/udp_wake_probe.c && /tmp/wake
9
10 On Linux 6.1: close does not wake it, shutdown does, SO_RCVTIMEO does. */
11#include <errno.h>
12#include <netinet/in.h>
13#include <pthread.h>
14#include <stdatomic.h>
15#include <stdio.h>
16#include <string.h>
17#include <sys/socket.h>
18#include <sys/time.h>
19#include <unistd.h>
20
21static _Atomic int returned;
22static _Atomic int saved_errno;
23static int sock;
24
25static void *reader(void *args) {
26 char buffer[64];
27 (void)args;
28 if (recvfrom(sock, buffer, sizeof(buffer), 0, NULL, NULL) < 0)
29 atomic_store(&saved_errno, errno);
30 atomic_store(&returned, 1);
31 return NULL;
32}
33
34static int bind_udp(int timeout_ms) {
35 struct sockaddr_in address;
36 int s = socket(AF_INET, SOCK_DGRAM, 0);
37
38 memset(&address, 0, sizeof(address));
39 address.sin_family = AF_INET;
40 address.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
41 address.sin_port = 0;
42 bind(s, (struct sockaddr *)&address, sizeof(address));
43
44 if (timeout_ms) {
45 struct timeval how_long;
46 how_long.tv_sec = timeout_ms / 1000;
47 how_long.tv_usec = (timeout_ms % 1000) * 1000;
48 setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &how_long, sizeof(how_long));
49 }
50 return s;
51}
52
53static void try_it(const char *name, int timeout_ms, int do_close, int do_shutdown) {
54 pthread_t thread;
55 int waited;
56
57 atomic_store(&returned, 0);
58 atomic_store(&saved_errno, 0);
59 sock = bind_udp(timeout_ms);
60 pthread_create(&thread, NULL, reader, NULL);
61 usleep(300000); /* let it reach recvfrom */
62
63 if (do_shutdown)
64 shutdown(sock, SHUT_RD);
65 if (do_close)
66 close(sock);
67
68 for (waited = 0; waited < 2000 && !atomic_load(&returned); ++waited)
69 usleep(1000);
70
71 if (atomic_load(&returned)) {
72 printf(" %-24s returned after %4d ms, errno %s\n", name, waited,
73 atomic_load(&saved_errno) ? strerror(atomic_load(&saved_errno)) : "none");
74 pthread_join(thread, NULL);
75 } else {
76 printf(" %-24s STILL BLOCKED after 2000 ms\n", name);
77 /* unblock it so the probe can finish */
78 struct sockaddr_in to;
79 socklen_t length = sizeof(to);
80 int poke = socket(AF_INET, SOCK_DGRAM, 0);
81 if (!getsockname(sock, (struct sockaddr *)&to, &length))
82 sendto(poke, "x", 1, 0, (struct sockaddr *)&to, length);
83 close(poke);
84 usleep(200000);
85 if (atomic_load(&returned))
86 pthread_join(thread, NULL);
87 else
88 pthread_detach(thread);
89 }
90 if (!do_close)
91 close(sock);
92}
93
94int main(void) {
95 try_it("close() the socket", 0, 1, 0);
96 try_it("shutdown(SHUT_RD)", 0, 0, 1);
97 try_it("SO_RCVTIMEO 500 ms", 500, 0, 0);
98 return 0;
99}