summaryrefslogtreecommitdiff
path: root/ot_http.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_http.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_http.c')
-rw-r--r--ot_http.c38
1 files changed, 28 insertions, 10 deletions
diff --git a/ot_http.c b/ot_http.c
index bb6ff24..602ff94 100644
--- a/ot_http.c
+++ b/ot_http.c
@@ -39,22 +39,30 @@ ssize_t g_stats_path_len;
39 39
40enum { SUCCESS_HTTP_HEADER_LENGTH = 80, SUCCESS_HTTP_SIZE_OFF = 17 }; 40enum { SUCCESS_HTTP_HEADER_LENGTH = 80, SUCCESS_HTTP_SIZE_OFF = 17 };
41 41
42int http_adopt(const int64_t sock, struct http_data *cookie) { 42struct http_data *http_adopt(const int64_t sock) {
43 struct http_data *heap = malloc(sizeof(*heap)); 43 struct http_data *cookie = fdstate_cookie(sock), *heap;
44 int interest;
44 45
45 if (!heap) 46 if (!cookie || !(cookie->flag & STRUCT_HTTP_FLAG_ONSTACK))
46 return -1; 47 return cookie;
48
49 if (!(heap = malloc(sizeof(*heap))))
50 return NULL;
47 51
48 *heap = *cookie; 52 *heap = *cookie;
49 heap->flag &= ~STRUCT_HTTP_FLAG_ONSTACK; 53 heap->flag &= ~STRUCT_HTTP_FLAG_ONSTACK;
54
55 /* Publishing the cookie is what lets another thread reach this connection,
56 so nothing may touch it afterwards, not even to read what it asked for */
57 interest = heap->interest;
50 fdstate_set_cookie((int)sock, heap); 58 fdstate_set_cookie((int)sock, heap);
51 59
52 if (event_add((int)sock, heap->interest, fdstate_generation((int)sock)) == -1) { 60 if (event_add((int)sock, interest, fdstate_generation((int)sock)) == -1) {
53 fdstate_set_cookie((int)sock, cookie); 61 fdstate_set_cookie((int)sock, cookie);
54 free(heap); 62 free(heap);
55 return -1; 63 return NULL;
56 } 64 }
57 return 0; 65 return heap;
58} 66}
59 67
60/* Give up a connection: the one place that knows everything hanging off it. */ 68/* Give up a connection: the one place that knows everything hanging off it. */
@@ -348,13 +356,17 @@ static ssize_t http_handle_stats(const int64_t sock, struct ot_workstruct *ws, c
348 } 356 }
349#endif 357#endif
350#endif 358#endif
351 /* Pass this task to the worker thread */
352 cookie->flag |= STRUCT_HTTP_FLAG_WAITINGFORTASK | STRUCT_HTTP_FLAG_CHUNKED; 359 cookie->flag |= STRUCT_HTTP_FLAG_WAITINGFORTASK | STRUCT_HTTP_FLAG_CHUNKED;
353 360
354 /* Clients waiting for us should not easily timeout */ 361 /* Clients waiting for us should not easily timeout */
355 fdstate_set_expiry(sock, 0); 362 fdstate_set_expiry(sock, 0);
356 fullscrape_deliver(sock, format);
357 http_interest_set(sock, cookie, 0); 363 http_interest_set(sock, cookie, 0);
364
365 /* Everything this connection needs has been written, so it can be given
366 a home of its own and only then be made reachable by a worker thread */
367 if (!http_adopt(sock))
368 HTTPERROR_500;
369 fullscrape_deliver(sock, format);
358 return ws->reply_size = -2; 370 return ws->reply_size = -2;
359 } 371 }
360#endif 372#endif
@@ -366,6 +378,9 @@ static ssize_t http_handle_stats(const int64_t sock, struct ot_workstruct *ws, c
366 378
367 /* Complex stats also include expensive memory debugging tools */ 379 /* Complex stats also include expensive memory debugging tools */
368 fdstate_set_expiry(sock, 0); 380 fdstate_set_expiry(sock, 0);
381
382 if (!http_adopt(sock))
383 HTTPERROR_500;
369 stats_deliver(sock, mode); 384 stats_deliver(sock, mode);
370 return ws->reply_size = -2; 385 return ws->reply_size = -2;
371 } 386 }
@@ -448,8 +463,11 @@ static ssize_t http_handle_fullscrape(const int64_t sock, struct ot_workstruct *
448 cookie->flag |= STRUCT_HTTP_FLAG_WAITINGFORTASK | STRUCT_HTTP_FLAG_CHUNKED; 463 cookie->flag |= STRUCT_HTTP_FLAG_WAITINGFORTASK | STRUCT_HTTP_FLAG_CHUNKED;
449 /* Clients waiting for us should not easily timeout */ 464 /* Clients waiting for us should not easily timeout */
450 fdstate_set_expiry(sock, 0); 465 fdstate_set_expiry(sock, 0);
451 fullscrape_deliver(sock, format);
452 http_interest_set(sock, cookie, 0); 466 http_interest_set(sock, cookie, 0);
467
468 if (!http_adopt(sock))
469 HTTPERROR_500;
470 fullscrape_deliver(sock, format);
453 return ws->reply_size = -2; 471 return ws->reply_size = -2;
454} 472}
455#endif 473#endif