==> tests/place.c <==1/* Placement in the driver (driver.rs's placement section) over a fake CPU executor: a peered driver,
2 * member 0 of 2, with a placeable program. Its placer exists from the load; `place` reports and asks for
3 * a change (as the leader: pin here, done at once with the other member down; as a follower: refused,
4 * the leader coordinates); the leader's PL_SET pins it on the other member (its state written into the
5 * view, the kernel stopped here, start and reset refused with the Rust's reason, input forwarded with
6 * F_INPUT, PL_DONE answered, the inventory still counting it run) and back to local (it runs again);
7 * the fleet's view reaches the placer; unload drops it. */
8#define _XOPEN_SOURCE 700 /* sexc compiles tests without the package's cflags (reported) */
9#include "mesh/check.h"
10#include "mesh/driver.h"
11#include "mesh/log.h"
12#include "mesh/manifest.h"
13#include "mesh/place_wire.h"
14
15#include <pthread.h>
16#include <stdatomic.h>
17#include <stdio.h>
18#include <stdlib.h>
19#include <string.h>
20#include <sys/stat.h>
21#include <unistd.h>
22
23/* ---- a fake executor ---- */
24
25static mesh_mailbox MB;
26
27typedef struct {
28 pthread_t th;
29 bool started;
30 _Atomic bool running;
31 uint32_t slot, nodes;
32} fake_kernel;
33
34static bool k_zero(const mesh_kernel *k, uint32_t *o, char **e) { (void)k, (void)e; *o = 0; return true; }
35static bool k_one(const mesh_kernel *k, uint32_t *o, char **e) { (void)k, (void)e; *o = 1; return true; }
36static uint64_t k_scratch(const mesh_kernel *k) { (void)k; return 0; }
37static bool k_global(const mesh_kernel *k, const char *n, uint32_t *o, char **e) {
38 (void)k;
39 if (strcmp(n, MESH_RT_ABI_SYMBOL)) { *e = strdup("no such global"); return false; }
40 *o = MESH_RT_ABI;
41 return true;
42}
43static void k_drop(mesh_kernel *k) {
44 fake_kernel *f = k->self;
45 if (f->started) pthread_join(f->th, NULL);
46 free(f);
47}
48static const mesh_kernel_ops KOPS = {MESH_BACKEND_WASM, k_zero, k_zero, k_one, k_scratch, k_global, k_drop};
49
50/* The node loop: online, take tickets in order, run them (OP_INIT writes a magic; a task is arg * 3 + 7), halt on P_SHUTDOWN. */
51static void *nodes(void *arg) {
52 fake_kernel *f = arg;
53 size_t c = mesh_prog_ctl(f->slot);
54 mesh_mailbox_fetch_add(MB, c + MESH_P_ONLINE, f->nodes);
55 uint32_t view = mesh_mailbox_get(MB, mesh_prog_entry(f->slot) + MESH_PT_VIEW_OFF);
56 for (;;) {
57 if (mesh_mailbox_get(MB, c + MESH_P_SHUTDOWN)) break;
58 uint32_t t = mesh_mailbox_get(MB, c + MESH_P_RING_HEAD);
59 size_t e = c + MESH_P_RING + MESH_ENTRY_BYTES * (t % MESH_PROG_RING);
60 if (mesh_mailbox_get(MB, e + MESH_E_SEQ) != t + 1) { mesh_sleep_us(50); continue; }
61 uint32_t op = mesh_mailbox_get(MB, e + MESH_E_OP), a = mesh_mailbox_get(MB, e + MESH_E_ARG), k = mesh_mailbox_get(MB, e + MESH_E_SLOT);
62 uint32_t r = 0;
63 if (op == MESH_OP_INIT) mesh_mailbox_set(MB, view, 0x4f4c4548u);
64 else if (op == MESH_OP_TASK) r = a * 3 + 7;
65 else r = MESH_R_BAD_OP;
66 mesh_mailbox_set(MB, c + MESH_P_RING_HEAD, t + 1);
67 mesh_mailbox_set(MB, c + MESH_P_RESULTS + 4 * k, r);
68 mesh_mailbox_publish(MB, c + MESH_P_MARKS + 4 * k, t + 1);
69 mesh_mailbox_fetch_add(MB, c + MESH_P_DONE, 1);
70 }
71 mesh_mailbox_fetch_add(MB, c + MESH_P_HALTED, f->nodes);
72 atomic_store(&f->running, false);
73 return NULL;
74}
75
76static mesh_caps CAPS = {MESH_BACKEND_WASM, "fake", 0, 0, 1, 1, false, 0, 0, 0, 0, "fake"};
77static const mesh_caps *b_caps(const mesh_backend *b) { (void)b; return &CAPS; }
78static bool b_true(const mesh_backend *b) { (void)b; return true; }
79static bool b_false(const mesh_backend *b) { (void)b; return false; }
80static uint32_t b_iso(const mesh_backend *b) { (void)b; return MESH_ISOLATION_LEASE; }
81static bool b_mailbox(const mesh_backend *b, uint8_t **h, uint64_t *dev, char **e) { (void)b, (void)e; *h = MB.base; *dev = 0; return true; }
82static bool b_current(const mesh_backend *b, char **e) { (void)b, (void)e; return true; }
83
84static bool c_needs_ptx(const mesh_compiler *c) { (void)c; return false; }
85static bool c_prepare(const mesh_compiler *c, mesh_payload *src, const uint64_t *expect, const char *root, mesh_prepared *out, char **err) {
86 (void)c, (void)expect, (void)root;
87 memset(out, 0, sizeof *out);
88 if (!mesh_check_manifest(src, MESH_BACKEND_WASM, &out->manifest, err)) { mesh_payload_free(src); return false; }
89 out->hash = 0x1234;
90 out->code_len = strlen(src->manifest);
91 out->code = (uint8_t *)strdup(src->manifest);
92 out->src = *src;
93 memset(src, 0, sizeof *src);
94 out->exec = MESH_BACKEND_WASM;
95 out->kernel = calloc(1, sizeof *out->kernel);
96 out->kernel->ops = &KOPS;
97 out->kernel->self = calloc(1, sizeof(fake_kernel));
98 return true;
99}
100static const mesh_compiler_ops COPS = {c_needs_ptx, c_prepare};
101static const mesh_compiler COMP = {&COPS, NULL};
102static const mesh_compiler *b_compiler(const mesh_backend *b) { (void)b; return &COMP; }
103
104static bool b_load(const mesh_backend *b, mesh_prepared *p, mesh_kernel **out, char **e) {
105 (void)b, (void)e;
106 *out = p->kernel;
107 p->kernel = NULL;
108 return true;
109}
110static bool b_stream(const mesh_backend *b, uint32_t slot, const mesh_kernel *k, mesh_stream **out, char **e) {
111 (void)b, (void)k, (void)e;
112 *out = calloc(1, sizeof **out);
113 (*out)->exec = MESH_BACKEND_WASM;
114 (*out)->slot = slot;
115 return true;
116}
117static bool b_launch(const mesh_backend *b, const mesh_stream *st, const mesh_kernel *k, uint32_t n, uint64_t dev, uint32_t slot, char **e) {
118 (void)b, (void)st, (void)dev, (void)e;
119 fake_kernel *f = k->self;
120 if (f->started) pthread_join(f->th, NULL);
121 f->slot = slot;
122 f->nodes = n;
123 atomic_store(&f->running, true);
124 f->started = true;
125 pthread_create(&f->th, NULL, nodes, f);
126 return true;
127}
128static bool b_query(const mesh_backend *b, const mesh_stream *st, const mesh_kernel *k, bool *done, char **e) {
129 (void)b, (void)st, (void)e;
130 *done = !atomic_load(&((fake_kernel *)k->self)->running);
131 return true;
132}
133static bool b_unload(const mesh_backend *b, mesh_kernel *k, char **e) {
134 (void)b, (void)e;
135 mesh_kernel_drop(k);
136 return true;
137}
138static bool b_stack(const mesh_backend *b, size_t *o, char **e) { (void)b, (void)e; *o = 0; return true; }
139static bool b_set_stack(const mesh_backend *b, size_t n, char **e) { (void)b, (void)n, (void)e; return true; }
140static bool b_scope(const mesh_backend *b, bool has, uint32_t slot, uint32_t *scope) { (void)b; *scope = has ? slot : MESH_NO_SLOT; return true; }
141
142static void b_destroy(mesh_backend *b) { free(b); }
143static const mesh_backend_ops BOPS = {b_caps, b_true, b_false, b_iso, b_mailbox, b_current, b_compiler, b_load, b_stream, b_launch, b_query,
144 b_unload, b_stack, b_set_stack, b_scope, NULL, NULL, NULL, NULL, NULL, b_destroy};
145
146/* ---- helpers ---- */
147
148static mesh_shared *SH;
149
150static bool ask(mesh_cmd_kind k, const char *name, bool on, char **text) {
151 mesh_cmd c = mesh_cmd_new(k);
152 if (name) c.name = strdup(name);
153 c.on = on;
154 return mesh_shared_ask(SH, c, 10000, text);
155}
156
157static bool load(const char *dir, char **text) {
158 mesh_cmd c = mesh_cmd_new(MESH_CMD_LOAD);
159 c.has_src = true;
160 c.src = mesh_src_catalog(dir);
161 return mesh_shared_ask(SH, c, 10000, text);
162}
163
164
165static bool place(const char *name, const char *words, char **text) {
166 mesh_cmd c = mesh_cmd_new(MESH_CMD_PLACE);
167 c.name = strdup(name);
168 if (words) {
169 c.has_words = true;
170 c.words = calloc(8, sizeof *c.words);
171 char *w = strdup(words), *save = NULL;
172 for (char *t = strtok_r(w, " ", &save); t; t = strtok_r(NULL, " ", &save)) c.words[c.nwords++] = strdup(t);
173 free(w);
174 }
175 return mesh_shared_ask(SH, c, 10000, text);
176}
177
178static void fleet_view(uint32_t up, bool has_leader, uint32_t leader) {
179 mesh_cmd c = mesh_cmd_new(MESH_CMD_FLEET_VIEW);
180 c.up = up;
181 c.has_leader = has_leader;
182 c.leader = leader;
183 mesh_chan_send(SH->cmd, &c);
184}
185
186static void place_free(void *m) {
187 mpw_place_free(m);
188 free(m);
189}
190
191/* F_PLACE from the leader (system 1): PL_SET mode, the members, the state from system `src`. */
192static void pl_set(mpm_mode mode, uint32_t id, uint32_t members, uint32_t src, size_t state_len) {
193 mpw_place *m = calloc(1, sizeof *m);
194 m->op = MESH_PL_SET;
195 m->id = id;
196 strcpy(m->name, "dm");
197 m->mode = mode;
198 m->aux = members | src << 8;
199 m->nbytes = state_len;
200 m->bytes = calloc(1, state_len ? state_len : 1);
201 for (size_t i = 0; i < state_len; i++) m->bytes[i] = (uint8_t)(i * 7 + 1);
202 mesh_cmd c = mesh_cmd_new(MESH_CMD_PEER_PLACE);
203 c.msg = m;
204 c.msg_free = place_free;
205 c.from = 1;
206 mesh_chan_send(SH->cmd, &c);
207}
208
209/* The next frame queued for system 1 (type byte), or -1 within 2 s. */
210static int next_frame(mesh_link_frame *out) {
211 for (int i = 0; i < 2000; i++) {
212 if (mesh_chan_try_recv(SH->link_out[1], out) == MESH_CHAN_OK) {
213 if (out->n == 0) continue;
214 return out->b[0];
215 }
216 mesh_sleep_us(1000);
217 }
218 return -1;
219}
220
221static void *run(void *arg) {
222 *(int *)((void **)arg)[0] = mesh_driver_run((mesh_driver *)((void **)arg)[1]);
223 return NULL;
224}
225
226static uint32_t p_run(void) { return mesh_mailbox_get(MB, mesh_prog_ctl(0) + MESH_P_RUN); }
227
228static bool wait_state(uint32_t st) {
229 for (int i = 0; i < 3000; i++) {
230 if (mesh_mailbox_get(MB, mesh_prog_entry(0) + MESH_PT_STATE) == st) return true;
231 mesh_sleep_us(1000);
232 }
233 return false;
234}
235
236int main(void) {
237 char root[64], path[256];
238 snprintf(root, sizeof root, "/tmp/mesh-driver-place-%d", (int)getpid());
239 mkdir(root, 0700);
240 snprintf(path, sizeof path, "%s/dm", root);
241 mkdir(path, 0700);
242 snprintf(path, sizeof path, "%s/dm/program.toml", root);
243 FILE *f = fopen(path, "w");
244 fputs("name = \"dm\"\nabi = 6\nview_bytes = 0x4000\nview_abi = 2\nnodes = 1\npolicy = \"periodic\"\nhz = 10\nin_flight = \"batch\"\nfanout = 4\ninput = 64\nautostart = true\n"
245 "backends = [\"wasm\"]\nplace = \"split\"\nplace_state = [\"0x0:0x24\", \"0x28:0x4\", \"0x100:0x400\"]\nplace_frame = 0x20\n"
246 "place_out = [\"0x1000:0x200\", \"0x1400:0x200\", \"0x1800:0x200\", \"0x1c00:0x200\"]\nplace_show = 0x2000\n",
247 f);
248 fclose(f);
249 mesh_log_set_fd(-1);
250 MB = mesh_mailbox_test();
251 static _Atomic bool quit;
252 SH = mesh_shared_new(MB, root, true, 0, 2, &quit);
253 SH->link_out[1] = mesh_link_queue_new(64);
254 SH->bulk_out[1] = mesh_link_queue_new(8);
255 mesh_backend *gpu = calloc(1, sizeof *gpu);
256 gpu->ops = &BOPS;
257 mesh_driver_config cfg = {true, NULL, false, false};
258 mesh_driver *d = mesh_driver_new(SH, gpu, NULL, 0, cfg);
259 char *err = NULL, *text = NULL;
260 CHECK(mesh_driver_start(d, &err));
261 void *arg[2] = {0, d};
262 int code = -1;
263 arg[0] = &code;
264 pthread_t th;
265 pthread_create(&th, NULL, run, arg);
266 if (!CHECK(load("dm", &text))) fprintf(stderr, "load dm: %s\n", text);
267 free(text);
268 CHECK(wait_state(MESH_ST_RUNNING));
269
270 /* Local at first; the report. */
271 CHECK(place("dm", NULL, &text));
272 CHECK(strncmp(text, "local role Local system 0 paused false shown 0 lat_n 0 lat_sum_us 0 lat_max_us 0 ignored 0 part_ns 0 uptime_ms ", 106) == 0);
273 free(text);
274 /* The leader with the other member down: pin here, at once. */
275 fleet_view(1, true, 0);
276 CHECK(place("dm", "pin 0", &text));
277 free(text);
278 CHECK(place("dm", NULL, &text));
279 CHECK(strncmp(text, "pin 0 role Runner system 0 ", 27) == 0);
280 free(text);
281 CHECK_U(mesh_mailbox_get(MB, mesh_prog_ext(0) + MESH_PX_PLACE), MESH_PLACE_PIN);
282 CHECK_U(p_run(), 1);
283 CHECK(!place("dm", "pin 7", &text));
284 CHECK(strstr(text, "7") != NULL);
285 free(text);
286
287 /* A follower now: the leader (system 1) coordinates. */
288 fleet_view(3, true, 1);
289 CHECK(!place("dm", "pin 0", &text));
290 CHECK_S(text, "peer: placement changes are coordinated by the leader, system 1");
291 free(text);
292 /* PL_SET pin 1 from the leader with its state: written here, the kernel stops, PL_DONE back. */
293 size_t st_len = 0x24 + 0x4 + 0x400;
294 pl_set(mpm_pin(1), 9, 3, 1, st_len);
295 mesh_link_frame fr;
296 int ty;
297 while ((ty = next_frame(&fr)) >= 0 && ty != MESH_F_PLACE) free(fr.b);
298 CHECK_I(ty, MESH_F_PLACE);
299 if (ty == MESH_F_PLACE) {
300 size_t rest = 0;
301 mpw_place got;
302 CHECK(mpw_place_rest_len(fr.b + 1, &rest, &err) && mpw_place_decode(fr.b + 1, fr.b + 1 + MPW_PLACE_HEAD, rest, &got, &err));
303 CHECK_U(got.op, MESH_PL_DONE);
304 CHECK_U(got.id, 9);
305 mpw_place_free(&got);
306 free(fr.b);
307 }
308 CHECK(wait_state(MESH_ST_LOADED));
309 CHECK_U(p_run(), 0);
310 uint32_t view = mesh_mailbox_get(MB, mesh_prog_entry(0) + MESH_PT_VIEW_OFF);
311 uint8_t b[4];
312 mesh_mailbox_read(MB, view + 0x100, b, 4);
313 CHECK(b[0] == (uint8_t)(0x28 * 7 + 1) && b[1] == (uint8_t)(0x29 * 7 + 1)); /* the third region's first bytes */
314 CHECK(!ask(MESH_CMD_PROG_RUN, "dm", true, &text));
315 CHECK_S(text, "dm is placed (pin 1): system 1 runs it; `place dm local` first, or change it there");
316 free(text);
317 CHECK(!ask(MESH_CMD_RESET, "dm", false, &text));
318 CHECK_S(text, "dm is placed (pin 1): system 1 runs it; `place dm local` first, or change it there");
319 free(text);
320 /* The inventory counts it run (its placement stopped it). */
321 mesh_reply *r = mesh_reply_new();
322 mesh_cmd inv = mesh_cmd_new(MESH_CMD_INVENTORY);
323 inv.reply = mesh_reply_sender(r);
324 mesh_chan_send(SH->cmd, &inv);
325 mesh_result x;
326 CHECK_I(mesh_reply_wait(r, 10000, &x), MESH_CHAN_OK);
327 mesh_reply_release(r);
328 const mesh_actuals *a = x.ptr;
329 CHECK(a && a->n == 1 && a->v[0].run);
330 mesh_result_free(&x);
331 /* Input goes to system 1. */
332 mesh_cmd in = mesh_cmd_new(MESH_CMD_INPUT);
333 in.name = strdup("dm");
334 in.bytes = (uint8_t *)strdup("wasd");
335 in.len = 4;
336 CHECK(mesh_shared_ask(SH, in, 10000, &text));
337 CHECK_S(text, "4 bytes to system 1");
338 free(text);
339 while ((ty = next_frame(&fr)) >= 0 && ty != MESH_F_INPUT) free(fr.b);
340 CHECK_I(ty, MESH_F_INPUT);
341 if (ty >= 0) free(fr.b);
342
343 /* Back to local from the leader: it runs here again. */
344 pl_set(mpm_local(), 10, 3, 1, 0);
345 CHECK(wait_state(MESH_ST_RUNNING));
346 CHECK_U(p_run(), 1);
347 CHECK(place("dm", NULL, &text));
348 CHECK(strncmp(text, "local role Local system 0 ", 26) == 0);
349 free(text);
350 CHECK(ask(MESH_CMD_PROG_RUN, "dm", false, &text));
351 free(text);
352
353 /* Unloaded: its placer goes with it. */
354 CHECK(ask(MESH_CMD_UNLOAD, "dm", false, &text));
355 free(text);
356 CHECK(!place("dm", NULL, &text));
357 CHECK_S(text, "no program \"dm\" (`programs` lists them)");
358 free(text);
359
360 atomic_store(&quit, true);
361 pthread_join(th, NULL);
362 CHECK_I(code, 0);
363 mesh_driver_free(d);
364 mesh_shared_free(SH);
365 free(MB.base);
366 unlink(path);
367 snprintf(path, sizeof path, "%s/dm", root);
368 rmdir(path);
369 rmdir(root);
370 return mesh_check_done();
371}