diff options
| author | Dirk Engling <erdgeist@erdgeist.org> | 2026-11-01 00:53:53 +0100 |
|---|---|---|
| committer | Dirk Engling <erdgeist@erdgeist.org> | 2026-11-01 00:53:53 +0100 |
| commit | af0f2cb1be5aa10bb6a4bdd9f7ef4aa3950ecb31 (patch) | |
| tree | bd366224016709d65bbb0b8fbc3148c29719e8c4 | |
| parent | b7d9dc14eb560b1d19f506610535815f29085579 (diff) | |
Generalize the worker setup/runloop/teardown logic by adding a smol ot_worker helper
| -rw-r--r-- | Makefile | 4 | ||||
| -rw-r--r-- | opentracker.c | 4 | ||||
| -rw-r--r-- | ot_tcp.c | 66 | ||||
| -rw-r--r-- | ot_tcp.h | 2 | ||||
| -rw-r--r-- | ot_udp.c | 69 | ||||
| -rw-r--r-- | ot_udp.h | 2 | ||||
| -rw-r--r-- | ot_worker.c | 75 | ||||
| -rw-r--r-- | ot_worker.h | 39 |
8 files changed, 128 insertions, 133 deletions
| @@ -69,8 +69,8 @@ CFLAGS_libowfat=-I$(LIBOWFAT_HEADERS) | |||
| 69 | LDFLAGS_libowfat=-L$(LIBOWFAT_LIBRARY) -lowfat | 69 | LDFLAGS_libowfat=-L$(LIBOWFAT_LIBRARY) -lowfat |
| 70 | 70 | ||
| 71 | BINARY =opentracker | 71 | BINARY =opentracker |
| 72 | HEADERS=trackerlogic.h ot_urlencoded.h ot_batch.h ot_buf.h ot_event.h ot_fdstate.h ot_fmt.h ot_hex.h ot_ip.h ot_config.h ot_scan.h ot_socket.h ot_tcp.h ot_workqueue.h ot_mutex.h ot_random.h ot_stats.h ot_vector.h ot_clean.h ot_udp.h ot_iovec.h ot_fullscrape.h ot_accesslist.h ot_http.h ot_livesync.h ot_rijndael.h | 72 | HEADERS=trackerlogic.h ot_urlencoded.h ot_batch.h ot_buf.h ot_event.h ot_fdstate.h ot_fmt.h ot_hex.h ot_ip.h ot_config.h ot_scan.h ot_socket.h ot_tcp.h ot_worker.h ot_workqueue.h ot_mutex.h ot_random.h ot_stats.h ot_vector.h ot_clean.h ot_udp.h ot_iovec.h ot_fullscrape.h ot_accesslist.h ot_http.h ot_livesync.h ot_rijndael.h |
| 73 | SOURCES=opentracker.c trackerlogic.c ot_urlencoded.c ot_batch.c ot_buf.c ot_event.c ot_fdstate.c ot_fmt.c ot_hex.c ot_ip.c ot_config.c ot_scan.c ot_socket.c ot_tcp.c ot_workqueue.c ot_mutex.c ot_random.c ot_stats.c ot_vector.c ot_clean.c ot_udp.c ot_iovec.c ot_fullscrape.c ot_accesslist.c ot_http.c ot_livesync.c ot_rijndael.c | 73 | SOURCES=opentracker.c trackerlogic.c ot_urlencoded.c ot_batch.c ot_buf.c ot_event.c ot_fdstate.c ot_fmt.c ot_hex.c ot_ip.c ot_config.c ot_scan.c ot_socket.c ot_tcp.c ot_worker.c ot_workqueue.c ot_mutex.c ot_random.c ot_stats.c ot_vector.c ot_clean.c ot_udp.c ot_iovec.c ot_fullscrape.c ot_accesslist.c ot_http.c ot_livesync.c ot_rijndael.c |
| 74 | SOURCES_proxy=proxy.c ot_batch.c ot_fdstate.c ot_vector.c ot_mutex.c ot_random.c ot_scan.c ot_ip.c ot_socket.c | 74 | SOURCES_proxy=proxy.c ot_batch.c ot_fdstate.c ot_vector.c ot_mutex.c ot_random.c ot_scan.c ot_ip.c ot_socket.c |
| 75 | 75 | ||
| 76 | all: $(BINARY) $(BINARY).debug | 76 | all: $(BINARY) $(BINARY).debug |
diff --git a/opentracker.c b/opentracker.c index 2f2746d..644e99c 100644 --- a/opentracker.c +++ b/opentracker.c | |||
| @@ -38,6 +38,7 @@ | |||
| 38 | #include "ot_stats.h" | 38 | #include "ot_stats.h" |
| 39 | #include "ot_tcp.h" | 39 | #include "ot_tcp.h" |
| 40 | #include "ot_udp.h" | 40 | #include "ot_udp.h" |
| 41 | #include "ot_worker.h" | ||
| 41 | #include "trackerlogic.h" | 42 | #include "trackerlogic.h" |
| 42 | 43 | ||
| 43 | /* Globals */ | 44 | /* Globals */ |
| @@ -429,8 +430,7 @@ int main(int argc, char **argv) { | |||
| 429 | listen_start_udp_workers(); | 430 | listen_start_udp_workers(); |
| 430 | 431 | ||
| 431 | server_mainloop(0); | 432 | server_mainloop(0); |
| 432 | tcp_workers_deinit(); | 433 | worker_stop_all(); |
| 433 | udp_workers_deinit(); | ||
| 434 | 434 | ||
| 435 | /* No thread is serving any more, so the teardown can run where a debugger | 435 | /* No thread is serving any more, so the teardown can run where a debugger |
| 436 | and the sanitizers can follow it */ | 436 | and the sanitizers can follow it */ |
| @@ -20,6 +20,7 @@ | |||
| 20 | #include "ot_http.h" | 20 | #include "ot_http.h" |
| 21 | #include "ot_mutex.h" | 21 | #include "ot_mutex.h" |
| 22 | #include "ot_socket.h" | 22 | #include "ot_socket.h" |
| 23 | #include "ot_worker.h" | ||
| 23 | #include "ot_stats.h" | 24 | #include "ot_stats.h" |
| 24 | #include "ot_tcp.h" | 25 | #include "ot_tcp.h" |
| 25 | #include "trackerlogic.h" | 26 | #include "trackerlogic.h" |
| @@ -164,75 +165,18 @@ void tcp_accept(const int64_t serversocket, struct ot_workstruct *ws) { | |||
| 164 | } | 165 | } |
| 165 | } | 166 | } |
| 166 | 167 | ||
| 167 | /* One of these per worker, each allocated on its own so that growing the | ||
| 168 | array below never moves the one a running worker holds. */ | ||
| 169 | struct tcp_worker { | ||
| 170 | pthread_t thread; | ||
| 171 | int64_t sock; | ||
| 172 | _Atomic int left; | ||
| 173 | }; | ||
| 174 | |||
| 175 | static struct tcp_worker **g_workers; | ||
| 176 | static size_t g_worker_count; | ||
| 177 | |||
| 178 | /* A worker blocks in accept and nowhere else: the connection it is handed is | 168 | /* A worker blocks in accept and nowhere else: the connection it is handed is |
| 179 | non blocking, so a client that stops talking is given to the event loop | 169 | non blocking, so a client that stops talking is given to the event loop |
| 180 | rather than keeping this thread. */ | 170 | rather than keeping this thread. */ |
| 181 | static void *tcp_worker(void *args) { | 171 | static void tcp_run(struct ot_worker *me) { |
| 182 | struct tcp_worker *me = args; | ||
| 183 | struct ot_workstruct ws; | ||
| 184 | |||
| 185 | if (workstruct_init(&ws) == -1) { | ||
| 186 | fprintf(stderr, "Warning: a tcp worker could not get its buffers and will not run.\n"); | ||
| 187 | atomic_store(&me->left, 1); | ||
| 188 | return NULL; | ||
| 189 | } | ||
| 190 | |||
| 191 | while (g_opentracker_running) | 172 | while (g_opentracker_running) |
| 192 | tcp_accept(me->sock, &ws); | 173 | tcp_accept(me->sock, &me->ws); |
| 193 | |||
| 194 | workstruct_deinit(&ws); | ||
| 195 | atomic_store(&me->left, 1); | ||
| 196 | return NULL; | ||
| 197 | } | 174 | } |
| 198 | 175 | ||
| 199 | void tcp_worker_install(int64_t sock) { | 176 | void tcp_worker_install(int64_t sock) { |
| 200 | struct tcp_worker **grown = realloc(g_workers, (g_worker_count + 1) * sizeof(*g_workers)); | ||
| 201 | struct tcp_worker *worker; | ||
| 202 | |||
| 203 | if (!grown || !(worker = calloc(1, sizeof(*worker)))) { | ||
| 204 | fprintf(stderr, "Warning: out of memory installing a tcp worker, this socket stays with the workers already running.\n"); | ||
| 205 | g_workers = grown ? grown : g_workers; | ||
| 206 | return; | ||
| 207 | } | ||
| 208 | g_workers = grown; | ||
| 209 | worker->sock = sock; | ||
| 210 | |||
| 211 | socket_set_blocking((int)sock, 1); | 177 | socket_set_blocking((int)sock, 1); |
| 212 | socket_defer_accept((int)sock); | 178 | socket_defer_accept((int)sock); |
| 213 | 179 | ||
| 214 | if (pthread_create(&worker->thread, NULL, tcp_worker, worker)) | 180 | if (worker_start(tcp_run, sock) == -1) |
| 215 | free(worker); | 181 | fprintf(stderr, "Warning: could not install a tcp worker, this socket stays with the workers already running.\n"); |
| 216 | else | ||
| 217 | g_workers[g_worker_count++] = worker; | ||
| 218 | } | ||
| 219 | |||
| 220 | void tcp_workers_deinit(void) { | ||
| 221 | size_t i; | ||
| 222 | |||
| 223 | for (i = 0; i < g_worker_count; ++i) { | ||
| 224 | struct tcp_worker *worker = g_workers[i]; | ||
| 225 | |||
| 226 | /* Repeated because a signal only interrupts a call already under way */ | ||
| 227 | while (!atomic_load(&worker->left)) { | ||
| 228 | pthread_kill(worker->thread, OT_SIGNAL_WAKE); | ||
| 229 | usleep(1000); | ||
| 230 | } | ||
| 231 | pthread_join(worker->thread, NULL); | ||
| 232 | free(worker); | ||
| 233 | } | ||
| 234 | |||
| 235 | free(g_workers); | ||
| 236 | g_workers = NULL; | ||
| 237 | g_worker_count = 0; | ||
| 238 | } | 182 | } |
| @@ -22,7 +22,5 @@ void tcp_write(const int64_t sock); | |||
| 22 | /* One thread accepting on one socket. Called once per worker. */ | 22 | /* One thread accepting on one socket. Called once per worker. */ |
| 23 | void tcp_worker_install(int64_t sock); | 23 | void tcp_worker_install(int64_t sock); |
| 24 | 24 | ||
| 25 | /* Stop every worker installed above and wait for it to be gone */ | ||
| 26 | void tcp_workers_deinit(void); | ||
| 27 | 25 | ||
| 28 | #endif | 26 | #endif |
| @@ -16,21 +16,11 @@ | |||
| 16 | #include "ot_random.h" | 16 | #include "ot_random.h" |
| 17 | #include "ot_rijndael.h" | 17 | #include "ot_rijndael.h" |
| 18 | #include "ot_socket.h" | 18 | #include "ot_socket.h" |
| 19 | #include "ot_worker.h" | ||
| 19 | #include "ot_stats.h" | 20 | #include "ot_stats.h" |
| 20 | #include "ot_udp.h" | 21 | #include "ot_udp.h" |
| 21 | #include "trackerlogic.h" | 22 | #include "trackerlogic.h" |
| 22 | 23 | ||
| 23 | /* One of these per worker, each allocated on its own so that growing the | ||
| 24 | array below never moves the one a running worker holds. */ | ||
| 25 | struct udp_worker { | ||
| 26 | pthread_t thread; | ||
| 27 | int64_t sock; | ||
| 28 | _Atomic int left; | ||
| 29 | }; | ||
| 30 | |||
| 31 | static struct udp_worker **g_workers; | ||
| 32 | static size_t g_worker_count; | ||
| 33 | |||
| 34 | static uint32_t g_rijndael_round_key[44] = {0}; | 24 | static uint32_t g_rijndael_round_key[44] = {0}; |
| 35 | static volatile uint32_t g_key_of_the_hour[2] = {0}; | 25 | static volatile uint32_t g_key_of_the_hour[2] = {0}; |
| 36 | static volatile ot_time g_hour_of_the_key; | 26 | static volatile ot_time g_hour_of_the_key; |
| @@ -217,22 +207,9 @@ int handle_udp6(int64_t serversocket, struct ot_workstruct *ws) { | |||
| 217 | return 1; | 207 | return 1; |
| 218 | } | 208 | } |
| 219 | 209 | ||
| 220 | static void *udp_worker(void *args) { | 210 | static void udp_run(struct ot_worker *me) { |
| 221 | struct udp_worker *me = args; | ||
| 222 | struct ot_workstruct ws; | ||
| 223 | |||
| 224 | if (workstruct_init(&ws) == -1) { | ||
| 225 | fprintf(stderr, "Warning: a udp worker could not get its buffers and will not run.\n"); | ||
| 226 | atomic_store(&me->left, 1); | ||
| 227 | return NULL; | ||
| 228 | } | ||
| 229 | |||
| 230 | while (g_opentracker_running) | 211 | while (g_opentracker_running) |
| 231 | handle_udp6(me->sock, &ws); | 212 | handle_udp6(me->sock, &me->ws); |
| 232 | |||
| 233 | workstruct_deinit(&ws); | ||
| 234 | atomic_store(&me->left, 1); | ||
| 235 | return NULL; | ||
| 236 | } | 213 | } |
| 237 | 214 | ||
| 238 | void udp_init(void) { | 215 | void udp_init(void) { |
| @@ -241,47 +218,11 @@ void udp_init(void) { | |||
| 241 | } | 218 | } |
| 242 | 219 | ||
| 243 | void udp_worker_install(int64_t sock) { | 220 | void udp_worker_install(int64_t sock) { |
| 244 | struct udp_worker **grown = realloc(g_workers, (g_worker_count + 1) * sizeof(*g_workers)); | ||
| 245 | struct udp_worker *worker; | ||
| 246 | |||
| 247 | if (!grown || !(worker = calloc(1, sizeof(*worker)))) { | ||
| 248 | fprintf(stderr, "Warning: out of memory installing a udp worker, this socket stays with the workers already running.\n"); | ||
| 249 | g_workers = grown ? grown : g_workers; | ||
| 250 | return; | ||
| 251 | } | ||
| 252 | g_workers = grown; | ||
| 253 | worker->sock = sock; | ||
| 254 | |||
| 255 | #ifdef _DEBUG | 221 | #ifdef _DEBUG |
| 256 | fprintf(stderr, "Installing a worker on udp socket %ld\n", (unsigned long)sock); | 222 | fprintf(stderr, "Installing a worker on udp socket %ld\n", (unsigned long)sock); |
| 257 | #endif | 223 | #endif |
| 258 | socket_set_blocking((int)sock, 1); | 224 | socket_set_blocking((int)sock, 1); |
| 259 | 225 | ||
| 260 | if (pthread_create(&worker->thread, NULL, udp_worker, worker)) | 226 | if (worker_start(udp_run, sock) == -1) |
| 261 | free(worker); | 227 | fprintf(stderr, "Warning: could not install a udp worker, this socket stays with the workers already running.\n"); |
| 262 | else | ||
| 263 | g_workers[g_worker_count++] = worker; | ||
| 264 | } | 228 | } |
| 265 | |||
| 266 | void udp_workers_deinit(void) { | ||
| 267 | size_t i; | ||
| 268 | |||
| 269 | for (i = 0; i < g_worker_count; ++i) { | ||
| 270 | struct udp_worker *worker = g_workers[i]; | ||
| 271 | |||
| 272 | /* Repeated because a signal only interrupts a read already under way: a | ||
| 273 | worker that was between two reads when the first one arrived is inside | ||
| 274 | the next one by now. */ | ||
| 275 | while (!atomic_load(&worker->left)) { | ||
| 276 | pthread_kill(worker->thread, OT_SIGNAL_WAKE); | ||
| 277 | usleep(1000); | ||
| 278 | } | ||
| 279 | pthread_join(worker->thread, NULL); | ||
| 280 | free(worker); | ||
| 281 | } | ||
| 282 | |||
| 283 | free(g_workers); | ||
| 284 | g_workers = NULL; | ||
| 285 | g_worker_count = 0; | ||
| 286 | } | ||
| 287 | |||
| @@ -14,8 +14,6 @@ void udp_init(void); | |||
| 14 | /* One thread reading one socket. Called once per worker. */ | 14 | /* One thread reading one socket. Called once per worker. */ |
| 15 | void udp_worker_install(int64_t sock); | 15 | void udp_worker_install(int64_t sock); |
| 16 | 16 | ||
| 17 | /* Stop every worker installed above and wait for it to be gone */ | ||
| 18 | void udp_workers_deinit(void); | ||
| 19 | int handle_udp6(int64_t serversocket, struct ot_workstruct *ws); | 17 | int handle_udp6(int64_t serversocket, struct ot_workstruct *ws); |
| 20 | 18 | ||
| 21 | #endif | 19 | #endif |
diff --git a/ot_worker.c b/ot_worker.c new file mode 100644 index 0000000..97eaeb2 --- /dev/null +++ b/ot_worker.c | |||
| @@ -0,0 +1,75 @@ | |||
| 1 | /* This software was written by Dirk Engling <erdgeist@erdgeist.org> | ||
| 2 | It is considered beerware. Prost. Skol. Cheers or whatever. | ||
| 3 | |||
| 4 | $id$ */ | ||
| 5 | |||
| 6 | /* System */ | ||
| 7 | #include <pthread.h> | ||
| 8 | #include <stdatomic.h> | ||
| 9 | #include <stdio.h> | ||
| 10 | #include <stdlib.h> | ||
| 11 | #include <unistd.h> | ||
| 12 | |||
| 13 | /* Opentracker */ | ||
| 14 | #include "ot_worker.h" | ||
| 15 | #include "trackerlogic.h" | ||
| 16 | |||
| 17 | static struct ot_worker **g_workers; | ||
| 18 | static size_t g_worker_count; | ||
| 19 | |||
| 20 | /* Everything a worker needs doing around its own loop, in one place, so that | ||
| 21 | a new kind of worker cannot miss a piece of it. */ | ||
| 22 | static void *worker_trampoline(void *args) { | ||
| 23 | struct ot_worker *me = args; | ||
| 24 | |||
| 25 | if (workstruct_init(&me->ws) == -1) | ||
| 26 | fprintf(stderr, "Warning: a worker could not get its buffers and will not run.\n"); | ||
| 27 | else { | ||
| 28 | me->run(me); | ||
| 29 | workstruct_deinit(&me->ws); | ||
| 30 | } | ||
| 31 | |||
| 32 | atomic_store(&me->left, 1); | ||
| 33 | return NULL; | ||
| 34 | } | ||
| 35 | |||
| 36 | int worker_start(ot_worker_run run, int64_t sock) { | ||
| 37 | struct ot_worker **grown = realloc(g_workers, (g_worker_count + 1) * sizeof(*g_workers)); | ||
| 38 | struct ot_worker *worker; | ||
| 39 | |||
| 40 | if (!grown) | ||
| 41 | return -1; | ||
| 42 | g_workers = grown; | ||
| 43 | |||
| 44 | if (!(worker = calloc(1, sizeof(*worker)))) | ||
| 45 | return -1; | ||
| 46 | worker->sock = sock; | ||
| 47 | worker->run = run; | ||
| 48 | |||
| 49 | if (pthread_create(&worker->thread, NULL, worker_trampoline, worker)) { | ||
| 50 | free(worker); | ||
| 51 | return -1; | ||
| 52 | } | ||
| 53 | g_workers[g_worker_count++] = worker; | ||
| 54 | return 0; | ||
| 55 | } | ||
| 56 | |||
| 57 | void worker_stop_all(void) { | ||
| 58 | size_t i; | ||
| 59 | |||
| 60 | for (i = 0; i < g_worker_count; ++i) { | ||
| 61 | struct ot_worker *worker = g_workers[i]; | ||
| 62 | |||
| 63 | /* Repeated because a signal only interrupts a call already under way */ | ||
| 64 | while (!atomic_load(&worker->left)) { | ||
| 65 | pthread_kill(worker->thread, OT_SIGNAL_WAKE); | ||
| 66 | usleep(1000); | ||
| 67 | } | ||
| 68 | pthread_join(worker->thread, NULL); | ||
| 69 | free(worker); | ||
| 70 | } | ||
| 71 | |||
| 72 | free(g_workers); | ||
| 73 | g_workers = NULL; | ||
| 74 | g_worker_count = 0; | ||
| 75 | } | ||
diff --git a/ot_worker.h b/ot_worker.h new file mode 100644 index 0000000..ea4f379 --- /dev/null +++ b/ot_worker.h | |||
| @@ -0,0 +1,39 @@ | |||
| 1 | /* This software was written by Dirk Engling <erdgeist@erdgeist.org> | ||
| 2 | It is considered beerware. Prost. Skol. Cheers or whatever. | ||
| 3 | |||
| 4 | $id$ */ | ||
| 5 | |||
| 6 | #ifndef OT_WORKER_H__ | ||
| 7 | #define OT_WORKER_H__ | ||
| 8 | |||
| 9 | #include <pthread.h> | ||
| 10 | #include <stdatomic.h> | ||
| 11 | #include <stdint.h> | ||
| 12 | |||
| 13 | #include "trackerlogic.h" | ||
| 14 | |||
| 15 | struct ot_worker; | ||
| 16 | |||
| 17 | /* What a worker does between being started and being told to stop. It reads | ||
| 18 | worker->sock and works out of worker->ws; everything around it, from the | ||
| 19 | buffers to the counters to saying goodbye, is handled here. */ | ||
| 20 | typedef void (*ot_worker_run)(struct ot_worker *worker); | ||
| 21 | |||
| 22 | /* A thread that serves requests on a socket of its own, with the buffers and | ||
| 23 | counters it needs. Each is allocated on its own, so taking on another one | ||
| 24 | never moves the record a running worker holds. */ | ||
| 25 | struct ot_worker { | ||
| 26 | pthread_t thread; | ||
| 27 | _Atomic int left; | ||
| 28 | int64_t sock; | ||
| 29 | ot_worker_run run; | ||
| 30 | struct ot_workstruct ws; | ||
| 31 | }; | ||
| 32 | |||
| 33 | /* Start one. Returns -1 if it could not be started. */ | ||
| 34 | int worker_start(ot_worker_run run, int64_t sock); | ||
| 35 | |||
| 36 | /* Wake every worker until it has left, then join it */ | ||
| 37 | void worker_stop_all(void); | ||
| 38 | |||
| 39 | #endif | ||
