summaryrefslogtreecommitdiff
path: root/ot_fdstate.c
diff options
context:
space:
mode:
authorDirk Engling <erdgeist@erdgeist.org>2026-10-31 15:59:41 +0100
committerDirk Engling <erdgeist@erdgeist.org>2026-10-31 15:59:41 +0100
commit4d030871c004c917458b5e1c296edcfc689a2e66 (patch)
treec6c057e47a17d8b03ea28a611ff029d950acfa8c /ot_fdstate.c
parent440cfeee71829308fa1cc9b5aef63beb7ab756dc (diff)
Make tcp worker multithreaded, too
This shamelessly steals the model established in the UDP workers, i.e. if the kernel is able to bind multiple listening sockets to the same endpoint, we will share them across threads. If it can't, all threads will accept on the same socket. The fast path handles most of the connections, only the rare case of no data yet on the socket is offloaded to the slow path (which used to be the mainloop) for completion. This scales nicely, as the higher the load is, the higher chances are that an accept happens only after the network roundtrip to the peer completes, leading to more connections that already have data waiting for us. We also consolidate the workstruct init and deinit code, preparation for later unifying all thread state structs for all protocols (mca, udc and tcp). The config file gains a per tcp connection worker count and some docs explaining what this knob buys and costs, nudging the user to either keep it 0 or dial it up to at least 2.
Diffstat (limited to 'ot_fdstate.c')
-rw-r--r--ot_fdstate.c9
1 files changed, 6 insertions, 3 deletions
diff --git a/ot_fdstate.c b/ot_fdstate.c
index 7d742da..09daf50 100644
--- a/ot_fdstate.c
+++ b/ot_fdstate.c
@@ -91,7 +91,7 @@ uint32_t fdstate_open(int fd, void *cookie, ot_time expiry) {
91 } 91 }
92 92
93 word = atomic_load_explicit(&slot->state, memory_order_relaxed); 93 word = atomic_load_explicit(&slot->state, memory_order_relaxed);
94 atomic_store_explicit(&slot->cookie, cookie, memory_order_relaxed); 94 atomic_store_explicit(&slot->cookie, cookie, memory_order_release);
95 atomic_store_explicit(&slot->expiry, expiry, memory_order_relaxed); 95 atomic_store_explicit(&slot->expiry, expiry, memory_order_relaxed);
96 /* Publishes the two above to whoever claims the slot next */ 96 /* Publishes the two above to whoever claims the slot next */
97 atomic_store_explicit(&slot->state, FD_WORD(FD_GEN(word), FD_IDLE), memory_order_release); 97 atomic_store_explicit(&slot->state, FD_WORD(FD_GEN(word), FD_IDLE), memory_order_release);
@@ -183,14 +183,17 @@ int fdstate_next_expired(ot_time now, int *cursor) {
183void *fdstate_cookie(int fd) { 183void *fdstate_cookie(int fd) {
184 ot_fdslot *slot = fdstate_slot(fd); 184 ot_fdslot *slot = fdstate_slot(fd);
185 185
186 return slot ? atomic_load_explicit(&slot->cookie, memory_order_relaxed) : NULL; 186 /* Pairs with the release in fdstate_set_cookie and fdstate_open */
187 return slot ? atomic_load_explicit(&slot->cookie, memory_order_acquire) : NULL;
187} 188}
188 189
189void fdstate_set_cookie(int fd, void *cookie) { 190void fdstate_set_cookie(int fd, void *cookie) {
190 ot_fdslot *slot = fdstate_slot(fd); 191 ot_fdslot *slot = fdstate_slot(fd);
191 192
193 /* Publishes what the cookie points at to whoever reads it next, which may
194 be another thread entirely */
192 if (slot) 195 if (slot)
193 atomic_store_explicit(&slot->cookie, cookie, memory_order_relaxed); 196 atomic_store_explicit(&slot->cookie, cookie, memory_order_release);
194} 197}
195 198
196void fdstate_set_expiry(int fd, ot_time expiry) { 199void fdstate_set_expiry(int fd, ot_time expiry) {