skip to content
==> src/pipesink.c <==
1/* pipesink: the pipe demo's sink as a native worker (view pipedemo, K_*). Each
2 * continuous task drains up to DRAIN digests from its inbox; each is checked
3 * against mesh-ref's SHA-256d of the msg rebuilt from its seq (an
4 * implementation independent of the hashing stage's), and its latency -- the
5 * host clock now minus the source's stamp: one clock for every executor --
6 * goes into a 1 us histogram in the view.
7 * Ported from alpha-rust-mesh programs/pipesink/src/main.rs. */
8#include "pipesink.h"
9
10#include "hist_quantile.h"
11#include "mesh/abi/ops.h"
12#include "mesh/abi/view_pipedemo.h"
13#include "mesh/ref/sha256d.h"
14
15#include <string.h>
16
17#define DRAIN 512u
18/* An idle task waits this long (ns) for a record. */
19#define WAIT_NS UINT64_C(1000000)
20
21static void check(const mesh_worker_ctx *ctx, const uint8_t rec[48], uint64_t now) {
22    uint32_t w[12];
23    for (int i = 0; i < 12; i++) w[i] = (uint32_t)rec[4 * i] | (uint32_t)rec[4 * i + 1] << 8 | (uint32_t)rec[4 * i + 2] << 16 | (uint32_t)rec[4 * i + 3] << 24;
24    uint64_t stamp = w[MESH_PIPEDEMO_R_STAMP_LO] | (uint64_t)w[MESH_PIPEDEMO_R_STAMP_HI] << 32;
25    uint32_t m[16];
26    mesh_pipedemo_msg(w[MESH_PIPEDEMO_R_SEQ], stamp, w[MESH_PIPEDEMO_R_ORIGIN] & 0xff, m);
27    uint8_t bytes[64], want[32], got[32];
28    for (int i = 0; i < 16; i++)
29        for (int k = 0; k < 4; k++) bytes[4 * i + k] = (uint8_t)(m[i] >> (8 * k));
30    mesh_sha256d(bytes, sizeof bytes, want);
31    for (int i = 0; i < 8; i++)
32        for (int k = 0; k < 4; k++) got[4 * i + k] = (uint8_t)(w[MESH_PIPEDEMO_R_PAYLOAD + i] >> (24 - 8 * k));
33    mesh_view v = mesh_ctx_view(ctx);
34    mesh_view_add_u32(v, MESH_PIPEDEMO_K_RECORDS, 1);
35    if (memcmp(got, want, 32) != 0) {
36        mesh_view_add_u32(v, MESH_PIPEDEMO_K_BAD, 1);
37        mesh_ctx_count(ctx, 1, 1);
38        mesh_view_write(v, MESH_PIPEDEMO_K_LAST_BAD, rec, 48);
39    }
40    if (w[MESH_PIPEDEMO_R_ORIGIN] & MESH_PIPEDEMO_PATH_GPU) {
41        mesh_view_add_u32(v, MESH_PIPEDEMO_K_GPU, 1);
42        mesh_ctx_count(ctx, 2, 1);
43    } else {
44        mesh_view_add_u32(v, MESH_PIPEDEMO_K_CPU, 1);
45        mesh_ctx_count(ctx, 3, 1);
46    }
47    uint64_t d = now > stamp ? (now - stamp) / 1000 : 0;
48    uint32_t us = d > UINT32_MAX ? UINT32_MAX : (uint32_t)d;
49    mesh_view_add_u32(v, MESH_PIPEDEMO_K_HIST + 4 * (us < MESH_PIPEDEMO_K_BINS - 1 ? us : MESH_PIPEDEMO_K_BINS - 1), 1);
50    if (us > mesh_view_u32(v, MESH_PIPEDEMO_K_MAX_US)) mesh_view_set_u32(v, MESH_PIPEDEMO_K_MAX_US, us);
51}
52
53static void stats(const mesh_worker_ctx *ctx) {
54    mesh_view v = mesh_ctx_view(ctx);
55    static _Thread_local uint32_t hist[MESH_PIPEDEMO_K_BINS];
56    for (uint32_t i = 0; i < MESH_PIPEDEMO_K_BINS; i++) hist[i] = mesh_view_u32(v, MESH_PIPEDEMO_K_HIST + 4 * i);
57    const uint32_t q[3][2] = {{MESH_PIPEDEMO_K_P50_US, 500}, {MESH_PIPEDEMO_K_P90_US, 900}, {MESH_PIPEDEMO_K_P99_US, 990}};
58    for (int i = 0; i < 3; i++) mesh_view_set_u32(v, q[i][0], hist_quantile(hist, MESH_PIPEDEMO_K_BINS, q[i][1]));
59}
60
61static uint32_t drain(const mesh_worker_ctx *ctx) {
62    uint8_t rec[48];
63    uint32_t n = 0, len = 0;
64    /* An empty inbox: wait for a record (bounded, past the driver's tick) before the task ends. */
65    uint64_t t0 = mesh_ctx_host_ns(ctx);
66    while (!mesh_ctx_pipe_len(ctx, mesh_ctx_slot(ctx), &len) || len == 0) {
67        if (mesh_ctx_host_ns(ctx) - t0 > WAIT_NS) {
68            mesh_view_add_u32(mesh_ctx_view(ctx), MESH_PIPEDEMO_K_EMPTY, 1);
69            return 0;
70        }
71    }
72    while (n < DRAIN && mesh_ctx_pop(ctx, rec, sizeof rec)) {
73        check(ctx, rec, mesh_ctx_host_ns(ctx));
74        n++;
75    }
76    mesh_ctx_count(ctx, 0, n);
77    if (mesh_ctx_ticket(ctx) % MESH_PIPEDEMO_K_STATS_EVERY == 0) stats(ctx);
78    return n;
79}
80
81uint32_t pipesink_task(const mesh_worker_ctx *ctx, uint32_t op, uint32_t arg, void *user) {
82    (void)arg, (void)user;
83    switch (op) {
84    case MESH_OP_TASK: return drain(ctx);
85    case MESH_PIPEDEMO_OP_STATS: stats(ctx); return 0;
86    case MESH_PIPEDEMO_OP_SINK_RESET: {
87        mesh_view v = mesh_ctx_view(ctx);
88        for (uint32_t o = 0; o < MESH_PIPEDEMO_K_HIST; o += 4) mesh_view_set_u32(v, o, 0);
89        for (uint32_t i = 0; i < MESH_PIPEDEMO_K_BINS; i++) mesh_view_set_u32(v, MESH_PIPEDEMO_K_HIST + 4 * i, 0);
90        return 0;
91    }
92    default: return MESH_R_BAD_OP;
93    }
94}
95
96void pipesink_init(const mesh_worker_ctx *ctx, void *user) { (void)ctx, (void)user; }