diff options
| author | Dirk Engling <erdgeist@erdgeist.org> | 2026-10-31 00:30:09 +0100 |
|---|---|---|
| committer | Dirk Engling <erdgeist@erdgeist.org> | 2026-10-31 00:30:09 +0100 |
| commit | e9445a41e5cbb4f1a1091302efdf3e80d0d70c7f (patch) | |
| tree | f2229d101a618f5d6747aa339f0d32bdc0191479 | |
| parent | 15f9f81b5a8398fefca023e90d8d1c168ecef620 (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.c | 26 | ||||
| -rw-r--r-- | ot_event.c | 7 | ||||
| -rw-r--r-- | ot_event.h | 5 | ||||
| -rw-r--r-- | tests/t_event.c | 9 |
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; | |||
| 32 | static ot_listen *g_listen; | 33 | static ot_listen *g_listen; |
| 33 | static size_t g_listen_count; | 34 | static size_t g_listen_count; |
| 34 | static int g_listen_shared; | 35 | static int g_listen_shared; |
| 35 | static int g_listen_workers = 1; | 36 | static int g_listen_workers_udp = 1; |
| 37 | static int g_listen_workers_tcp = 0; | ||
| 36 | 38 | ||
| 37 | static void panic(const char *routine) __attribute__((noreturn)); | 39 | static void panic(const char *routine) __attribute__((noreturn)); |
| 38 | static void panic(const char *routine) { | 40 | static 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 | ||
| @@ -40,6 +40,9 @@ static struct epoll_event *g_ready; | |||
| 40 | static int g_ready_space; | 40 | static int g_ready_space; |
| 41 | 41 | ||
| 42 | const char *event_mechanism(void) { return "epoll"; } | 42 | const 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 */ | ||
| 45 | int event_can_share(void) { return 1; } | ||
| 43 | 46 | ||
| 44 | int event_init(void) { | 47 | int event_init(void) { |
| 45 | g_ready = NULL; | 48 | g_ready = NULL; |
| @@ -118,6 +121,8 @@ static struct kevent *g_ready; | |||
| 118 | static int g_ready_space; | 121 | static int g_ready_space; |
| 119 | 122 | ||
| 120 | const char *event_mechanism(void) { return "kqueue"; } | 123 | const char *event_mechanism(void) { return "kqueue"; } |
| 124 | /* The set lives in the kernel, and kevent may be called from any thread */ | ||
| 125 | int event_can_share(void) { return 1; } | ||
| 121 | 126 | ||
| 122 | int event_init(void) { | 127 | int event_init(void) { |
| 123 | g_ready = NULL; | 128 | g_ready = NULL; |
| @@ -229,6 +234,8 @@ static int *g_where; | |||
| 229 | static int g_count, g_space, g_where_space; | 234 | static int g_count, g_space, g_where_space; |
| 230 | 235 | ||
| 231 | const char *event_mechanism(void) { return "poll"; } | 236 | const char *event_mechanism(void) { return "poll"; } |
| 237 | /* The arrays below are ours and nothing guards them */ | ||
| 238 | int event_can_share(void) { return 0; } | ||
| 232 | 239 | ||
| 233 | int event_init(void) { | 240 | int event_init(void) { |
| 234 | g_pfds = NULL, g_generation = NULL, g_where = NULL; | 241 | g_pfds = NULL, g_generation = NULL, g_where = NULL; |
| @@ -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. */ |
| 39 | int event_wait(ot_event *out, int max, int timeout_ms); | 39 | int 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. */ | ||
| 44 | int 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 */ |
| 42 | const char *event_mechanism(void); | 47 | const 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. */ | ||
| 269 | static 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 | |||
| 266 | void test_event(void) { | 274 | void 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(); |
