diff options
Diffstat (limited to 'tests/t_socket.c')
| -rw-r--r-- | tests/t_socket.c | 87 |
1 files changed, 52 insertions, 35 deletions
diff --git a/tests/t_socket.c b/tests/t_socket.c index ede8ae0..ab4b785 100644 --- a/tests/t_socket.c +++ b/tests/t_socket.c | |||
| @@ -8,14 +8,18 @@ | |||
| 8 | #include <fcntl.h> | 8 | #include <fcntl.h> |
| 9 | #include <netinet/in.h> | 9 | #include <netinet/in.h> |
| 10 | #include <poll.h> | 10 | #include <poll.h> |
| 11 | #include <pthread.h> | ||
| 12 | #include <signal.h> | ||
| 13 | #include <stdatomic.h> | ||
| 11 | #include <string.h> | 14 | #include <string.h> |
| 12 | #include <sys/socket.h> | 15 | #include <sys/socket.h> |
| 13 | #include <time.h> | 16 | #include <sys/time.h> |
| 14 | #include <unistd.h> | 17 | #include <unistd.h> |
| 15 | 18 | ||
| 16 | /* Opentracker */ | 19 | /* Opentracker */ |
| 17 | #include "ot_ip.h" | 20 | #include "ot_ip.h" |
| 18 | #include "ot_socket.h" | 21 | #include "ot_socket.h" |
| 22 | #include "trackerlogic.h" | ||
| 19 | #include "test.h" | 23 | #include "test.h" |
| 20 | 24 | ||
| 21 | /* These cases talk to the loopback interface rather than to a mock, because | 25 | /* These cases talk to the loopback interface rather than to a mock, because |
| @@ -145,42 +149,55 @@ static void case_udp_roundtrip(void) { | |||
| 145 | close(s); | 149 | close(s); |
| 146 | } | 150 | } |
| 147 | 151 | ||
| 148 | /* udp_workers_deinit relies on a worker parked in a read coming back. The | 152 | /* udp_workers_deinit and livesync_deinit end a read another thread is parked |
| 149 | receive timeout is what guarantees that; shutting the socket down is what | 153 | in by signalling that thread. Which mechanisms wake a blocked reader |
| 150 | makes it prompt where the platform allows it. Neither is uniform across the | 154 | differs between kernels -- shutting the socket down does so on Linux and |
| 151 | systems opentracker runs on, so both are checked rather than assumed. */ | 155 | FreeBSD but not on Darwin, closing it does so on Darwin but not on Linux -- |
| 152 | static void case_blocking_read_comes_back(void) { | 156 | so the one the tracker relies on is checked rather than assumed. The |
| 153 | char buf[32], ip[16], own_ip[16]; | 157 | watchdog is the test's own: where a reader is not woken this fails with a |
| 154 | uint16_t port = 0, own_port = 0; | 158 | figure to look at instead of hanging the suite. */ |
| 155 | time_t started; | 159 | static _Atomic int reader_returned; |
| 156 | int s; | 160 | static int reader_socket; |
| 157 | 161 | ||
| 158 | test_case("a receive timeout does not delay a datagram that is already there"); | 162 | static void reader_wake(int s) { (void)s; } |
| 159 | s = socket_udp_bind(ip_loopback, 0, SOCKET_EXCLUSIVE); | ||
| 160 | CHECK(s != -1); | ||
| 161 | CHECK_INT(socket_set_blocking(s, 1), 0); | ||
| 162 | CHECK_INT(socket_set_timeout(s, 1), 0); | ||
| 163 | bound_address(s, own_ip, &own_port); | ||
| 164 | CHECK_INT(socket_send(s, "opentracker", 11, own_ip, own_port), 11); | ||
| 165 | CHECK_INT(socket_recv(s, buf, sizeof(buf), ip, &port), 11); | ||
| 166 | 163 | ||
| 167 | test_case("and ends a read that would otherwise never return"); | 164 | static void *blocked_reader(void *args) { |
| 168 | started = time(NULL); | 165 | char buf[32], ip[16]; |
| 169 | CHECK_INT(socket_recv(s, buf, sizeof(buf), ip, &port), -1); | 166 | uint16_t port = 0; |
| 170 | CHECK(errno == EAGAIN || errno == EWOULDBLOCK); | ||
| 171 | CHECK(time(NULL) - started <= 3); | ||
| 172 | close(s); | ||
| 173 | 167 | ||
| 174 | test_case("shutting the socket down ends one too"); | 168 | (void)args; |
| 175 | s = socket_udp_bind(ip_loopback, 0, SOCKET_EXCLUSIVE); | 169 | socket_recv(reader_socket, buf, sizeof(buf), ip, &port); |
| 176 | CHECK(s != -1); | 170 | atomic_store(&reader_returned, 1); |
| 177 | CHECK_INT(socket_set_blocking(s, 1), 0); | 171 | return NULL; |
| 178 | CHECK_INT(socket_set_timeout(s, 1), 0); | 172 | } |
| 179 | shutdown(s, SHUT_RD); | 173 | |
| 180 | started = time(NULL); | 174 | static void case_a_signal_ends_a_blocking_read(void) { |
| 181 | CHECK(socket_recv(s, buf, sizeof(buf), ip, &port) <= 0); | 175 | struct sigaction sa; |
| 182 | CHECK(time(NULL) - started <= 3); | 176 | struct timeval watchdog = {2, 0}; |
| 183 | close(s); | 177 | pthread_t thread; |
| 178 | int waited; | ||
| 179 | |||
| 180 | test_case("a signal ends a read another thread is parked in"); | ||
| 181 | memset(&sa, 0, sizeof(sa)); | ||
| 182 | sa.sa_handler = reader_wake; | ||
| 183 | sa.sa_flags = 0; /* SA_RESTART would resume the read instead of ending it */ | ||
| 184 | CHECK_INT(sigaction(OT_SIGNAL_WAKE, &sa, NULL), 0); | ||
| 185 | |||
| 186 | atomic_store(&reader_returned, 0); | ||
| 187 | reader_socket = socket_udp_bind(ip_loopback, 0, SOCKET_EXCLUSIVE); | ||
| 188 | CHECK(reader_socket != -1); | ||
| 189 | CHECK_INT(socket_set_blocking(reader_socket, 1), 0); | ||
| 190 | CHECK_INT(setsockopt(reader_socket, SOL_SOCKET, SO_RCVTIMEO, &watchdog, sizeof(watchdog)), 0); | ||
| 191 | |||
| 192 | CHECK_INT(pthread_create(&thread, NULL, blocked_reader, NULL), 0); | ||
| 193 | usleep(100000); /* long enough to be sure it reached the read */ | ||
| 194 | CHECK_INT(pthread_kill(thread, OT_SIGNAL_WAKE), 0); | ||
| 195 | |||
| 196 | for (waited = 0; waited < 1000 && !atomic_load(&reader_returned); ++waited) | ||
| 197 | usleep(1000); | ||
| 198 | CHECK(waited < 1000); | ||
| 199 | pthread_join(thread, NULL); | ||
| 200 | close(reader_socket); | ||
| 184 | } | 201 | } |
| 185 | 202 | ||
| 186 | /* An IPv4 peer must arrive in the v4 mapped form opentracker stores, however | 203 | /* An IPv4 peer must arrive in the v4 mapped form opentracker stores, however |
| @@ -332,7 +349,7 @@ void test_socket(void) { | |||
| 332 | case_sharing_is_opt_in(); | 349 | case_sharing_is_opt_in(); |
| 333 | #endif | 350 | #endif |
| 334 | case_udp_roundtrip(); | 351 | case_udp_roundtrip(); |
| 335 | case_blocking_read_comes_back(); | 352 | case_a_signal_ends_a_blocking_read(); |
| 336 | case_udp_v4mapped(); | 353 | case_udp_v4mapped(); |
| 337 | case_tcp_roundtrip(); | 354 | case_tcp_roundtrip(); |
| 338 | case_connect_refused(); | 355 | case_connect_refused(); |
