/* test_manage.c -- the kernel's side of births (docs/v4.0.0/MESH.md 9a.4): * the registry of live nodes, and who may ask what. At the level of C: a * request's arguments are put on the asking node's stack and the request is * served, as the kernel does when the node writes its number to port 0. */ #include "v4/fabric.h" #include "v4/manage.h" #include #include static int failures = 0, checks = 0; #define CHECK(c,...) do{checks++; if(!(c)){failures++; printf("FAIL %s:%d: ",__FILE__,__LINE__); printf(__VA_ARGS__); printf("\n");}}while(0) #define PB ((v4_cell)(V4_NODE_WORDS - 24u)) /* the ports */ #define ERR ((v4_cell)(V4_NODE_WORDS - 40u)) /* NODE-ERROR */ #define NODES 8 static v4_fabric_node pool[NODES]; static int used[NODES]; static v4_place places[NODES]; static v4_fabric f; static v4_manage_entry reg[NODES]; static unsigned gone_count, parity_count; static v4_cell parity_number; static v4_manage m; static int born(void *self) { unsigned k; int place; (void)self; for (k = 0; k < NODES && used[k]; k++) { } if (k == NODES) return -1; place = v4_fabric_add(&f, &pool[k], PB); if (place < 0) return -1; used[k] = 1; v4_node_error_attach(&pool[k].n, ERR); return place; } static void gone(void *self, unsigned place, v4_fabric_node *was) { (void)self; (void)place; used[was - pool] = 0; gone_count++; } static int open_(void *self, unsigned asker, const char *name, unsigned len) { (void)self; (void)asker; return len == 5 && memcmp(name, "v4:ok", 5) == 0; } static int cell_(void *self, unsigned asker, v4_cell *w) { (void)self; *w = (v4_cell)asker; return 1; } static int line_(void *self, unsigned asker, char *text) { (void)self; (void)asker; text[0] = 'O'; text[1] = 'K'; return 2; } static void parity_(void *self, unsigned place, v4_cell number, const v4_node *n) { (void)self; (void)place; (void)n; parity_count++; parity_number = number; } static v4_node *N(unsigned place) { return &v4_fabric_node_at(&f, place)->n; } static void push(unsigned place, v4_cell x) { v4_dstack_push(&N(place)->ds, x); } static v4_cell pop(unsigned place) { return v4_dstack_pop(&N(place)->ds); } static unsigned depth(unsigned place) { return N(place)->ds.depth; } /* serve a request; 1 if it raised error 12 on the asker */ static int refused(unsigned place, v4_cell request) { unsigned before = N(place)->faults; CHECK(v4_manage_serve(&m, place, request) == 1, "request %ld is served or refused", (long)request); return N(place)->faults == before + 1 && N(place)->fault_kind == V4_FAULT_RAISED && N(place)->fault_addr == 12; } static v4_cell place_of(unsigned asker, v4_cell number) { push(asker, number); (void)v4_manage_serve(&m, asker, V4_REQ_NODE_PLACE); return pop(asker); } static v4_cell child(unsigned asker, v4_cell number, v4_cell i) { push(asker, number); push(asker, i); (void)v4_manage_serve(&m, asker, V4_REQ_NODE_CHILD); return pop(asker); } /* asker births `number`: its place, or -1 */ static v4_cell birth(unsigned asker, v4_cell number) { push(asker, number); (void)v4_manage_serve(&m, asker, V4_REQ_NODE_BORN); return depth(asker) ? pop(asker) : -2; } static void put_chars(unsigned place, v4_cell baddr, const char *s) { unsigned i; for (i = 0; s[i]; i++) { v4_cell a = baddr + (v4_cell)i; unsigned shift = 8u * (unsigned)(a & 3); v4_ucell c = (v4_ucell)N(place)->mem[a >> 2]; c = (c & ~((v4_ucell)0xFFu << shift)) | ((v4_ucell)(unsigned char)s[i] << shift); N(place)->mem[a >> 2] = (v4_cell)c; } } int main(void) { unsigned h, a, b, x; v4_cell r; printf("v4 manage tests: V4_CELL_BITS=%d\n", V4_CELL_BITS); v4_fabric_init(&f, places, NODES); memset(&m, 0, sizeof m); m.f = &f; m.reg = reg; m.reg_cap = NODES; m.born = born; m.gone = gone; m.open = open_; m.cell = cell_; m.line = line_; m.parity = parity_; h = (unsigned)born(0); m.hera = h; CHECK(v4_manage_serve(&m, h, -1) == 0 && v4_manage_serve(&m, h, 1) == 0 && v4_manage_serve(&m, h, -19) == 0, "a block request, a host's own word and a number past the last are not this file's"); CHECK(v4_manage_enter(&m, 10, h, 0) == 1, "Hera is entered by whoever made her, as v3's kernel enters Hera at boot"); CHECK(v4_manage_enter(&m, 10, h, 0) == 0 && v4_manage_enter(&m, 0, h, 0) == 0, "a number that is live, or 0, is not entered again"); CHECK(v4_manage_serve(&m, h, V4_REQ_NODE_ME) == 1 && pop(h) == (v4_cell)h, "NODE-ME is her place"); /* births */ r = birth(h, 11); CHECK(r >= 0 && place_of(h, 11) == r && child(h, 10, 0) == 11 && child(h, 10, 1) == 0, "Hera births 11: the registry has it, with her as its parent"); a = (unsigned)r; push(h, 11); CHECK(refused(h, V4_REQ_NODE_BORN) && depth(h) == 0 && used[2] == 0, "a birth of a number that is live is refused, and nothing is born"); push(h, 0); CHECK(refused(h, V4_REQ_NODE_BORN) && depth(h) == 0, "and so is a birth of 0"); r = birth(a, 12); CHECK(r >= 0 && child(a, 11, 0) == 12 && child(h, 10, 0) == 11 && child(h, 10, 1) == 0, "node 11 births 12, as v3: its parent is 11, not Hera"); b = (unsigned)r; x = (unsigned)v4_fabric_add(&f, &pool[7], PB); used[7] = 1; v4_node_error_attach(&pool[7].n, ERR); push(x, 13); CHECK(refused(x, V4_REQ_NODE_BORN) && depth(x) == 0, "a node the kernel has no record of may not birth"); /* wiring: its own ports, or those of a node it birthed */ push(a, (v4_cell)a); push(a, 3); push(a, (v4_cell)b); push(a, 2); CHECK(v4_manage_serve(&m, a, V4_REQ_NODE_WIRE) == 1 && pop(a) == -1, "node 11 wires itself to its child"); push(b, (v4_cell)a); push(b, 4); push(b, (v4_cell)h); push(b, 5); CHECK(refused(b, V4_REQ_NODE_WIRE) && depth(b) == 0, "node 12 may not wire its parent to Hera"); push(a, (v4_cell)h); push(a, 5); push(a, (v4_cell)a); push(a, 5); CHECK(refused(a, V4_REQ_NODE_WIRE) && depth(a) == 0, "node 11 may not wire Hera"); push(a, (v4_cell)h); push(a, 2); CHECK(refused(a, V4_REQ_NODE_UNWIRE) && depth(a) == 0, "nor cut a wire of hers"); push(a, (v4_cell)a); push(a, 3); CHECK(v4_manage_serve(&m, a, V4_REQ_NODE_UNWIRE) == 1 && depth(a) == 0, "and cuts its own"); /* parity: of a node it birthed */ push(a, (v4_cell)a); push(a, 11); CHECK(refused(a, V4_REQ_NODE_PARITY) && parity_count == 0, "node 11 may not report its own parity"); push(h, (v4_cell)a); push(h, 12); CHECK(refused(h, V4_REQ_NODE_PARITY) && parity_count == 0, "nor Hera that of her child under another number"); push(h, (v4_cell)a); push(h, 11); CHECK(v4_manage_serve(&m, h, V4_REQ_NODE_PARITY) == 1 && parity_count == 1 && parity_number == 11 && depth(h) == 0, "Hera reports her child's"); /* capsules: the asker's own */ put_chars(a, 400, "v4:ok"); push(a, 400); push(a, 5); CHECK(v4_manage_serve(&m, a, V4_REQ_CAPSULE_OPEN) == 1 && pop(a) == -1, "node 11 opens a capsule"); CHECK(v4_manage_serve(&m, a, V4_REQ_CAPSULE_CELL) == 1 && pop(a) == -1 && pop(a) == (v4_cell)a, "and reads its cells: the reader is the asker's"); push(a, 400); CHECK(v4_manage_serve(&m, a, V4_REQ_CAPSULE_LINE) == 1 && pop(a) == 2 && (N(a)->mem[100] & 0xFFFF) == ('O' | 'K' << 8), "and its lines"); /* sleep, wake and kill: Hera's alone, by number */ push(a, 12); CHECK(refused(a, V4_REQ_NODE_SLEEP) && !places[b].asleep && depth(a) == 0, "node 11 may not put 12 to sleep"); push(h, 12); CHECK(v4_manage_serve(&m, h, V4_REQ_NODE_SLEEP) == 1 && places[b].asleep, "Hera may, by its number"); push(a, 12); CHECK(refused(a, V4_REQ_NODE_WAKE) && places[b].asleep, "node 11 may not wake it"); push(h, 12); CHECK(v4_manage_serve(&m, h, V4_REQ_NODE_WAKE) == 1 && !places[b].asleep, "Hera may"); push(a, 12); CHECK(refused(a, V4_REQ_NODE_KILL) && v4_fabric_node_at(&f, b) != NULL, "node 11 may not kill its own child: KILL is Hera's, as v3"); { unsigned faults = N(h)->faults; push(h, 10); (void)v4_manage_serve(&m, h, V4_REQ_NODE_KILL); push(h, 99); (void)v4_manage_serve(&m, h, V4_REQ_NODE_KILL); CHECK(N(h)->faults == faults && gone_count == 0 && depth(h) == 0, "Hera's kill of herself, or of a number not live, does nothing"); } push(h, 12); CHECK(v4_manage_serve(&m, h, V4_REQ_NODE_KILL) == 1 && gone_count == 1 && place_of(h, 12) == -1 && child(h, 11, 0) == 0, "Hera kills 12, which she did not birth: the registry forgets it"); r = birth(a, 13); CHECK(r >= 0, "node 11 births 13"); push(h, 11); CHECK(v4_manage_serve(&m, h, V4_REQ_NODE_KILL) == 1 && place_of(h, 13) == r && child(h, 11, 0) == 13, "killing 11 does not kill its child, as in v3"); CHECK(place_of(h, 12) == -1 && place_of(h, 10) == (v4_cell)h, "NODE-PLACE of a number not live is -1"); printf(" %d checks, %d failures\n", checks, failures); return failures != 0; }