==> src/pipesink_w.c <==1/* pipesink-w: programs/pipesink on the wasm executor (alpha-rust-mesh programs/pipesink-w/wasm;
2 * mesh-abi views::pipedemo, K_*). Drains up to DRAIN digests a task; checks each against mesh-ref's
3 * SHA-256 of the msg rebuilt from its seq; histograms the latency since the source's stamp (the
4 * host clock: the same for every executor). */
5#include "hist_quantile.h"
6#include "mesh/abi/view_pipedemo.h"
7#include "mesh/ref/sha256d.h"
8#include "mesh/rt_wasm.h"
9
10#define DRAIN 512u
11/* An idle task waits this long (ns) for a record. */
12#define WAIT_NS 1000000ull
13
14static void check(const mesh_ctx *ctx, const uint32_t w[12], uint64_t now) {
15 uint64_t stamp = (uint64_t)w[MESH_PIPEDEMO_R_STAMP_LO] | (uint64_t)w[MESH_PIPEDEMO_R_STAMP_HI] << 32;
16 uint32_t m[16];
17 mesh_pipedemo_msg(w[MESH_PIPEDEMO_R_SEQ], stamp, w[MESH_PIPEDEMO_R_ORIGIN] & 0xffu, m);
18 uint8_t bytes[64];
19 for (int i = 0; i < 16; i++)
20 for (int k = 0; k < 4; k++) bytes[4 * i + k] = (uint8_t)(m[i] >> (8 * k));
21 uint8_t want[32], got[32];
22 mesh_sha256d(bytes, sizeof bytes, want);
23 for (int i = 0; i < 8; i++)
24 for (int k = 0; k < 4; k++) got[4 * i + k] = (uint8_t)(w[MESH_PIPEDEMO_R_PAYLOAD + i] >> (24 - 8 * k));
25 mesh_view v = mesh_ctx_view(ctx);
26 mesh_view_add32(v, MESH_PIPEDEMO_K_RECORDS, 1);
27 bool same = true;
28 for (int i = 0; i < 32; i++) same = same && got[i] == want[i];
29 if (!same) {
30 mesh_view_add32(v, MESH_PIPEDEMO_K_BAD, 1);
31 mesh_ctx_count(ctx, 1, 1);
32 mesh_view_write_u32s(v, MESH_PIPEDEMO_K_LAST_BAD, w, 12);
33 }
34 if (w[MESH_PIPEDEMO_R_ORIGIN] & MESH_PIPEDEMO_PATH_GPU) {
35 mesh_view_add32(v, MESH_PIPEDEMO_K_GPU, 1);
36 mesh_ctx_count(ctx, 2, 1);
37 } else {
38 mesh_view_add32(v, MESH_PIPEDEMO_K_CPU, 1);
39 mesh_ctx_count(ctx, 3, 1);
40 }
41 uint64_t d = now > stamp ? (now - stamp) / 1000 : 0;
42 uint32_t us = d > UINT32_MAX ? UINT32_MAX : (uint32_t)d;
43 mesh_view_add32(v, MESH_PIPEDEMO_K_HIST + 4 * (us < MESH_PIPEDEMO_K_BINS - 1 ? us : MESH_PIPEDEMO_K_BINS - 1), 1);
44 if (us > mesh_view_u32(v, MESH_PIPEDEMO_K_MAX_US)) mesh_view_set_u32(v, MESH_PIPEDEMO_K_MAX_US, us);
45}
46
47static uint32_t hist[MESH_PIPEDEMO_K_BINS];
48
49static void stats(const mesh_ctx *ctx) {
50 mesh_view v = mesh_ctx_view(ctx);
51 mesh_view_read_u32s(v, MESH_PIPEDEMO_K_HIST, hist, MESH_PIPEDEMO_K_BINS);
52 mesh_view_set_u32(v, MESH_PIPEDEMO_K_P50_US, hist_quantile(hist, MESH_PIPEDEMO_K_BINS, 500));
53 mesh_view_set_u32(v, MESH_PIPEDEMO_K_P90_US, hist_quantile(hist, MESH_PIPEDEMO_K_BINS, 900));
54 mesh_view_set_u32(v, MESH_PIPEDEMO_K_P99_US, hist_quantile(hist, MESH_PIPEDEMO_K_BINS, 990));
55}
56
57static uint32_t drain(const mesh_ctx *ctx) {
58 uint32_t w[12];
59 uint32_t n = 0, len = 0;
60 /* An empty inbox: wait for a record (bounded, past the driver's tick) before the task ends. */
61 uint64_t t0 = mesh_ctx_host_ns(ctx);
62 while (!mesh_ctx_pipe_len(ctx, mesh_ctx_slot(ctx), &len) || len == 0) {
63 if (mesh_ctx_host_ns(ctx) - t0 > WAIT_NS) {
64 mesh_view_add32(mesh_ctx_view(ctx), MESH_PIPEDEMO_K_EMPTY, 1);
65 return 0;
66 }
67 }
68 while (n < DRAIN && mesh_ctx_pop_u32s(ctx, w, 12)) {
69 check(ctx, w, mesh_ctx_host_ns(ctx));
70 n++;
71 }
72 mesh_ctx_count(ctx, 0, n);
73 if (mesh_ctx_ticket(ctx) % MESH_PIPEDEMO_K_STATS_EVERY == 0) stats(ctx);
74 return n;
75}
76
77static void init(const mesh_ctx *ctx) { (void)ctx; }
78
79static uint32_t task(const mesh_ctx *ctx, uint32_t op, uint32_t arg) {
80 (void)arg;
81 switch (op) {
82 case MESH_OP_TASK: return drain(ctx);
83 case MESH_PIPEDEMO_OP_STATS: stats(ctx); return 0;
84 case MESH_PIPEDEMO_OP_SINK_RESET: {
85 mesh_view v = mesh_ctx_view(ctx);
86 for (uint32_t o = 0; o < MESH_PIPEDEMO_K_HIST; o += 4) mesh_view_set_u32(v, o, 0);
87 static const uint32_t zeros[MESH_PIPEDEMO_K_BINS];
88 mesh_view_write_u32s(v, MESH_PIPEDEMO_K_HIST, zeros, MESH_PIPEDEMO_K_BINS);
89 return 0;
90 }
91 default: return MESH_R_BAD_OP;
92 }
93}
94
95MESH_PROGRAM(init, task)