skip to content
==> src/http.c <==
1/* http: an HTTP/1.0 GET server computed on the GPU. meshd only moves bytes: it
2 * writes the request into the request window and publishes OP_REQ; the warp
3 * parses, routes and renders the whole response (status line, headers, body).
4 * Pages are generic over the program table (`reads = ["*"]`):
5 *   /  /programs  /status          every loaded program, from the table
6 *   /start/<name>  /stop/<name>    the slot's GPU-owned P_RUN_REQ (meshd honours it)
7 *   /view/<name>                   the program's declared `fields`
8 *   /load/<name>                   the load request words (view http), meshd honours it
9 *   /doom  /train                  those programs' pages, found by name
10 *   /render                        pathtrace's progress (pathtrace, else pathtrace-live)
11 *   /render.bmp  /render.ppm       its current image (24-bit BMP; binary PPM),
12 *                                  copied from its tile-major view by the whole warp
13 * Errors: 400, 404, 405 (anything but GET), 409, 503. Layout: view http.
14 * The warp gathers the request (and, for /doom, the per-column wall spans)
15 * with coalesced loads + shuffles; lane 0 then does the serial string work in
16 * one pass: Content-Length is a fixed-width field, filled in after the body.
17 * v1's renderer, kept as written (`v != v` is the NaN test).
18 * Ported from alpha-rust-mesh programs/http/src/lib.rs. */
19#include "mesh/rt.h"
20#include "pathtrace/view.h"
21
22/* Bounded byte writer into the response window. */
23typedef struct {
24    uint8_t *p;
25    uint32_t n, cap;
26} http_w;
27
28static inline __attribute__((always_inline)) void http_b(http_w *w, uint8_t c) {
29    if (w->n < w->cap) { mesh_rt_wrb(w->p + w->n, c); w->n++; }
30}
31__attribute__((noinline)) static void http_s(http_w *w, const char *s) {
32    for (; *s; s++) http_b(w, (uint8_t)*s);
33}
34/* A NUL-padded name. */
35__attribute__((noinline)) static void http_name(http_w *w, const uint8_t n[MESH_NAME_BYTES]) {
36    for (uint32_t i = 0; i < MESH_NAME_BYTES && n[i]; i++) http_b(w, n[i]);
37}
38__attribute__((noinline)) static void http_u(http_w *w, uint64_t v) {
39    uint8_t d[20];
40    uint32_t k = 0;
41    for (;;) {
42        d[k] = (uint8_t)('0' + v % 10);
43        v /= 10;
44        k++;
45        if (v == 0 || k == 20) break;
46    }
47    while (k > 0) http_b(w, d[--k]);
48}
49static void http_i(http_w *w, int32_t v) {
50    if (v < 0) http_b(w, '-');
51    http_u(w, v < 0 ? (uint64_t)(-(int64_t)v) : (uint64_t)v);
52}
53static void http_fixed(http_w *w, double x, uint32_t dec) {
54    uint64_t scale = 1;
55    for (uint32_t i = 0; i < dec; i++) scale *= 10;
56    uint64_t ip = (uint64_t)x;
57    uint64_t fp = (uint64_t)((x - (double)ip) * (double)scale + 0.5);
58    if (fp >= scale) { ip += 1; fp -= scale; }
59    http_u(w, ip);
60    if (dec > 0) {
61        http_b(w, '.');
62        for (uint64_t div = scale / 10; div > 0; div /= 10) http_b(w, (uint8_t)('0' + fp / div % 10));
63    }
64}
65/* Fixed point with dec decimals for ordinary magnitudes, d.dddde[-]N
66 * otherwise; handles sign, NaN and infinities. */
67__attribute__((noinline)) static void http_f(http_w *w, float v, uint32_t dec) {
68    if (v != v) { http_s(w, "nan"); return; }
69    double x = (double)v;
70    if (x < 0.0) { http_b(w, '-'); x = -x; }
71    if (x == __builtin_inf()) { http_s(w, "inf"); return; }
72    if (x != 0.0 && (x < 1e-3 || x >= 1e9)) {
73        int32_t e = 0;
74        while (x >= 10.0) { x /= 10.0; e++; }
75        while (x < 1.0) { x *= 10.0; e--; }
76        http_fixed(w, x, 4);
77        http_b(w, 'e');
78        http_i(w, e);
79        return;
80    }
81    http_fixed(w, x, dec);
82}
83
84/* Route codes (lane 0 decides, the warp learns them by shuffle). */
85enum {
86    HTTP_RT_INDEX, HTTP_RT_PROGRAMS, HTTP_RT_STATUS, HTTP_RT_DOOM, HTTP_RT_TRAIN, HTTP_RT_VIEW, HTTP_RT_OK, HTTP_RT_LOAD,
87    HTTP_RT_BAD, HTTP_RT_NOT_FOUND, HTTP_RT_NO_METHOD, HTTP_RT_BUSY, HTTP_RT_CONFLICT, HTTP_RT_RENDER, HTTP_RT_PPM, HTTP_RT_BMP,
88};
89
90/* The program table, read through the mailbox (READS_ALL). */
91typedef struct {
92    uint8_t *mb;
93} http_tab;
94static uint32_t tab_ent(http_tab t, uint32_t s, size_t off) { return mesh_rt_ld_relaxed(t.mb + mesh_prog_entry(s) + off); }
95static uint32_t tab_ctl(http_tab t, uint32_t s, size_t off) { return mesh_rt_ld_relaxed(t.mb + mesh_prog_ctl(s) + off); }
96static uint32_t tab_hdr(http_tab t, size_t off) { return mesh_rt_ld_relaxed(t.mb + off); }
97/* A word of the load page (the host block's S_LOAD.. regions, mirrored by meshd). */
98static uint32_t tab_load(http_tab t, size_t s_off) { return mesh_rt_ld_relaxed(t.mb + mesh_load_page(s_off)); }
99static float tab_loadf(http_tab t, size_t s_off) { return mesh_f32_from_bits(tab_load(t, s_off)); }
100static uint8_t *tab_ext(http_tab t, uint32_t s, size_t off) { return t.mb + mesh_prog_ext(s) + off; }
101static bool tab_used(http_tab t, uint32_t s) { return tab_ent(t, s, MESH_PT_STATE) != MESH_ST_EMPTY; }
102static void http_bytes16(const uint8_t *p, uint8_t n[16]) {
103    for (int i = 0; i < 16; i++) n[i] = *(const volatile uint8_t *)(p + i);
104}
105static void tab_name(http_tab t, uint32_t s, uint8_t n[16]) { http_bytes16(t.mb + mesh_prog_entry(s) + MESH_PT_NAME, n); }
106
107static inline __attribute__((always_inline)) uint8_t http_byte(const uint32_t req[64], size_t i) {
108    return i >= MESH_HTTP_REQ_MAX ? 0 : (uint8_t)(req[i >> 2] >> (8 * (i & 3)));
109}
110/* req[at..end] == s */
111static bool http_eq(const uint32_t req[64], size_t at, size_t end, const char *s) {
112    size_t n = 0;
113    while (s[n]) n++;
114    if (end < at || end - at != n) return false;
115    for (size_t i = 0; i < n; i++)
116        if (http_byte(req, at + i) != (uint8_t)s[i]) return false;
117    return true;
118}
119/* req[at..] starts with s */
120static bool http_starts(const uint32_t req[64], size_t at, size_t end, const char *s) {
121    size_t n = 0;
122    while (s[n]) n++;
123    return end >= at + n && http_eq(req, at, at + n, s);
124}
125
126static void http_init(const mesh_ctx *ctx) {
127    if (mesh_ctx_leader(ctx)) {
128        /* A pending load request from before a reset is dropped: ack == seq. */
129        mesh_view v = mesh_ctx_view(ctx);
130        mesh_view_set_u32(v, MESH_HTTP_HV_LOAD_ACK, mesh_view_u32(v, MESH_HTTP_HV_LOAD_SEQ));
131    }
132}
133
134/* The load request words: refused (503) while meshd has not acked the last one. */
135static uint32_t http_load_request(const mesh_ctx *ctx, const uint8_t *name, size_t n, uint32_t *slot) {
136    mesh_view v = mesh_ctx_view(ctx), ack, sq;
137    *slot = 0;
138    uint32_t seq = mesh_view_u32(v, MESH_HTTP_HV_LOAD_SEQ);
139    if (!mesh_view_sub(v, MESH_HTTP_HV_LOAD_ACK, 4, &ack)) return HTTP_RT_BAD;
140    if (mesh_rt_ld_acquire(mesh_view_ptr(ack)) != seq) return HTTP_RT_BUSY;
141    for (uint32_t i = 0; i < MESH_NAME_BYTES; i++) mesh_view_set_u8(v, MESH_HTTP_HV_LOAD_NAME + i, i < n ? name[i] : 0);
142    mesh_rt_fence_acq_rel();
143    if (mesh_view_sub(v, MESH_HTTP_HV_LOAD_SEQ, 4, &sq)) mesh_rt_st_release(mesh_view_ptr(sq), seq + 1);
144    return HTTP_RT_LOAD;
145}
146
147/* Lane 0: the route and its slot, with /start, /stop and /load's side effects. */
148static uint32_t http_parse(const mesh_ctx *ctx, const uint32_t req[64], size_t len, uint32_t *slot) {
149    *slot = 0;
150    /* "METHOD SP PATH[?query] SP VERSION" */
151    size_t sp = 0;
152    while (sp < len && http_byte(req, sp) != ' ') sp++;
153    size_t ps = sp + 1, pe = ps;
154    while (pe < len) {
155        uint8_t c = http_byte(req, pe);
156        if (c == ' ' || c == '\r' || c == '\n' || c == '?') break;
157        pe++;
158    }
159    if (sp >= len || ps >= len || http_byte(req, ps) != '/') return HTTP_RT_BAD;
160    if (!http_eq(req, 0, sp, "GET")) return HTTP_RT_NO_METHOD;
161    static const char *const fixed_p[8] = {"/", "/programs", "/status", "/doom", "/train", "/render", "/render.ppm", "/render.bmp"};
162    static const uint32_t fixed_r[8] = {HTTP_RT_INDEX, HTTP_RT_PROGRAMS, HTTP_RT_STATUS, HTTP_RT_DOOM,
163                                        HTTP_RT_TRAIN, HTTP_RT_RENDER,   HTTP_RT_PPM,    HTTP_RT_BMP};
164    for (int i = 0; i < 8; i++) {
165        if (!http_eq(req, ps, pe, fixed_p[i])) continue;
166        uint32_t r = fixed_r[i], s;
167        if (r == HTTP_RT_RENDER || r == HTTP_RT_PPM || r == HTTP_RT_BMP) {
168            if (mesh_ctx_find(ctx, "pathtrace", 9, &s) || mesh_ctx_find(ctx, "pathtrace-live", 14, &s)) { *slot = s; return r; }
169            *slot = 1;
170            return HTTP_RT_NOT_FOUND;
171        }
172        if (r == HTTP_RT_DOOM || r == HTTP_RT_TRAIN) {
173            bool found = r == HTTP_RT_DOOM ? mesh_ctx_find(ctx, "doom", 4, &s) : mesh_ctx_find(ctx, "train", 5, &s);
174            if (found) { *slot = s; return r; }
175            *slot = 1;
176            return HTTP_RT_NOT_FOUND;
177        }
178        return r;
179    }
180    static const char *const prefixes[4] = {"/start/", "/stop/", "/view/", "/load/"};
181    for (uint32_t verb = 0; verb < 4; verb++) {
182        if (!http_starts(req, ps, pe, prefixes[verb])) continue;
183        size_t pl = 0;
184        while (prefixes[verb][pl]) pl++;
185        size_t at = ps + pl, n = pe - at;
186        uint8_t name[MESH_NAME_BYTES] = {0};
187        if (n == 0 || n > MESH_NAME_MAX) return verb == 3 ? HTTP_RT_BAD : HTTP_RT_NOT_FOUND;
188        for (size_t i = 0; i < n; i++) name[i] = http_byte(req, at + i);
189        uint32_t s;
190        bool found = mesh_ctx_find(ctx, (const char *)name, (uint32_t)n, &s);
191        if (verb == 3) {
192            if (!mesh_valid_name(name, n)) return HTTP_RT_BAD;
193            if (found) { *slot = s; return HTTP_RT_CONFLICT; }
194            return http_load_request(ctx, name, n, slot);
195        }
196        if (!found) return HTTP_RT_NOT_FOUND;
197        *slot = s;
198        if (verb == 2) return HTTP_RT_VIEW;
199        if (verb == 1 && s == mesh_ctx_slot(ctx)) return HTTP_RT_BAD; /* stopping http would end the server */
200        mesh_ctx_request_run(ctx, s, verb == 0);
201        return HTTP_RT_OK;
202    }
203    return HTTP_RT_NOT_FOUND;
204}
205
206/* Where doom's frame is: its own column arrays (local), or, placed on two
207 * machines (pin, split), meshd's frame-matched copy in the show region -- the
208 * current buffer, SH_SEQ & 1. (heights, kinds, sides, dists). All lanes call
209 * it; lane 0's acquire of SH_SEQ picks the buffer for all. */
210static void http_frame_arrays(mesh_view d, uint32_t at[4]) {
211    at[0] = MESH_DOOM_D_HEIGHT, at[1] = MESH_DOOM_D_KIND, at[2] = MESH_DOOM_D_SIDE, at[3] = MESH_DOOM_D_DIST;
212    if (mesh_view_len(d) < MESH_DOOM_SNAP_BYTES || mesh_view_u32(d, MESH_DOOM_D_SHOW + MESH_SH_MODE) == MESH_PLACE_LOCAL) return;
213    uint32_t seq = mesh_rt_shfl_idx(mesh_rt_ld_acquire(mesh_view_ptr(d) + MESH_DOOM_D_SHOW + MESH_SH_SEQ), 0);
214    if (seq == 0) return;
215    uint32_t b = mesh_doom_show_data(seq & 1);
216    at[0] = b + MESH_DOOM_SHOW_HEIGHT, at[1] = b + MESH_DOOM_SHOW_KIND, at[2] = b + MESH_DOOM_SHOW_SIDE, at[3] = b + MESH_DOOM_SHOW_DIST;
217}
218
219/* Wall span of screen column c on a DOOM_ROWS-row frame, and its shade (nearer
220 * is denser; y-sides one shade darker). 0: no wall. */
221static uint32_t http_column(mesh_view d, float rows, const uint32_t at[4], uint32_t c) {
222    uint32_t kind = mesh_view_u32(d, at[1] + 4 * c);
223    if (kind == 0 || !(rows > 0.0f)) return 0;
224    float h = mesh_view_f32(d, at[0] + 4 * c) * (float)MESH_HTTP_DOOM_ROWS / rows;
225    float dist = mesh_view_f32(d, at[3] + 4 * c);
226    uint32_t side = mesh_view_u32(d, at[2] + 4 * c);
227    float half = (float)MESH_HTTP_DOOM_ROWS / 2.0f;
228    float t = half - h / 2.0f, bb = half + h / 2.0f;
229    uint32_t top = t < 0.0f ? 0 : (uint32_t)t;
230    uint32_t bot = bb > (float)MESH_HTTP_DOOM_ROWS ? MESH_HTTP_DOOM_ROWS : (uint32_t)bb;
231    uint32_t shade = dist < 2.0f ? 0 : dist < 4.0f ? 1 : dist < 8.0f ? 2 : 3;
232    if (side == 1 && shade < 3) shade++;
233    static const uint8_t ch[4] = {'#', '%', '=', '-'};
234    return (uint32_t)ch[shade] | top << 8 | bot << 16;
235}
236
237/* Width of the Content-Length value: filled in after the body, padded with
238 * spaces (optional whitespace after a field value is valid HTTP). */
239#define HTTP_CLEN_WIDTH 6u
240
241static const char *http_state_name(uint32_t st) { return st < 5 ? MESH_STATES[st] : "?"; }
242
243/* Tasks/s of slot s since the last page that sampled it (0 on the first). */
244static float http_rate(const mesh_ctx *ctx, http_tab tab, uint32_t s) {
245    mesh_view v = mesh_ctx_view(ctx);
246    uint32_t at = MESH_HTTP_HV_SAMPLES + MESH_HTTP_SAMPLE_BYTES * s;
247    uint32_t d0 = mesh_view_u32(v, at);
248    uint64_t t0 = mesh_view_u64(v, at + 8);
249    uint32_t d1 = tab_ctl(tab, s, MESH_P_DONE);
250    uint64_t t1 = mesh_rt_gtimer();
251    mesh_view_set_u32(v, at, d1);
252    mesh_view_set_u64(v, at + 8, t1);
253    return (t0 == 0 || t1 <= t0) ? 0.0f : (float)(d1 - d0) * 1e9f / (float)(t1 - t0);
254}
255
256/* "c0=v c1=v" for the named counters. */
257static void http_counters(http_w *w, http_tab tab, uint32_t s) {
258    bool first = true;
259    for (uint32_t i = 0; i < MESH_COUNTERS; i++) {
260        uint8_t n[16];
261        http_bytes16(tab_ext(tab, s, MESH_PX_COUNTER_NAMES + MESH_NAME_BYTES * i), n);
262        if (n[0] == 0) continue;
263        if (!first) http_b(w, ' ');
264        first = false;
265        http_name(w, n);
266        http_b(w, '=');
267        http_u(w, tab_ctl(tab, s, MESH_P_COUNTERS + 4 * i));
268    }
269}
270
271static void http_peer(http_w *w, http_tab tab) {
272    uint32_t k = tab_hdr(tab, MESH_H_PEER), n = tab_hdr(tab, MESH_H_SYSTEMS);
273    if (k < n) {
274        http_s(w, "system ");
275        http_u(w, k);
276        http_s(w, " of ");
277        http_u(w, n);
278    } else http_s(w, "single");
279}
280
281/* (step u64, last loss, params) of the program named train, if loaded. */
282static bool http_train_state(const mesh_ctx *ctx, uint64_t *step, float *loss, float p[4]) {
283    uint32_t s;
284    mesh_view t;
285    if (!mesh_ctx_find(ctx, "train", 5, &s) || !mesh_ctx_peer_view(ctx, s, &t)) return false;
286    *step = (uint64_t)mesh_view_u32(t, MESH_TRAIN_T_STEP_HI) << 32 | mesh_view_u32(t, MESH_TRAIN_T_NEXT_STEP);
287    *loss = *step == 0 ? 0.0f : mesh_view_f32(t, MESH_TRAIN_T_LOSS + 4 * (uint32_t)((*step - 1) % 256));
288    for (uint32_t k = 0; k < 4; k++) p[k] = mesh_view_f32(t, MESH_TRAIN_T_PARAMS + 4 * k);
289    return true;
290}
291
292__attribute__((noinline)) static void http_index(http_w *w, const mesh_ctx *ctx, http_tab tab) {
293    http_s(w, "<!doctype html>\n<html><head><meta charset=\"utf-8\"><title>alpha-rust-mesh</title>"
294              "<style>body{font:14px monospace;background:#111;color:#ddd;margin:2em}a{color:#8cf}td,th{padding:0 1em 0 0;text-align:left}</style></head><body>\n"
295              "<h1>alpha-rust-mesh</h1>\n<p>This page was routed and rendered by a GPU warp (program http, slot ");
296    http_u(w, mesh_ctx_slot(ctx));
297    http_s(w, ").</p>\n<p>");
298    http_peer(w, tab);
299    http_s(w, ", epoch ");
300    http_u(w, tab_hdr(tab, MESH_H_EPOCH));
301    http_s(w, "</p>\n<table>\n<tr><th>slot</th><th>program</th><th>state</th><th>nodes</th><th>tasks</th><th>tasks/s</th><th>counters</th><th></th></tr>\n");
302    for (uint32_t s = 0; s < MESH_PROG_SLOTS; s++) {
303        if (!tab_used(tab, s)) continue;
304        uint8_t n[16];
305        tab_name(tab, s, n);
306        http_s(w, "<tr><td>");
307        http_u(w, s);
308        http_s(w, "</td><td><a href=\"/view/");
309        http_name(w, n);
310        http_s(w, "\">");
311        http_name(w, n);
312        http_s(w, "</a></td><td>");
313        http_s(w, http_state_name(tab_ent(tab, s, MESH_PT_STATE)));
314        http_s(w, "</td><td>");
315        http_u(w, tab_ctl(tab, s, MESH_P_ONLINE));
316        http_b(w, '/');
317        http_u(w, tab_ent(tab, s, MESH_PT_NODES));
318        http_s(w, "</td><td>");
319        http_u(w, tab_ctl(tab, s, MESH_P_DONE));
320        http_s(w, "</td><td>");
321        http_f(w, http_rate(ctx, tab, s), 1);
322        http_s(w, "</td><td>");
323        http_counters(w, tab, s);
324        http_s(w, "</td><td><a href=\"/start/");
325        http_name(w, n);
326        http_s(w, "\">start</a> <a href=\"/stop/");
327        http_name(w, n);
328        http_s(w, "\">stop</a></td></tr>\n");
329    }
330    http_s(w, "</table>\n");
331    uint64_t step;
332    float loss, p[4];
333    if (http_train_state(ctx, &step, &loss, p)) {
334        http_s(w, "<p>train step ");
335        http_u(w, step);
336        http_s(w, ", loss ");
337        http_f(w, loss, 6);
338        http_s(w, ", params");
339        for (int k = 0; k < 4; k++) { http_b(w, ' '); http_f(w, p[k], 6); }
340        http_s(w, "</p>\n");
341    }
342    http_s(w, "<p><a href=\"/status\">/status</a> <a href=\"/programs\">/programs</a> <a href=\"/doom\">/doom</a> <a href=\"/train\">/train</a> "
343              "<a href=\"/render\">/render</a> <a href=\"/render.bmp\">/render.bmp</a> | meshd: <a href=\"/fleet\">/fleet</a> <a href=\"/jobs\">/jobs</a> "
344              "<a href=\"/pipes\">/pipes</a> (?format=json)</p>\n</body></html>\n");
345}
346
347/* One line per loaded slot: `slot name state online/nodes policy tasks tasks/s
348 * counters`, then for a CPU executor `native pid P cpu% C rss_mb R` or `wasm
349 * lanes L cpu% C rss_mb R` (the load page's PL_HOST). */
350__attribute__((noinline)) static void http_programs(http_w *w, const mesh_ctx *ctx, http_tab tab) {
351    for (uint32_t s = 0; s < MESH_PROG_SLOTS; s++) {
352        if (!tab_used(tab, s)) continue;
353        uint8_t n[16];
354        tab_name(tab, s, n);
355        http_u(w, s);
356        http_b(w, ' ');
357        http_name(w, n);
358        http_b(w, ' ');
359        http_s(w, http_state_name(tab_ent(tab, s, MESH_PT_STATE)));
360        http_b(w, ' ');
361        http_u(w, tab_ctl(tab, s, MESH_P_ONLINE));
362        http_b(w, '/');
363        http_u(w, tab_ent(tab, s, MESH_PT_NODES));
364        http_b(w, ' ');
365        uint32_t pol = tab_ent(tab, s, MESH_PT_POLICY);
366        http_s(w, pol < 4 ? MESH_POLICIES[pol] : "?");
367        http_b(w, ' ');
368        http_u(w, tab_ctl(tab, s, MESH_P_DONE));
369        http_b(w, ' ');
370        http_f(w, http_rate(ctx, tab, s), 1);
371        http_b(w, ' ');
372        http_counters(w, tab, s);
373        /* A CPU executor (PX_EXEC): native worker process (its pid) or wasm lanes; CPU % (one core = 100) and RSS in MiB. */
374        uint32_t exec = mesh_rt_ld_relaxed(tab_ext(tab, s, MESH_PX_EXEC));
375        if (exec == MESH_BACKEND_NATIVE || exec == MESH_BACKEND_WASM) {
376            size_t pl = mesh_pload(s);
377            if (exec == MESH_BACKEND_NATIVE) {
378                http_s(w, " native pid ");
379                http_u(w, tab_ctl(tab, s, MESH_P_PID));
380            } else {
381                http_s(w, " wasm lanes ");
382                http_u(w, tab_load(tab, pl + MESH_PL_UNITS));
383            }
384            http_s(w, " cpu% ");
385            http_f(w, 100.0f * tab_loadf(tab, pl + MESH_PL_CPU), 1);
386            http_s(w, " rss_mb ");
387            http_u(w, ((uint64_t)tab_load(tab, pl + MESH_PL_RSS) | (uint64_t)tab_load(tab, pl + MESH_PL_RSS + 4) << 32) >> 20);
388        }
389        http_b(w, '\n');
390    }
391}
392
393static void http_opt(http_w *w, const char *name, bool on, float v, uint32_t dec) {
394    http_s(w, name);
395    if (on) http_f(w, v, dec);
396    else http_b(w, '-');
397}
398
399/* The load page (meshd's load sampler, about once a second): `key: value`
400 * lines, `-` for what this machine does not measure. */
401__attribute__((noinline)) static void http_load(http_w *w, http_tab tab) {
402    uint32_t flags = tab_load(tab, MESH_S_LOAD_FLAGS);
403    http_s(w, "load_samples: ");
404    http_u(w, tab_load(tab, MESH_S_LOAD_SAMPLES));
405    bool g = (flags & MESH_LF_GPU) != 0;
406    http_opt(w, "\ngpu_share: ", g, tab_loadf(tab, MESH_S_GPU_SHARE), 3);
407    http_opt(w, "\ngpu_share10: ", g, tab_loadf(tab, MESH_S_GPU_SHARE10), 3);
408    http_opt(w, "\ngpu_slice_us: ", g, tab_loadf(tab, MESH_S_GPU_SLICE_US), 0);
409    http_opt(w, "\ngpu_gap_us: ", g, tab_loadf(tab, MESH_S_GPU_GAP_US), 0);
410    http_opt(w, "\ngpu_max_gap_us: ", true, tab_loadf(tab, MESH_S_GPU_MAX_GAP_US), 0);
411    http_opt(w, "\ngpu_util_ext: ", (flags & MESH_LF_GPU_EXT) != 0, tab_loadf(tab, MESH_S_GPU_UTIL_EXT), 2);
412    http_opt(w, "\ncpu: ", true, tab_loadf(tab, MESH_S_CPU_UTIL), 3);
413    http_opt(w, "\ncpu_big: ", true, tab_loadf(tab, MESH_S_CPU_BIG), 3);
414    http_opt(w, "\ncpu_little: ", tab_load(tab, MESH_S_CPUS_LITTLE) > 0, tab_loadf(tab, MESH_S_CPU_LITTLE), 3);
415    http_opt(w, "\nloadavg: ", true, tab_loadf(tab, MESH_S_LOADAVG), 2);
416    bool p = (flags & MESH_LF_PSI) != 0;
417    http_opt(w, "\npsi_cpu: ", p, tab_loadf(tab, MESH_S_PSI_CPU), 4);
418    http_opt(w, "\npsi_mem: ", p, tab_loadf(tab, MESH_S_PSI_MEM), 4);
419    http_opt(w, "\npsi_io: ", p, tab_loadf(tab, MESH_S_PSI_IO), 4);
420    http_s(w, "\nmem_avail_mb: ");
421    http_u(w, tab_load(tab, MESH_S_MEM_AVAIL_MB));
422    http_s(w, "\nmem_total_mb: ");
423    http_u(w, tab_load(tab, MESH_S_MEM_TOTAL_MB));
424    http_opt(w, "\nmeshd_cpu: ", true, tab_loadf(tab, MESH_S_MESHD_CPU), 3);
425    http_opt(w, "\nmeshd_rss_mb: ", true, tab_loadf(tab, MESH_S_MESHD_RSS_MB), 1);
426    http_s(w, "\ndisk_free_mb: ");
427    http_u(w, tab_load(tab, MESH_S_DISK_FREE_MB));
428    http_b(w, '\n');
429    for (uint32_t s = 0; s < MESH_PROG_SLOTS; s++) {
430        size_t at = mesh_pload(s);
431        uint32_t pf = tab_load(tab, at + MESH_PL_FLAGS);
432        if ((pf & MESH_PLF_LOADED) == 0 || !tab_used(tab, s)) continue;
433        uint8_t n[16];
434        tab_name(tab, s, n);
435        http_s(w, "program_load: ");
436        http_name(w, n);
437        http_opt(w, " share ", (pf & MESH_PLF_GPU) != 0, tab_loadf(tab, at + MESH_PL_SHARE10), 3);
438        http_opt(w, " p50_us ", (pf & MESH_PLF_LAT) != 0, tab_loadf(tab, at + MESH_PL_P50_US), 1);
439        http_opt(w, " p99_us ", (pf & MESH_PLF_LAT) != 0, tab_loadf(tab, at + MESH_PL_P99_US), 1);
440        http_opt(w, " tasks_s ", true, tab_loadf(tab, at + MESH_PL_TASKS_S), 1);
441        http_b(w, '\n');
442    }
443}
444
445__attribute__((noinline)) static void http_status(http_w *w, const mesh_ctx *ctx, http_tab tab, uint32_t served) {
446    uint32_t progs = 0, nodes = 0, online = 0, done = 0;
447    for (uint32_t s = 0; s < MESH_PROG_SLOTS; s++) {
448        if (!tab_used(tab, s)) continue;
449        progs++;
450        nodes += tab_ent(tab, s, MESH_PT_NODES);
451        online += tab_ctl(tab, s, MESH_P_ONLINE);
452        done += tab_ctl(tab, s, MESH_P_DONE);
453    }
454    http_s(w, "programs: "); http_u(w, progs);
455    http_s(w, "\nnodes: "); http_u(w, nodes);
456    http_s(w, "\nonline: "); http_u(w, online);
457    http_s(w, "\ndone: "); http_u(w, done);
458    http_s(w, "\nepoch: "); http_u(w, tab_hdr(tab, MESH_H_EPOCH));
459    http_s(w, "\npeer: "); http_peer(w, tab);
460    uint64_t step;
461    float loss, p[4];
462    if (http_train_state(ctx, &step, &loss, p)) {
463        http_s(w, "\ntrain_step: "); http_u(w, step);
464        http_s(w, "\nloss: "); http_f(w, loss, 6);
465    }
466    http_s(w, "\nhttp_served: "); http_u(w, served);
467    http_b(w, '\n');
468    http_load(w, tab);
469}
470
471__attribute__((noinline)) static void http_train_page(http_w *w, const mesh_ctx *ctx) {
472    uint64_t step;
473    float loss, p[4];
474    if (!http_train_state(ctx, &step, &loss, p)) { http_s(w, "not loaded\n"); return; }
475    static const char *const names[4] = {"a: ", "b: ", "c: ", "d: "};
476    for (int i = 0; i < 4; i++) { http_s(w, names[i]); http_f(w, p[i], 6); http_b(w, '\n'); }
477    http_s(w, "step: "); http_u(w, step);
478    http_s(w, "\nloss: "); http_f(w, loss, 6);
479    http_b(w, '\n');
480}
481
482/* The declared fields of slot s: `name: value` lines. */
483__attribute__((noinline)) static void http_view_page(http_w *w, const mesh_ctx *ctx, http_tab tab, uint32_t s) {
484    uint8_t nm[16];
485    tab_name(tab, s, nm);
486    http_s(w, "program: "); http_name(w, nm);
487    http_s(w, "\nstate: "); http_s(w, http_state_name(tab_ent(tab, s, MESH_PT_STATE)));
488    http_s(w, "\nview_bytes: "); http_u(w, tab_ent(tab, s, MESH_PT_VIEW_BYTES));
489    http_b(w, '\n');
490    mesh_view pv;
491    if (!mesh_ctx_peer_view(ctx, s, &pv)) return;
492    uint32_t n = mesh_rt_rd(tab_ext(tab, s, MESH_PX_FIELD_COUNT));
493    if (n > MESH_MAX_FIELDS) n = MESH_MAX_FIELDS;
494    for (uint32_t f = 0; f < n; f++) {
495        uint8_t *fd = tab_ext(tab, s, MESH_PX_FIELDS + MESH_FIELD_BYTES * f);
496        uint8_t name[16];
497        http_bytes16(fd, name);
498        uint32_t ty = mesh_rt_rd(fd + MESH_FD_TYPE), off = mesh_rt_rd(fd + MESH_FD_OFF);
499        http_name(w, name);
500        /* names are up to 19 bytes: the tail past 16 */
501        for (uint32_t i = 16; i < MESH_FD_NAME_MAX; i++) {
502            uint8_t c = *(const volatile uint8_t *)(fd + i);
503            if (c == 0) break;
504            http_b(w, c);
505        }
506        http_s(w, ": ");
507        switch (ty) {
508        case MESH_FT_U32: http_u(w, mesh_view_u32(pv, off)); break;
509        case MESH_FT_I32: http_i(w, (int32_t)mesh_view_u32(pv, off)); break;
510        case MESH_FT_F32: http_f(w, mesh_view_f32(pv, off), 6); break;
511        case MESH_FT_U64: http_u(w, mesh_view_u64(pv, off)); break;
512        default: http_u(w, mesh_view_u8(pv, off)); break;
513        }
514        http_b(w, '\n');
515    }
516}
517
518/* pathtrace's view, if it is one (its magic and size). */
519static bool http_pt_view(const mesh_ctx *ctx, uint32_t slot, mesh_view *p) {
520    return mesh_ctx_peer_view(ctx, slot, p) && mesh_view_len(*p) >= PT_VIEW_BYTES && mesh_view_u32(*p, PT_V_MAGIC) == PT_VIEW_MAGIC;
521}
522
523/* Lane 0: /render, pathtrace's progress. */
524static void http_render_page(http_w *w, const mesh_ctx *ctx, http_tab tab, uint32_t slot) {
525    mesh_view p;
526    if (!http_pt_view(ctx, slot, &p)) { http_s(w, "pathtrace: no image yet (OP_INIT has not run)\n"); return; }
527    uint8_t nm[16];
528    tab_name(tab, slot, nm);
529    http_name(w, nm);
530    http_s(w, ": "); http_u(w, mesh_view_u32(p, PT_V_WIDTH)); http_b(w, 'x'); http_u(w, mesh_view_u32(p, PT_V_HEIGHT));
531    http_s(w, ", "); http_u(w, mesh_view_u32(p, PT_V_TILES)); http_s(w, " tiles of 32x24, ");
532    http_u(w, mesh_view_u32(p, PT_V_PASSES)); http_s(w, " passes of "); http_u(w, mesh_view_u32(p, PT_V_SPP)); http_s(w, " samples\n");
533    http_s(w, "tiles done "); http_u(w, mesh_view_u32(p, PT_V_DONE));
534    http_s(w, ", items "); http_u(w, mesh_view_u32(p, PT_V_ITEMS));
535    http_s(w, ", scene gen "); http_u(w, mesh_view_u32(p, PT_V_GEN));
536    http_s(w, ", tone map "); http_s(w, mesh_view_u32(p, PT_V_TONEMAP) == 1 ? "reinhard" : "aces");
537    http_s(w, ", last tile "); http_u(w, mesh_view_u32(p, PT_V_LAST_TILE));
538    http_s(w, "\nimage: /render.bmp /render.ppm\n");
539}
540
541static void http_le(http_w *w, uint32_t x, uint32_t n) {
542    for (uint32_t i = 0; i < n; i++) http_b(w, (uint8_t)(x >> (8 * i)));
543}
544
545/* /render.ppm, /render.bmp: the whole warp copies pathtrace's 8-bit pixels
546 * (tile-major 0xffBBGGRR words) into the response, raster order (BMP: rows
547 * bottom-up, BGR). False when there is no image or it does not fit: the text
548 * path answers then. */
549static bool http_image(const mesh_ctx *ctx, uint32_t route, uint32_t slot, uint32_t *code) {
550    uint32_t l = mesh_ctx_lane(ctx);
551    mesh_view p;
552    if (!http_pt_view(ctx, slot, &p)) return false;
553    uint32_t wd = mesh_view_u32(p, PT_V_WIDTH), ht = mesh_view_u32(p, PT_V_HEIGHT), tiles = mesh_view_u32(p, PT_V_TILES);
554    mesh_view resp = mesh_ctx_response(ctx);
555    uint32_t body = 3 * wd * ht;
556    if (wd == 0 || ht == 0 || tiles != (wd / 32) * (ht / 24) || tiles > PT_MAX_TILES || body + 256 > mesh_view_len(resp)) return false;
557    bool bmp = route == HTTP_RT_BMP;
558    uint32_t head = 0;
559    if (l == 0) {
560        http_w w = {mesh_view_ptr(resp), 0, mesh_view_len(resp)};
561        uint32_t file = bmp ? 54 + body : 0;
562        http_s(&w, "HTTP/1.0 200 OK\r\nContent-Type: ");
563        http_s(&w, bmp ? "image/bmp" : "image/x-portable-pixmap");
564        http_s(&w, "\r\nContent-Length: ");
565        uint32_t clen = w.n;
566        for (uint32_t i = 0; i < HTTP_CLEN_WIDTH; i++) http_b(&w, ' ');
567        http_s(&w, "\r\nCache-Control: no-store\r\nConnection: close\r\n\r\n");
568        uint32_t at = w.n;
569        if (bmp) {
570            http_s(&w, "BM"); http_le(&w, file, 4); http_le(&w, 0, 4); http_le(&w, 54, 4);
571            http_le(&w, 40, 4); http_le(&w, wd, 4); http_le(&w, ht, 4); http_le(&w, 1, 2); http_le(&w, 24, 2);
572            http_le(&w, 0, 4); http_le(&w, body, 4); http_le(&w, 2835, 4); http_le(&w, 2835, 4); http_le(&w, 0, 4); http_le(&w, 0, 4);
573        } else {
574            http_s(&w, "P6\n"); http_u(&w, wd); http_b(&w, ' '); http_u(&w, ht); http_s(&w, "\n255\n");
575        }
576        head = w.n;
577        http_w cl = {mesh_view_ptr(resp), clen, clen + HTTP_CLEN_WIDTH};
578        http_u(&cl, head - at + body);
579    }
580    head = mesh_rt_shfl_idx(head, 0);
581    uint32_t tx = wd / 32;
582    for (uint32_t i = l; i < wd * ht; i += 32) {
583        uint32_t yo = i / wd, x = i % wd;
584        uint32_t y = bmp ? ht - 1 - yo : yo;
585        uint32_t t = (y / 24) * tx + x / 32;
586        uint32_t px = mesh_view_u32(p, pt_view_rgb(t) + 4 * ((y % 24) * 32 + x % 32));
587        uint8_t *o = mesh_view_ptr(resp) + head + 3 * i;
588        uint32_t a = bmp ? px >> 16 : px, b = bmp ? px : px >> 16;
589        mesh_rt_wrb(o, (uint8_t)a);
590        mesh_rt_wrb(o + 1, (uint8_t)(px >> 8));
591        mesh_rt_wrb(o + 2, (uint8_t)b);
592    }
593    mesh_rt_fence_acq_rel();
594    mesh_rt_warp_sync();
595    mesh_view v = mesh_ctx_view(ctx);
596    if (l == 0) {
597        mesh_view_set_u32(v, MESH_HTTP_HV_SERVED, mesh_view_u32(v, MESH_HTTP_HV_SERVED) + 1);
598        mesh_view_set_u32(v, MESH_HTTP_HV_LAST_CODE, 200);
599    }
600    mesh_ctx_respond(ctx, head + body);
601    *code = 200;
602    return true;
603}
604
605/* Lane 0: writes the response; its bytes, and the status code in *code. */
606__attribute__((noinline)) static uint32_t http_render(const mesh_ctx *ctx, http_tab tab, uint32_t route, uint32_t slot,
607                                                      const uint32_t cols[MESH_DOOM_WIDTH], uint32_t *code_out) {
608    mesh_view resp = mesh_ctx_response(ctx), v = mesh_ctx_view(ctx);
609    uint32_t code = 200;
610    const char *reason = "OK", *ctype = "text/plain; charset=utf-8";
611    switch (route) {
612    case HTTP_RT_INDEX: ctype = "text/html; charset=utf-8"; break;
613    case HTTP_RT_LOAD: code = 202, reason = "Accepted"; break;
614    case HTTP_RT_BAD: code = 400, reason = "Bad Request"; break;
615    case HTTP_RT_NOT_FOUND: code = 404, reason = "Not Found"; break;
616    case HTTP_RT_NO_METHOD: code = 405, reason = "Method Not Allowed"; break;
617    case HTTP_RT_CONFLICT: code = 409, reason = "Conflict"; break;
618    case HTTP_RT_BUSY: code = 503, reason = "Service Unavailable"; break;
619    default: break;
620    }
621    http_w w = {mesh_view_ptr(resp), 0, mesh_view_len(resp)};
622    http_s(&w, "HTTP/1.0 "); http_u(&w, code); http_b(&w, ' '); http_s(&w, reason);
623    http_s(&w, "\r\nContent-Type: "); http_s(&w, ctype);
624    if (route == HTTP_RT_NO_METHOD) http_s(&w, "\r\nAllow: GET");
625    http_s(&w, "\r\nContent-Length: ");
626    uint32_t clen_at = w.n;
627    for (uint32_t i = 0; i < HTTP_CLEN_WIDTH; i++) http_b(&w, ' ');
628    http_s(&w, "\r\nConnection: close\r\n\r\n");
629    uint32_t body_at = w.n;
630    uint32_t served = mesh_view_u32(v, MESH_HTTP_HV_SERVED) + 1;
631    switch (route) {
632    case HTTP_RT_INDEX: http_index(&w, ctx, tab); break;
633    case HTTP_RT_PROGRAMS: http_programs(&w, ctx, tab); break;
634    case HTTP_RT_STATUS: http_status(&w, ctx, tab, served); break;
635    case HTTP_RT_TRAIN: http_train_page(&w, ctx); break;
636    case HTTP_RT_VIEW: http_view_page(&w, ctx, tab, slot); break;
637    case HTTP_RT_RENDER: http_render_page(&w, ctx, tab, slot); break;
638    case HTTP_RT_DOOM:
639        for (uint32_t r = 0; r < MESH_HTTP_DOOM_ROWS; r++) {
640            for (uint32_t c = 0; c < MESH_DOOM_WIDTH; c++) {
641                uint32_t p = cols[c];
642                uint8_t ch = (uint8_t)p;
643                uint32_t top = (p >> 8) & 0xff, bot = (p >> 16) & 0xff;
644                http_b(&w, (ch != 0 && r >= top && r < bot) ? ch : r < MESH_HTTP_DOOM_ROWS / 2 ? ' ' : '.');
645            }
646            http_b(&w, '\n');
647        }
648        break;
649    case HTTP_RT_OK: http_s(&w, "ok\n"); break;
650    case HTTP_RT_LOAD: http_s(&w, "load requested\n"); break;
651    case HTTP_RT_BAD: http_s(&w, "bad request\n"); break;
652    case HTTP_RT_NOT_FOUND: http_s(&w, slot == 1 ? "not loaded\n" : "not found\n"); break;
653    case HTTP_RT_NO_METHOD: http_s(&w, "method not allowed\n"); break;
654    case HTTP_RT_CONFLICT: http_s(&w, "already loaded\n"); break;
655    default: http_s(&w, "a load request is pending\n"); break;
656    }
657    uint32_t total = w.n;
658    http_w cl = {mesh_view_ptr(resp), clen_at, clen_at + HTTP_CLEN_WIDTH};
659    http_u(&cl, total - body_at);
660    mesh_view_set_u32(v, MESH_HTTP_HV_SERVED, served);
661    mesh_view_set_u32(v, MESH_HTTP_HV_LAST_CODE, code);
662    *code_out = code;
663    return total;
664}
665
666/* Serves the request in the window; returns the HTTP status code. */
667static uint32_t http_serve(const mesh_ctx *ctx) {
668    uint32_t l = mesh_ctx_lane(ctx);
669    mesh_view v = mesh_ctx_view(ctx), reqw;
670    /* Without READS_ALL or the window (a manifest error) the reply is empty. */
671    uint8_t *mb = mesh_ctx_mailbox(ctx);
672    if (!mb || !mesh_view_sub(v, MESH_HTTP_REQ_OFF, MESH_HTTP_REQ_MAX, &reqw)) {
673        mesh_ctx_respond(ctx, 0);
674        return 0;
675    }
676    http_tab tab = {mb};
677    /* Gather the request window: lane l loads words l and l + 32, every lane ends with all 64 words. */
678    uint8_t *rp = mesh_view_ptr(reqw);
679    uint32_t w0 = mesh_rt_rd(rp + 4 * l), w1 = mesh_rt_rd(rp + 4 * (l + 32));
680    uint32_t req[64];
681    for (uint32_t i = 0; i < 32; i++) {
682        req[i] = mesh_rt_shfl_idx(w0, i);
683        req[i + 32] = mesh_rt_shfl_idx(w1, i);
684    }
685    size_t len = mesh_view_len(mesh_ctx_request(ctx));
686
687    uint32_t route = 0, slot = 0;
688    if (l == 0) route = http_parse(ctx, req, len, &slot);
689    route = mesh_rt_shfl_idx(route, 0);
690    slot = mesh_rt_shfl_idx(slot, 0);
691
692    /* The /doom frame, one packed column per entry: char | top << 8 | bottom << 16. */
693    uint32_t cols[MESH_DOOM_WIDTH];
694    for (uint32_t i = 0; i < MESH_DOOM_WIDTH; i++) cols[i] = 0;
695    if (route == HTTP_RT_DOOM) {
696        mesh_view d;
697        if (mesh_ctx_peer_view(ctx, slot, &d)) {
698            float rows = mesh_view_f32(d, MESH_DOOM_D_ROWS);
699            uint32_t at[4];
700            http_frame_arrays(d, at);
701            for (uint32_t k = 0; k < MESH_DOOM_WIDTH / 32; k++) {
702                uint32_t packed = http_column(d, rows, at, 32 * k + l);
703                for (uint32_t i = 0; i < 32; i++) cols[32 * k + i] = mesh_rt_shfl_idx(packed, i);
704            }
705        }
706    }
707    uint32_t code = 0;
708    if ((route == HTTP_RT_PPM || route == HTTP_RT_BMP) && http_image(ctx, route, slot, &code)) return code;
709    uint32_t n = 0;
710    if (l == 0) n = http_render(ctx, tab, route, slot, cols, &code);
711    n = mesh_rt_shfl_idx(n, 0);
712    mesh_ctx_respond(ctx, n);
713    return mesh_rt_shfl_idx(code, 0);
714}
715
716static uint32_t http_task(const mesh_ctx *ctx, uint32_t op, uint32_t arg) {
717    (void)arg;
718    if (op != MESH_OP_REQ) return MESH_R_BAD_OP;
719    return http_serve(ctx);
720}
721
722MESH_PROGRAM(http_init, http_task);