summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDirk Engling <erdgeist@erdgeist.org>2026-10-31 00:30:09 +0100
committerDirk Engling <erdgeist@erdgeist.org>2026-10-31 00:30:09 +0100
commite9445a41e5cbb4f1a1091302efdf3e80d0d70c7f (patch)
treef2229d101a618f5d6747aa339f0d32bdc0191479
parent15f9f81b5a8398fefca023e90d8d1c168ecef620 (diff)
Add config parsers for multiple tcp socket readers and a helper to see if we can offer it on the platform
-rw-r--r--ot_config.c26
-rw-r--r--ot_event.c7
-rw-r--r--ot_event.h5
-rw-r--r--tests/t_event.c9
4 files changed, 43 insertions, 4 deletions
diff --git a/ot_config.c b/ot_config.c
index a0dbef1..272acf1 100644
--- a/ot_config.c
+++ b/ot_config.c
@@ -14,6 +14,7 @@
14 14
15/* Opentracker */ 15/* Opentracker */
16#include "ot_accesslist.h" 16#include "ot_accesslist.h"
17#include "ot_event.h"
17#include "ot_config.h" 18#include "ot_config.h"
18#include "ot_http.h" 19#include "ot_http.h"
19#include "ot_ip.h" 20#include "ot_ip.h"
@@ -32,7 +33,8 @@ const char *g_statefile;
32static ot_listen *g_listen; 33static ot_listen *g_listen;
33static size_t g_listen_count; 34static size_t g_listen_count;
34static int g_listen_shared; 35static int g_listen_shared;
35static int g_listen_workers = 1; 36static int g_listen_workers_udp = 1;
37static int g_listen_workers_tcp = 0;
36 38
37static void panic(const char *routine) __attribute__((noreturn)); 39static void panic(const char *routine) __attribute__((noreturn));
38static void panic(const char *routine) { 40static void panic(const char *routine) {
@@ -196,6 +198,7 @@ typedef enum {
196 CONFIG_LISTEN_TCP, 198 CONFIG_LISTEN_TCP,
197 CONFIG_LISTEN_UDP, 199 CONFIG_LISTEN_UDP,
198 CONFIG_LISTEN_UDP_WORKERS, 200 CONFIG_LISTEN_UDP_WORKERS,
201 CONFIG_LISTEN_TCP_WORKERS,
199 CONFIG_LISTEN_SHARED, 202 CONFIG_LISTEN_SHARED,
200 CONFIG_ACCESS_WHITELIST, 203 CONFIG_ACCESS_WHITELIST,
201 CONFIG_ACCESS_BLACKLIST, 204 CONFIG_ACCESS_BLACKLIST,
@@ -224,6 +227,7 @@ static const config_key config_keys[] = {
224 CONFIG_KEY("listen.tcp", CONFIG_LISTEN_TCP), 227 CONFIG_KEY("listen.tcp", CONFIG_LISTEN_TCP),
225 CONFIG_KEY("listen.udp", CONFIG_LISTEN_UDP), 228 CONFIG_KEY("listen.udp", CONFIG_LISTEN_UDP),
226 CONFIG_KEY("listen.udp.workers", CONFIG_LISTEN_UDP_WORKERS), 229 CONFIG_KEY("listen.udp.workers", CONFIG_LISTEN_UDP_WORKERS),
230 CONFIG_KEY("listen.tcp.workers", CONFIG_LISTEN_TCP_WORKERS),
227 CONFIG_KEY("listen.shared", CONFIG_LISTEN_SHARED), 231 CONFIG_KEY("listen.shared", CONFIG_LISTEN_SHARED),
228#ifdef WANT_ACCESSLIST_WHITE 232#ifdef WANT_ACCESSLIST_WHITE
229 CONFIG_KEY("access.whitelist", CONFIG_ACCESS_WHITELIST), 233 CONFIG_KEY("access.whitelist", CONFIG_ACCESS_WHITELIST),
@@ -318,7 +322,7 @@ static int parse_configfile(char *config_filename) {
318 if (!off || !listen_scan_options(p + off, &shared, &workers)) 322 if (!off || !listen_scan_options(p + off, &shared, &workers))
319 goto parse_error; 323 goto parse_error;
320 if (kind != CONFIG_LISTEN_UDP) 324 if (kind != CONFIG_LISTEN_UDP)
321 listen_append(tmpip, tmpport, FLAG_TCP, shared, OT_LISTEN_INHERIT), ++bound; 325 listen_append(tmpip, tmpport, FLAG_TCP, shared, workers), ++bound;
322 if (kind != CONFIG_LISTEN_TCP) 326 if (kind != CONFIG_LISTEN_TCP)
323 listen_append(tmpip, tmpport, FLAG_UDP, shared, workers), ++bound; 327 listen_append(tmpip, tmpport, FLAG_UDP, shared, workers), ++bound;
324 break; 328 break;
@@ -326,7 +330,12 @@ static int parse_configfile(char *config_filename) {
326 case CONFIG_LISTEN_UDP_WORKERS: 330 case CONFIG_LISTEN_UDP_WORKERS:
327 if (!scan_uint(p, &value, UINT_MAX)) 331 if (!scan_uint(p, &value, UINT_MAX))
328 goto parse_error; 332 goto parse_error;
329 g_listen_workers = (int)value; 333 g_listen_workers_udp = (int)value;
334 break;
335 case CONFIG_LISTEN_TCP_WORKERS:
336 if (!scan_uint(p, &value, UINT_MAX))
337 goto parse_error;
338 g_listen_workers_tcp = (int)value;
330 break; 339 break;
331 case CONFIG_LISTEN_SHARED: 340 case CONFIG_LISTEN_SHARED:
332 if (!scan_uint(p, &value, 1)) 341 if (!scan_uint(p, &value, 1))
@@ -446,7 +455,16 @@ static void listen_resolve_defaults(void) {
446 if (entry->shared == OT_LISTEN_INHERIT) 455 if (entry->shared == OT_LISTEN_INHERIT)
447 entry->shared = g_listen_shared; 456 entry->shared = g_listen_shared;
448 if (entry->workers == OT_LISTEN_INHERIT) 457 if (entry->workers == OT_LISTEN_INHERIT)
449 entry->workers = g_listen_workers; 458 entry->workers = entry->proto == FLAG_TCP ? g_listen_workers_tcp : g_listen_workers_udp;
459
460 /* A tcp worker hands a connection it cannot finish to the event loop,
461 which only one thread may touch where the set is ours rather than the
462 kernel's */
463 if (entry->proto == FLAG_TCP && entry->workers > 1 && !event_can_share()) {
464 fprintf(stderr, "Warning: Multiple workers impossible on this platform with event mechanism %s chosen.\n",
465 event_mechanism());
466 entry->workers = 1;
467 }
450 } 468 }
451} 469}
452 470
diff --git a/ot_event.c b/ot_event.c
index b4c4a55..61cc07b 100644
--- a/ot_event.c
+++ b/ot_event.c
@@ -40,6 +40,9 @@ static struct epoll_event *g_ready;
40static int g_ready_space; 40static int g_ready_space;
41 41
42const char *event_mechanism(void) { return "epoll"; } 42const char *event_mechanism(void) { return "epoll"; }
43/* The set lives in the kernel, and epoll_ctl during another thread's
44 epoll_wait is what it is for */
45int event_can_share(void) { return 1; }
43 46
44int event_init(void) { 47int event_init(void) {
45 g_ready = NULL; 48 g_ready = NULL;
@@ -118,6 +121,8 @@ static struct kevent *g_ready;
118static int g_ready_space; 121static int g_ready_space;
119 122
120const char *event_mechanism(void) { return "kqueue"; } 123const char *event_mechanism(void) { return "kqueue"; }
124/* The set lives in the kernel, and kevent may be called from any thread */
125int event_can_share(void) { return 1; }
121 126
122int event_init(void) { 127int event_init(void) {
123 g_ready = NULL; 128 g_ready = NULL;
@@ -229,6 +234,8 @@ static int *g_where;
229static int g_count, g_space, g_where_space; 234static int g_count, g_space, g_where_space;
230 235
231const char *event_mechanism(void) { return "poll"; } 236const char *event_mechanism(void) { return "poll"; }
237/* The arrays below are ours and nothing guards them */
238int event_can_share(void) { return 0; }
232 239
233int event_init(void) { 240int event_init(void) {
234 g_pfds = NULL, g_generation = NULL, g_where = NULL; 241 g_pfds = NULL, g_generation = NULL, g_where = NULL;
diff --git a/ot_event.h b/ot_event.h
index aa007d0..efdefff 100644
--- a/ot_event.h
+++ b/ot_event.h
@@ -38,6 +38,11 @@ int event_close(int fd);
38 out with up to max ready descriptors. Returns how many, or -1. */ 38 out with up to max ready descriptors. Returns how many, or -1. */
39int event_wait(ot_event *out, int max, int timeout_ms); 39int event_wait(ot_event *out, int max, int timeout_ms);
40 40
41/* Whether the registered set may be changed from another thread while this
42 one is waiting on it. True where the kernel keeps the set, false where
43 this module keeps it in memory of its own. */
44int event_can_share(void);
45
41/* Which mechanism was compiled in, for the version string */ 46/* Which mechanism was compiled in, for the version string */
42const char *event_mechanism(void); 47const char *event_mechanism(void);
43 48
diff --git a/tests/t_event.c b/tests/t_event.c
index 706a285..1908dc9 100644
--- a/tests/t_event.c
+++ b/tests/t_event.c
@@ -263,9 +263,18 @@ static void case_add_nothing_then_ask(void) {
263 pair_close(); 263 pair_close();
264} 264}
265 265
266/* Whether a worker thread may register a descriptor while this one waits
267 depends on who keeps the set. Tying the answer to the mechanism's name
268 here means a backend added later cannot quietly forget to say. */
269static void case_sharing_matches_the_mechanism(void) {
270 test_case("only a set the kernel keeps may be changed from another thread");
271 CHECK_INT(event_can_share(), strcmp(event_mechanism(), "poll") ? 1 : 0);
272}
273
266void test_event(void) { 274void test_event(void) {
267 CHECK_INT(event_init(), 0); 275 CHECK_INT(event_init(), 0);
268 CHECK(event_mechanism() != NULL); 276 CHECK(event_mechanism() != NULL);
277 case_sharing_matches_the_mechanism();
269 278
270 case_readable(); 279 case_readable();
271 case_writable(); 280 case_writable();