summaryrefslogtreecommitdiff
path: root/tests/udp_wake_probe.c
diff options
context:
space:
mode:
Diffstat (limited to 'tests/udp_wake_probe.c')
-rw-r--r--tests/udp_wake_probe.c42
1 files changed, 32 insertions, 10 deletions
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 @@
1/* Three ways to get a thread out of a blocking recvfrom, tested rather than 1/* Four 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. 2 assumed: close the socket, shut it down, give it a receive timeout, or
3 send it a signal.
3 4
4 This decides how udp workers can be stopped. They sit in recvfrom and are 5 This decides how udp workers are stopped. The sockets of a SO_REUSEPORT
5 never joined today, and the sockets of a SO_REUSEPORT group all share one 6 group all share one address, so a worker cannot be singled out by sending
6 address, so a worker cannot be singled out by sending it a datagram. 7 it a datagram, and the two mechanisms differ in what they cost while the
8 tracker is running: a receive timeout wakes every worker on every tick
9 forever, a signal and a shutdown cost nothing until the one moment they
10 are used.
7 11
8 cc -O2 -pthread -o /tmp/wake tests/udp_wake_probe.c && /tmp/wake 12 cc -O2 -pthread -o /tmp/wake tests/udp_wake_probe.c && /tmp/wake
9 13
10 On Linux 6.1: close does not wake it, shutdown does, SO_RCVTIMEO does. */ 14 On Linux 6.1: close does not wake it, the other three do. */
11#include <errno.h> 15#include <errno.h>
12#include <netinet/in.h> 16#include <netinet/in.h>
13#include <pthread.h> 17#include <pthread.h>
@@ -15,9 +19,12 @@
15#include <stdio.h> 19#include <stdio.h>
16#include <string.h> 20#include <string.h>
17#include <sys/socket.h> 21#include <sys/socket.h>
22#include <signal.h>
18#include <sys/time.h> 23#include <sys/time.h>
19#include <unistd.h> 24#include <unistd.h>
20 25
26#define WAKE_SIGNAL SIGUSR1
27
21static _Atomic int returned; 28static _Atomic int returned;
22static _Atomic int saved_errno; 29static _Atomic int saved_errno;
23static int sock; 30static int sock;
@@ -31,6 +38,11 @@ static void *reader(void *args) {
31 return NULL; 38 return NULL;
32} 39}
33 40
41/* A handler that does nothing is all it takes: the point is the return from
42 recvfrom with EINTR, not anything the handler does. SA_RESTART must stay
43 off or the kernel resumes the call instead of reporting it. */
44static void noop(int s) { (void)s; }
45
34static int bind_udp(int timeout_ms) { 46static int bind_udp(int timeout_ms) {
35 struct sockaddr_in address; 47 struct sockaddr_in address;
36 int s = socket(AF_INET, SOCK_DGRAM, 0); 48 int s = socket(AF_INET, SOCK_DGRAM, 0);
@@ -50,7 +62,7 @@ static int bind_udp(int timeout_ms) {
50 return s; 62 return s;
51} 63}
52 64
53static void try_it(const char *name, int timeout_ms, int do_close, int do_shutdown) { 65static void try_it(const char *name, int timeout_ms, int do_close, int do_shutdown, int do_signal) {
54 pthread_t thread; 66 pthread_t thread;
55 int waited; 67 int waited;
56 68
@@ -62,6 +74,8 @@ static void try_it(const char *name, int timeout_ms, int do_close, int do_shutdo
62 74
63 if (do_shutdown) 75 if (do_shutdown)
64 shutdown(sock, SHUT_RD); 76 shutdown(sock, SHUT_RD);
77 if (do_signal)
78 pthread_kill(thread, WAKE_SIGNAL);
65 if (do_close) 79 if (do_close)
66 close(sock); 80 close(sock);
67 81
@@ -92,8 +106,16 @@ static void try_it(const char *name, int timeout_ms, int do_close, int do_shutdo
92} 106}
93 107
94int main(void) { 108int main(void) {
95 try_it("close() the socket", 0, 1, 0); 109 struct sigaction sa;
96 try_it("shutdown(SHUT_RD)", 0, 0, 1); 110
97 try_it("SO_RCVTIMEO 500 ms", 500, 0, 0); 111 memset(&sa, 0, sizeof(sa));
112 sa.sa_handler = noop;
113 sa.sa_flags = 0;
114 sigaction(WAKE_SIGNAL, &sa, NULL);
115
116 try_it("close() the socket", 0, 1, 0, 0);
117 try_it("shutdown(SHUT_RD)", 0, 0, 1, 0);
118 try_it("SO_RCVTIMEO 500 ms", 500, 0, 0, 0);
119 try_it("pthread_kill(SIGUSR1)", 0, 0, 0, 1);
98 return 0; 120 return 0;
99} 121}