feat: std.http pooled handler dispatch (PLAN-HTTPZ S7b)
thread_pool_count = 0 (default) keeps handlers inline on the loop thread — the measured fast path (BENCH-HTTPZ.md). N > 0 dispatches each parsed request to a std.thread Pool of N workers, completing the httpz two-pool shape: the connection freezes as CONN_HANDLING (no reads, growth, eviction, or recycling — the worker borrows views into its read buffer), the worker runs the handler under a per-job arena and serializes into job-owned bytes, the completion queues under the PoolState mutex, and the loop wakes through the new std.event wake channel (kqueue EVFILT_USER + EV_CLEAR; the epoll twin maps to eventfd), attaches the response, compacts the buffer, and resumes keep-alive/pipeline handling. A full backlog sheds with 503. Stale completions (generation mismatch after close) are dropped. Pool mode requires the server's constructing allocator to be thread-safe (GPA/malloc), documented on the knob. PoolState lives behind a heap pointer (it embeds a Mutex and is shared with workers; the Server struct itself is returned by value). serialize_response/run_handler_job share one serialize_bytes. examples/1633 gains the pooled section (GET, body echo, 404 across worker threads) plus the loop-wake path exercised end to end; AOT run five times. examples/1632 unchanged but the Event struct gains `user`.
This commit is contained in:
@@ -39,18 +39,22 @@ contains :: (hay: string, needle: string) -> bool {
|
||||
}
|
||||
|
||||
// Connect a nonblocking loopback client.
|
||||
dial :: () -> i32 {
|
||||
dial_port :: (port: i64) -> i32 {
|
||||
fd := socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0);
|
||||
if fd < 0 { return -1; }
|
||||
addr : socket.SockAddr = .{
|
||||
sin_len = 16, sin_family = xx socket.AF_INET,
|
||||
sin_port = socket.htons(PORT), sin_addr = 0x0100007F,
|
||||
sin_port = socket.htons(port), sin_addr = 0x0100007F,
|
||||
};
|
||||
if socket.connect(fd, @addr, 16) != 0 { socket.close(fd); return -1; }
|
||||
if !socket.set_nonblocking(fd) { socket.close(fd); return -1; }
|
||||
return fd;
|
||||
}
|
||||
|
||||
dial :: () -> i32 {
|
||||
return dial_port(PORT);
|
||||
}
|
||||
|
||||
// True when `buf[0..len]` holds a complete response (headers + body).
|
||||
resp_complete :: (buf: [*]u8, len: i64) -> bool {
|
||||
s := string.{ ptr = buf, len = xx len };
|
||||
@@ -210,6 +214,36 @@ main :: () -> i32 {
|
||||
print("slow client evicted, healthy client served\n");
|
||||
|
||||
srv.close();
|
||||
|
||||
// ── pooled dispatch (S7b): same contract through worker threads ──
|
||||
// thread_pool_count > 0 runs handlers on a pool; completions come
|
||||
// back through the loop's wake channel. Same assertions: routing,
|
||||
// body echo, keep-alive reuse — now crossing threads per request.
|
||||
pcfg : http.Config = .{
|
||||
port = PORT + 1,
|
||||
timeout_request_ms = 1000,
|
||||
timeout_keepalive_ms = 1000,
|
||||
request_count = 50,
|
||||
max_conn = 8,
|
||||
thread_pool_count = 2,
|
||||
thread_pool_backlog = 16,
|
||||
};
|
||||
psrv, pse := http.Server.init(pcfg, handler, 77);
|
||||
if pse { print("pooled server init failed\n"); return 1; }
|
||||
|
||||
c5 := dial_port(PORT + 1);
|
||||
if c5 < 0 { print("dial5 failed\n"); return 1; }
|
||||
r7 := roundtrip(@psrv, c5, "GET /hello HTTP/1.1\r\nHost: t\r\n\r\n", @buf[0]);
|
||||
if !contains(r7, "HTTP/1.1 200 OK") { print("pooled: bad status\n"); return 1; }
|
||||
if !contains(r7, "hello GET") { print("pooled: bad body\n"); return 1; }
|
||||
r8 := roundtrip(@psrv, c5, "POST /echo HTTP/1.1\r\nHost: t\r\nContent-Length: 9\r\n\r\nping-pong", @buf[0]);
|
||||
if !contains(r8, "ping-pong") { print("pooled: echo failed\n"); return 1; }
|
||||
r9 := roundtrip(@psrv, c5, "GET /missing HTTP/1.1\r\nHost: t\r\n\r\n", @buf[0]);
|
||||
if !contains(r9, "HTTP/1.1 404 Not Found") { print("pooled: expected 404\n"); return 1; }
|
||||
socket.close(c5);
|
||||
psrv.close();
|
||||
print("pooled dispatch: GET, echo, 404 across worker threads ok\n");
|
||||
|
||||
print("http server ok\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user