==> 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);