skip to content
==> tests/node.c <==
1/* mesh-worker lib.rs's tests, one for one: the node loop runs a ring like
2 * mesh-rt; faults, requests and peer views; the input queue pops once each. */
3#include "mesh/check.h"
4#include "mesh/worker_node.h"
5
6#include "mesh/abi/control_block.h"
7#include "mesh/abi/fault.h"
8#include "mesh/abi/input_queue.h"
9#include "mesh/abi/node.h"
10#include "mesh/abi/ops.h"
11#include "mesh/abi/program_table.h"
12#include "mesh/abi/ring.h"
13#include "mesh/abi/trace.h"
14
15#include <pthread.h>
16#include <sched.h>
17#include <stdlib.h>
18#include <string.h>
19#include <time.h>
20
21/* The table entry meshd would write for a slot (view, nodes, request window). */
22static void install(mesh_mailbox mb, uint32_t s, uint32_t nodes, uint32_t view_off, uint32_t view_bytes) {
23    size_t e = mesh_prog_entry(s);
24    const uint32_t kv[][2] = {{MESH_PT_VIEW_OFF, view_off}, {MESH_PT_VIEW_BYTES, view_bytes}, {MESH_PT_NODES, nodes}, {MESH_PT_STATE, MESH_ST_RUNNING},
25                              {MESH_PT_REQ_OFF, 0x100}, {MESH_PT_REQ_MAX, 0x40}, {MESH_PT_RESP_OFF, 0x200}, {MESH_PT_RESP_MAX, 0x40}};
26    for (size_t i = 0; i < 8; i++) mesh_mailbox_set(mb, e + kv[i][0], kv[i][1]);
27    mesh_mailbox_set(mb, e + MESH_PT_NAME, 't' | 's' << 8 | 't' << 16);
28}
29
30static void publish(mesh_mailbox mb, uint32_t s, uint32_t t, uint32_t op, uint32_t arg) {
31    size_t e = mesh_prog_ctl(s) + MESH_P_RING + MESH_ENTRY_BYTES * (t % MESH_PROG_RING);
32    mesh_mailbox_set(mb, e + MESH_E_OP, op);
33    mesh_mailbox_set(mb, e + MESH_E_ARG, arg);
34    mesh_mailbox_set(mb, e + MESH_E_SLOT, t % MESH_PROG_RESULTS);
35    mesh_mailbox_publish(mb, e + MESH_E_SEQ, t + 1);
36}
37
38static bool done(mesh_mailbox mb, uint32_t s, uint32_t t, uint32_t *result) {
39    size_t c = mesh_prog_ctl(s), k = t % MESH_PROG_RESULTS;
40    if (mesh_mailbox_get(mb, c + MESH_P_MARKS + 4 * k) != t + 1) return false;
41    if (result) *result = mesh_mailbox_get(mb, c + MESH_P_RESULTS + 4 * k);
42    return true;
43}
44
45static void init(const mesh_worker_ctx *c, void *u) {
46    (void)u;
47    mesh_ctx_set_u32(c, 0, 0x4f4c4548);
48}
49static uint32_t task(const mesh_worker_ctx *c, uint32_t op, uint32_t arg, void *u) {
50    (void)u;
51    if (op != MESH_OP_TASK) return MESH_R_BAD_OP;
52    mesh_ctx_count(c, 0, 1);
53    mesh_view_add_u32(mesh_ctx_view(c), 8, 1);
54    return arg * 3 + 7;
55}
56
57typedef struct {
58    mesh_mailbox mb;
59    atomic_bool stop;
60} run_arg;
61
62static const mesh_worker_program prog = {init, task, NULL};
63static void *runner(void *a) {
64    run_arg *r = a;
65    mesh_worker_run_nodes(r->mb, 3, &prog, &r->stop);
66    return NULL;
67}
68
69static double secs(const struct timespec *t0) {
70    struct timespec t;
71    clock_gettime(CLOCK_MONOTONIC, &t);
72    return (double)(t.tv_sec - t0->tv_sec) + (double)(t.tv_nsec - t0->tv_nsec) / 1e9;
73}
74
75static void nodes_run_the_ring_like_mesh_rt(void) {
76    mesh_mailbox mb = mesh_mailbox_test();
77    install(mb, 3, 4, 0x100000, 0x1000);
78    run_arg r = {mb, false};
79    pthread_t th;
80    pthread_create(&th, NULL, runner, &r);
81    struct timespec t0;
82    clock_gettime(CLOCK_MONOTONIC, &t0);
83    while (mesh_mailbox_get(mb, mesh_prog_ctl(3) + MESH_P_ONLINE) < 4) {
84        if (!CHECK(secs(&t0) < 5)) break;
85        sched_yield();
86    }
87    publish(mb, 3, 0, MESH_OP_INIT, 0);
88    for (uint32_t t = 1; t < 200; t++) {
89        /* The publisher never runs PROG_RING ahead of the oldest outstanding ticket. */
90        while (t >= MESH_PROG_RING && !done(mb, 3, t - MESH_PROG_RING, NULL)) sched_yield();
91        publish(mb, 3, t, t == 150 ? 9 : MESH_OP_TASK, t);
92    }
93    clock_gettime(CLOCK_MONOTONIC, &t0);
94    while (mesh_mailbox_get(mb, mesh_prog_ctl(3) + MESH_P_DONE) < 200) {
95        if (!CHECK(secs(&t0) < 5)) break;
96        sched_yield();
97    }
98    mesh_mailbox_publish(mb, mesh_prog_ctl(3) + MESH_P_SHUTDOWN, MESH_SHUTDOWN_HALT);
99    pthread_join(th, NULL);
100    size_t c = mesh_prog_ctl(3);
101    CHECK_U(mesh_mailbox_get(mb, c + MESH_P_HALTED), 4);
102    CHECK_U(mesh_mailbox_get(mb, c + MESH_P_DONE), 200);
103    CHECK_U(mesh_mailbox_get(mb, c + MESH_P_COUNTERS), 198);
104    uint32_t res;
105    CHECK(done(mb, 3, 199, &res) && res == 199 * 3 + 7);
106    CHECK(done(mb, 3, 150, &res) && res == MESH_R_BAD_OP);
107    CHECK_U(mesh_mailbox_get(mb, 0x100000), 0x4f4c4548);
108    CHECK_U(mesh_mailbox_get(mb, 0x100008), 198);
109    uint32_t tasks = 0;
110    for (uint32_t n = 0; n < 4; n++) tasks += mesh_mailbox_get(mb, mesh_node_rec(3, n) + MESH_N_TASKS);
111    CHECK_U(tasks, 200);
112    CHECK_U(mesh_mailbox_get(mb, c + MESH_P_TRACE_N), 200);
113    CHECK_U(mesh_mailbox_get(mb, c + MESH_P_FAULT), 0);
114    /* The trace record of ticket 199: slot | node << 8 | op << 16, the ticket, start <= end. */
115    size_t tr = 0;
116    for (uint32_t n = 0; n < 200; n++)
117        if (mesh_mailbox_get(mb, mesh_trace_rec(3, n) + 4) == 199) tr = mesh_trace_rec(3, n);
118    CHECK(tr != 0);
119    CHECK_U(mesh_mailbox_get(mb, tr) & 0xff, 3);
120    CHECK(mesh_mailbox_get(mb, tr + 12) - mesh_mailbox_get(mb, tr + 8) < 1u << 31);
121    free(mb.base);
122}
123
124static void faults_requests_and_peers(void) {
125    mesh_mailbox mb = mesh_mailbox_test();
126    install(mb, 1, 1, 0x100000, 0x400);
127    install(mb, 2, 1, 0x101000, 0x40);
128    mesh_mailbox_set(mb, 0x101000, 77);
129    mesh_worker_ctx c;
130    mesh_worker_ctx_new(mb, 1, 0, &c);
131    /* Bounds: a read past the view is 0 and records the first fault. */
132    CHECK_U(mesh_ctx_view_u32(&c, 0x400), 0);
133    CHECK_U(mesh_mailbox_get(mb, mesh_prog_ctl(1) + MESH_P_FAULT), MESH_FAULT_VIEW_BOUNDS | 0x400 << 8);
134    mesh_ctx_set_u32(&c, 0x3fd, 1);
135    CHECK_U(mesh_mailbox_get(mb, mesh_prog_ctl(1) + MESH_P_FAULT), MESH_FAULT_VIEW_BOUNDS | 0x400 << 8);
136    mesh_mailbox_set(mb, mesh_prog_ctl(1) + MESH_P_FAULT, 0);
137    /* Peer views need the reads mask (re-read per call). */
138    mesh_view pv;
139    CHECK(!mesh_ctx_peer_view(&c, 2, &pv));
140    CHECK_U(mesh_mailbox_get(mb, mesh_prog_ctl(1) + MESH_P_FAULT) & 0xff, MESH_FAULT_PEER_VIEW);
141    mesh_mailbox_set(mb, mesh_prog_entry(1) + MESH_PT_READS_MASK, 1 << 2);
142    CHECK(mesh_ctx_peer_view(&c, 2, &pv) && mesh_view_u32(pv, 0) == 77);
143    uint32_t s = 99;
144    CHECK(mesh_ctx_find(&c, "tst", &s) && s == 1);
145    char name[17];
146    mesh_ctx_prog_name(&c, 2, name);
147    CHECK_S(name, "tst");
148    /* The request window: request bytes, the response and its sequence. */
149    mesh_mailbox_set(mb, mesh_prog_ctl(1) + MESH_P_REQ_LEN, 5);
150    mesh_view_write(mesh_ctx_view(&c), 0x100, (const uint8_t *)"hello", 5);
151    mesh_mailbox_publish64(mb, mesh_prog_ctl(1) + MESH_P_REQ_SEQ, 9);
152    uint8_t b[5];
153    CHECK(mesh_view_read(mesh_ctx_request(&c), 0, b, 5) && memcmp(b, "hello", 5) == 0);
154    mesh_view_write(mesh_ctx_response(&c), 0, (const uint8_t *)"olleh", 5);
155    mesh_ctx_respond(&c, 5);
156    CHECK_U(mesh_mailbox_get64(mb, mesh_prog_ctl(1) + MESH_P_RESP_SEQ), 9);
157    CHECK_U(mesh_mailbox_get(mb, mesh_prog_ctl(1) + MESH_P_RESP_LEN), 5);
158    /* Bulk f32s and their bounds. */
159    float f2[2] = {1.5f, -2.0f};
160    CHECK(mesh_view_write_f32s(mesh_ctx_view(&c), 0x20, f2, 2));
161    CHECK(mesh_ctx_view_f32(&c, 0x20) == 1.5f && mesh_ctx_view_f32(&c, 0x24) == -2.0f);
162    CHECK(!mesh_view_write_f32s(mesh_ctx_view(&c), 0x3fc, f2, 2));
163    free(mb.base);
164}
165
166typedef struct {
167    mesh_mailbox mb;
168    uint32_t node;
169    uint8_t got[16];
170    size_t n;
171} popper;
172
173static void *pop_all(void *a) {
174    popper *p = a;
175    mesh_worker_ctx c;
176    mesh_worker_ctx_new(p->mb, 0, p->node, &c);
177    uint8_t b;
178    while (mesh_ctx_input_pop(&c, &b)) p->got[p->n++] = b;
179    return NULL;
180}
181
182static void input_queue_pops_once_each(void) {
183    mesh_mailbox mb = mesh_mailbox_test();
184    install(mb, 0, 2, 0x100000, 0x1000);
185    uint32_t off = 0x800, len = 64;
186    mesh_mailbox_set(mb, mesh_prog_entry(0) + MESH_PT_INPUT_OFF, off);
187    mesh_mailbox_set(mb, mesh_prog_entry(0) + MESH_PT_INPUT_LEN, len);
188    size_t q = 0x100000 + off;
189    const char *bytes = "abcdefgh";
190    for (size_t i = 0; i < 8; i++) {
191        size_t at = q + MESH_IQ_DATA + i;
192        mesh_mailbox_set(mb, at & ~(size_t)3, mesh_mailbox_get(mb, at & ~(size_t)3) | (uint32_t)(uint8_t)bytes[i] << (8 * (at & 3)));
193    }
194    mesh_mailbox_publish(mb, q + MESH_IQ_HEAD, 8);
195    mesh_worker_ctx c;
196    mesh_worker_ctx_new(mb, 0, 0, &c);
197    CHECK_U(mesh_ctx_input_pending(&c), 8);
198    popper x = {mb, 0, {0}, 0}, y = {mb, 1, {0}, 0};
199    pthread_t tx, ty;
200    pthread_create(&tx, NULL, pop_all, &x);
201    pthread_create(&ty, NULL, pop_all, &y);
202    pthread_join(tx, NULL);
203    pthread_join(ty, NULL);
204    CHECK_U(x.n + y.n, 8);
205    int seen[256] = {0};
206    for (size_t i = 0; i < x.n; i++) seen[x.got[i]]++;
207    for (size_t i = 0; i < y.n; i++) seen[y.got[i]]++;
208    for (size_t i = 0; i < 8; i++) CHECK_I(seen[(uint8_t)bytes[i]], 1);
209    uint8_t b;
210    CHECK(!mesh_ctx_input_pop(&c, &b));
211    free(mb.base);
212}
213
214int main(void) {
215    nodes_run_the_ring_like_mesh_rt();
216    faults_requests_and_peers();
217    input_queue_pops_once_each();
218    return mesh_check_done();
219}