diff options
| -rw-r--r-- | opentracker.c | 7 | ||||
| -rw-r--r-- | ot_livesync.c | 12 | ||||
| -rw-r--r-- | ot_socket.c | 7 | ||||
| -rw-r--r-- | ot_socket.h | 4 | ||||
| -rw-r--r-- | ot_udp.c | 58 | ||||
| -rw-r--r-- | tests/t_socket.c | 87 | ||||
| -rw-r--r-- | trackerlogic.h | 4 |
7 files changed, 103 insertions, 76 deletions
diff --git a/opentracker.c b/opentracker.c index ab4a934..e0f16d7 100644 --- a/opentracker.c +++ b/opentracker.c | |||
| @@ -49,7 +49,9 @@ static void panic(const char *routine) { | |||
| 49 | exit(111); | 49 | exit(111); |
| 50 | } | 50 | } |
| 51 | 51 | ||
| 52 | /* Say we are stopping, wake the loop, and let main do the work on its own stack. */ | 52 | /* Say we are stopping, wake the loop, and let main do the work on its own |
| 53 | stack. Everything other than SIGINT falls through and does nothing, which | ||
| 54 | is what OT_SIGNAL_WAKE wants: returning from the read is all it is for. */ | ||
| 53 | static void signal_handler(int s) { | 55 | static void signal_handler(int s) { |
| 54 | if (s == SIGINT) { | 56 | if (s == SIGINT) { |
| 55 | const char byte = 0; | 57 | const char byte = 0; |
| @@ -85,7 +87,8 @@ static void install_signal_handlers(void) { | |||
| 85 | sa.sa_handler = signal_handler; | 87 | sa.sa_handler = signal_handler; |
| 86 | sigemptyset(&sa.sa_mask); | 88 | sigemptyset(&sa.sa_mask); |
| 87 | sa.sa_flags = 0; | 89 | sa.sa_flags = 0; |
| 88 | if ((sigaction(SIGINT, &sa, NULL) == -1) || (sigaction(SIGALRM, &sa, NULL) == -1)) | 90 | if ((sigaction(SIGINT, &sa, NULL) == -1) || (sigaction(SIGALRM, &sa, NULL) == -1) || |
| 91 | (sigaction(OT_SIGNAL_WAKE, &sa, NULL) == -1)) | ||
| 89 | panic("install_signal_handlers"); | 92 | panic("install_signal_handlers"); |
| 90 | 93 | ||
| 91 | sigaddset(&signal_mask, SIGINT); | 94 | sigaddset(&signal_mask, SIGINT); |
diff --git a/ot_livesync.c b/ot_livesync.c index e6db720..4a0ede1 100644 --- a/ot_livesync.c +++ b/ot_livesync.c | |||
| @@ -32,9 +32,6 @@ ot_ip6 groupip_1 = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 224, 0, 23, 5}; | |||
| 32 | 32 | ||
| 33 | #define LIVESYNC_MAXDELAY 15 /* seconds */ | 33 | #define LIVESYNC_MAXDELAY 15 /* seconds */ |
| 34 | 34 | ||
| 35 | /* How long the worker may sit in a read before it looks at the running flag */ | ||
| 36 | #define LIVESYNC_WORKER_PATIENCE 1 /* seconds */ | ||
| 37 | |||
| 38 | enum { OT_SYNC_PEER4, OT_SYNC_PEER6 }; | 35 | enum { OT_SYNC_PEER4, OT_SYNC_PEER6 }; |
| 39 | 36 | ||
| 40 | /* Forward declaration */ | 37 | /* Forward declaration */ |
| @@ -58,6 +55,7 @@ static sync_buffer g_v4_buf; | |||
| 58 | 55 | ||
| 59 | static pthread_t thread_id; | 56 | static pthread_t thread_id; |
| 60 | static int thread_started; | 57 | static int thread_started; |
| 58 | static _Atomic int thread_left; | ||
| 61 | void livesync_init(void) { | 59 | void livesync_init(void) { |
| 62 | uint32_t packet_type6 = htonl(OT_SYNC_PEER6); | 60 | uint32_t packet_type6 = htonl(OT_SYNC_PEER6); |
| 63 | uint32_t packet_type4 = htonl(OT_SYNC_PEER4); | 61 | uint32_t packet_type4 = htonl(OT_SYNC_PEER4); |
| @@ -78,13 +76,16 @@ void livesync_init(void) { | |||
| 78 | g_v6_buf.next_packet_time = time_future_seconds(LIVESYNC_MAXDELAY); | 76 | g_v6_buf.next_packet_time = time_future_seconds(LIVESYNC_MAXDELAY); |
| 79 | g_v4_buf.next_packet_time = time_future_seconds(LIVESYNC_MAXDELAY); | 77 | g_v4_buf.next_packet_time = time_future_seconds(LIVESYNC_MAXDELAY); |
| 80 | 78 | ||
| 81 | socket_set_timeout(g_socket_in, LIVESYNC_WORKER_PATIENCE); | ||
| 82 | thread_started = !pthread_create(&thread_id, NULL, livesync_worker, NULL); | 79 | thread_started = !pthread_create(&thread_id, NULL, livesync_worker, NULL); |
| 83 | } | 80 | } |
| 84 | 81 | ||
| 85 | void livesync_deinit(void) { | 82 | void livesync_deinit(void) { |
| 86 | if (thread_started) { | 83 | if (thread_started) { |
| 87 | shutdown(g_socket_in, SHUT_RD); | 84 | /* Repeats so it interrupts a read already under way */ |
| 85 | while (!atomic_load(&thread_left)) { | ||
| 86 | pthread_kill(thread_id, OT_SIGNAL_WAKE); | ||
| 87 | usleep(1000); | ||
| 88 | } | ||
| 88 | pthread_join(thread_id, NULL); | 89 | pthread_join(thread_id, NULL); |
| 89 | thread_started = 0; | 90 | thread_started = 0; |
| 90 | } | 91 | } |
| @@ -235,6 +236,7 @@ static void *livesync_worker(void *args) { | |||
| 235 | } | 236 | } |
| 236 | 237 | ||
| 237 | free(ws.inbuf); | 238 | free(ws.inbuf); |
| 239 | atomic_store(&thread_left, 1); | ||
| 238 | return NULL; | 240 | return NULL; |
| 239 | } | 241 | } |
| 240 | 242 | ||
diff --git a/ot_socket.c b/ot_socket.c index c73738c..158459f 100644 --- a/ot_socket.c +++ b/ot_socket.c | |||
| @@ -9,7 +9,6 @@ | |||
| 9 | #include <netinet/in.h> | 9 | #include <netinet/in.h> |
| 10 | #include <string.h> | 10 | #include <string.h> |
| 11 | #include <sys/socket.h> | 11 | #include <sys/socket.h> |
| 12 | #include <sys/time.h> | ||
| 13 | #include <unistd.h> | 12 | #include <unistd.h> |
| 14 | 13 | ||
| 15 | /* Opentracker */ | 14 | /* Opentracker */ |
| @@ -49,12 +48,6 @@ int socket_set_blocking(int s, int blocking) { | |||
| 49 | return fcntl(s, F_SETFL, blocking ? (flags & ~O_NONBLOCK) : (flags | O_NONBLOCK)); | 48 | return fcntl(s, F_SETFL, blocking ? (flags & ~O_NONBLOCK) : (flags | O_NONBLOCK)); |
| 50 | } | 49 | } |
| 51 | 50 | ||
| 52 | int socket_set_timeout(int s, int seconds) { | ||
| 53 | struct timeval patience = {seconds, 0}; | ||
| 54 | |||
| 55 | return setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &patience, sizeof(patience)); | ||
| 56 | } | ||
| 57 | |||
| 58 | static int set_nonblocking(int s) { return socket_set_blocking(s, 0); } | 51 | static int set_nonblocking(int s) { return socket_set_blocking(s, 0); } |
| 59 | 52 | ||
| 60 | /* Close without letting the failure we are about to report be overwritten */ | 53 | /* Close without letting the failure we are about to report be overwritten */ |
diff --git a/ot_socket.h b/ot_socket.h index 9340d0d..f73a5c9 100644 --- a/ot_socket.h +++ b/ot_socket.h | |||
| @@ -39,10 +39,6 @@ int socket_is_connected(int s); | |||
| 39 | int socket_accept(int s, ot_ip6 ip, uint16_t *port); | 39 | int socket_accept(int s, ot_ip6 ip, uint16_t *port); |
| 40 | int socket_set_blocking(int s, int blocking); | 40 | int socket_set_blocking(int s, int blocking); |
| 41 | 41 | ||
| 42 | /* Bound how long a blocking read waits, so the thread sitting in one gets to | ||
| 43 | look at the running flag now and then */ | ||
| 44 | int socket_set_timeout(int s, int seconds); | ||
| 45 | |||
| 46 | ssize_t socket_recv(int s, void *buf, size_t len, ot_ip6 ip, uint16_t *port); | 42 | ssize_t socket_recv(int s, void *buf, size_t len, ot_ip6 ip, uint16_t *port); |
| 47 | ssize_t socket_send(int s, const void *buf, size_t len, const ot_ip6 ip, uint16_t port); | 43 | ssize_t socket_send(int s, const void *buf, size_t len, const ot_ip6 ip, uint16_t port); |
| 48 | 44 | ||
| @@ -9,7 +9,7 @@ | |||
| 9 | #include <stdio.h> | 9 | #include <stdio.h> |
| 10 | #include <stdlib.h> | 10 | #include <stdlib.h> |
| 11 | #include <string.h> | 11 | #include <string.h> |
| 12 | #include <sys/socket.h> | 12 | #include <unistd.h> |
| 13 | 13 | ||
| 14 | /* Opentracker */ | 14 | /* Opentracker */ |
| 15 | #include "ot_ip.h" | 15 | #include "ot_ip.h" |
| @@ -20,16 +20,16 @@ | |||
| 20 | #include "ot_udp.h" | 20 | #include "ot_udp.h" |
| 21 | #include "trackerlogic.h" | 21 | #include "trackerlogic.h" |
| 22 | 22 | ||
| 23 | /* How long a worker may sit in a read before it looks at the running flag */ | 23 | /* One of these per worker, each allocated on its own so that growing the |
| 24 | #define OT_UDP_WORKER_PATIENCE 1 | 24 | array below never moves the one a running worker holds. */ |
| 25 | |||
| 26 | struct udp_worker { | 25 | struct udp_worker { |
| 27 | pthread_t thread; | 26 | pthread_t thread; |
| 28 | int64_t sock; | 27 | int64_t sock; |
| 28 | _Atomic int left; | ||
| 29 | }; | 29 | }; |
| 30 | 30 | ||
| 31 | static struct udp_worker *g_workers; | 31 | static struct udp_worker **g_workers; |
| 32 | static size_t g_worker_count; | 32 | static size_t g_worker_count; |
| 33 | 33 | ||
| 34 | static uint32_t g_rijndael_round_key[44] = {0}; | 34 | static uint32_t g_rijndael_round_key[44] = {0}; |
| 35 | static volatile uint32_t g_key_of_the_hour[2] = {0}; | 35 | static volatile uint32_t g_key_of_the_hour[2] = {0}; |
| @@ -218,7 +218,7 @@ int handle_udp6(int64_t serversocket, struct ot_workstruct *ws) { | |||
| 218 | } | 218 | } |
| 219 | 219 | ||
| 220 | static void *udp_worker(void *args) { | 220 | static void *udp_worker(void *args) { |
| 221 | int64_t sock = (int64_t)args; | 221 | struct udp_worker *me = args; |
| 222 | struct ot_workstruct ws; | 222 | struct ot_workstruct ws; |
| 223 | memset(&ws, 0, sizeof(ws)); | 223 | memset(&ws, 0, sizeof(ws)); |
| 224 | 224 | ||
| @@ -230,7 +230,7 @@ static void *udp_worker(void *args) { | |||
| 230 | #endif | 230 | #endif |
| 231 | 231 | ||
| 232 | while (g_opentracker_running) | 232 | while (g_opentracker_running) |
| 233 | handle_udp6(sock, &ws); | 233 | handle_udp6(me->sock, &ws); |
| 234 | 234 | ||
| 235 | stats_counters_retire(&ws); | 235 | stats_counters_retire(&ws); |
| 236 | 236 | ||
| @@ -239,6 +239,8 @@ static void *udp_worker(void *args) { | |||
| 239 | #ifdef _DEBUG_HTTPERROR | 239 | #ifdef _DEBUG_HTTPERROR |
| 240 | free(ws.debugbuf); | 240 | free(ws.debugbuf); |
| 241 | #endif | 241 | #endif |
| 242 | |||
| 243 | atomic_store(&me->left, 1); | ||
| 242 | return NULL; | 244 | return NULL; |
| 243 | } | 245 | } |
| 244 | 246 | ||
| @@ -248,34 +250,44 @@ void udp_init(void) { | |||
| 248 | } | 250 | } |
| 249 | 251 | ||
| 250 | void udp_worker_install(int64_t sock) { | 252 | void udp_worker_install(int64_t sock) { |
| 251 | struct udp_worker *grown = realloc(g_workers, (g_worker_count + 1) * sizeof(*g_workers)); | 253 | struct udp_worker **grown = realloc(g_workers, (g_worker_count + 1) * sizeof(*g_workers)); |
| 254 | struct udp_worker *worker; | ||
| 252 | 255 | ||
| 253 | if (!grown) { | 256 | if (!grown || !(worker = calloc(1, sizeof(*worker)))) { |
| 254 | fprintf(stderr, "Warning: out of memory installing a udp worker, this socket stays with the workers already running.\n"); | 257 | fprintf(stderr, "Warning: out of memory installing a udp worker, this socket stays with the workers already running.\n"); |
| 258 | g_workers = grown ? grown : g_workers; | ||
| 255 | return; | 259 | return; |
| 256 | } | 260 | } |
| 257 | g_workers = grown; | 261 | g_workers = grown; |
| 262 | worker->sock = sock; | ||
| 258 | 263 | ||
| 259 | #ifdef _DEBUG | 264 | #ifdef _DEBUG |
| 260 | fprintf(stderr, "Installing a worker on udp socket %ld\n", (unsigned long)sock); | 265 | fprintf(stderr, "Installing a worker on udp socket %ld\n", (unsigned long)sock); |
| 261 | #endif | 266 | #endif |
| 262 | socket_set_blocking((int)sock, 1); | 267 | socket_set_blocking((int)sock, 1); |
| 263 | socket_set_timeout((int)sock, OT_UDP_WORKER_PATIENCE); | ||
| 264 | 268 | ||
| 265 | g_workers[g_worker_count].sock = sock; | 269 | if (pthread_create(&worker->thread, NULL, udp_worker, worker)) |
| 266 | if (!pthread_create(&g_workers[g_worker_count].thread, NULL, udp_worker, (void *)sock)) | 270 | free(worker); |
| 267 | ++g_worker_count; | 271 | else |
| 272 | g_workers[g_worker_count++] = worker; | ||
| 268 | } | 273 | } |
| 269 | 274 | ||
| 270 | void udp_workers_deinit(void) { | 275 | void udp_workers_deinit(void) { |
| 271 | size_t i; | 276 | size_t i; |
| 272 | 277 | ||
| 273 | /* The receive timeout bounds this on its own, shutting the socket down | 278 | for (i = 0; i < g_worker_count; ++i) { |
| 274 | cuts it short wherever the platform wakes a blocked reader */ | 279 | struct udp_worker *worker = g_workers[i]; |
| 275 | for (i = 0; i < g_worker_count; ++i) | 280 | |
| 276 | shutdown((int)g_workers[i].sock, SHUT_RD); | 281 | /* Repeated because a signal only interrupts a read already under way: a |
| 277 | for (i = 0; i < g_worker_count; ++i) | 282 | worker that was between two reads when the first one arrived is inside |
| 278 | pthread_join(g_workers[i].thread, NULL); | 283 | the next one by now. */ |
| 284 | while (!atomic_load(&worker->left)) { | ||
| 285 | pthread_kill(worker->thread, OT_SIGNAL_WAKE); | ||
| 286 | usleep(1000); | ||
| 287 | } | ||
| 288 | pthread_join(worker->thread, NULL); | ||
| 289 | free(worker); | ||
| 290 | } | ||
| 279 | 291 | ||
| 280 | free(g_workers); | 292 | free(g_workers); |
| 281 | g_workers = NULL; | 293 | g_workers = NULL; |
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(); |
diff --git a/trackerlogic.h b/trackerlogic.h index ad6c911..7d0693a 100644 --- a/trackerlogic.h +++ b/trackerlogic.h | |||
| @@ -6,6 +6,7 @@ | |||
| 6 | #ifndef OT_TRACKERLOGIC_H__ | 6 | #ifndef OT_TRACKERLOGIC_H__ |
| 7 | #define OT_TRACKERLOGIC_H__ | 7 | #define OT_TRACKERLOGIC_H__ |
| 8 | 8 | ||
| 9 | #include <signal.h> | ||
| 9 | #include <stdatomic.h> | 10 | #include <stdatomic.h> |
| 10 | #include <stdint.h> | 11 | #include <stdint.h> |
| 11 | #include <stdlib.h> | 12 | #include <stdlib.h> |
| @@ -90,6 +91,9 @@ static inline ot_time time_future_seconds(int span) { return time_now_seconds() | |||
| 90 | /* Cleared from the interrupt handler and read by every worker */ | 91 | /* Cleared from the interrupt handler and read by every worker */ |
| 91 | extern _Atomic int g_opentracker_running; | 92 | extern _Atomic int g_opentracker_running; |
| 92 | 93 | ||
| 94 | /* Sent to a thread parked in a blocking read */ | ||
| 95 | #define OT_SIGNAL_WAKE SIGUSR1 | ||
| 96 | |||
| 93 | 97 | ||
| 94 | extern uint32_t g_tracker_id; | 98 | extern uint32_t g_tracker_id; |
| 95 | typedef enum { FLAG_TCP, FLAG_UDP, FLAG_MCA, FLAG_SELFPIPE } PROTO_FLAG; | 99 | typedef enum { FLAG_TCP, FLAG_UDP, FLAG_MCA, FLAG_SELFPIPE } PROTO_FLAG; |
