feat(v4): the kernel keeps a registry of nodes and checks who may ask (MESH.md 9a.4)
Any node may birth and wire its own; only Hera sleeps, wakes and kills, by number. Requests have fixed numbers, -6 to -18. NODE-BORN takes the number; NODE-KILL a number. test_manage.c: 41 checks at both widths. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
bf97f339f6
commit
d154342314
@@ -162,23 +162,23 @@ static void host_gone(void *self, unsigned place, v4_fabric_node *was)
|
||||
(void)self; (void)place;
|
||||
pool_used[was - pool] = 0;
|
||||
}
|
||||
static int host_open(void *self, const char *name, unsigned len)
|
||||
static int host_open(void *self, unsigned asker, const char *name, unsigned len)
|
||||
{
|
||||
(void)self;
|
||||
(void)self; (void)asker;
|
||||
if (len == 17 && memcmp(name, "v4:nucleus-64.f18", 17) == 0) { hera_reads.is_f18 = 1; hera_reads.cells_at = 0; return 1; }
|
||||
if (len == 14 && memcmp(name, "v4:forth79.4th", 14) == 0) return read_file(&hera_reads, "forth79.4th");
|
||||
return 0;
|
||||
}
|
||||
static int host_cell(void *self, v4_cell *w)
|
||||
static int host_cell(void *self, unsigned asker, v4_cell *w)
|
||||
{
|
||||
(void)self;
|
||||
(void)self; (void)asker;
|
||||
if (!hera_reads.is_f18 || hera_reads.cells_at >= f18_count) return 0;
|
||||
*w = f18[hera_reads.cells_at++];
|
||||
return 1;
|
||||
}
|
||||
static int host_line(void *self, char *text)
|
||||
static int host_line(void *self, unsigned asker, char *text)
|
||||
{
|
||||
(void)self;
|
||||
(void)self; (void)asker;
|
||||
return hera_reads.is_f18 ? -1 : next_line(&hera_reads, text);
|
||||
}
|
||||
static void host_parity(void *self, unsigned place, v4_cell number, const v4_node *n)
|
||||
@@ -187,7 +187,8 @@ static void host_parity(void *self, unsigned place, v4_cell number, const v4_nod
|
||||
if (parity_count < 8) { parity_number[parity_count] = number; parity_hash[parity_count] = v4_image_dict_hash(n, &im); }
|
||||
parity_count++;
|
||||
}
|
||||
static const v4_manage manage = { &f, host_born, host_gone, host_open, host_cell, host_line, host_parity, 0 };
|
||||
static v4_manage manage;
|
||||
static v4_manage_entry registry[PLACES]; /* the kernel's registry of live nodes (MESH.md 9a.4) */
|
||||
|
||||
/* ---- the kernel: a device on Hera's port 0 ---- */
|
||||
#define FIRST_REQUEST 1 /* the number of NODE-ME on her */
|
||||
@@ -203,7 +204,7 @@ static int kernel_take(void *self, v4_cell value) /* a request */
|
||||
{
|
||||
(void)self;
|
||||
if (v4_blocks_serve(&node_blocks[0], &pool[0].n, value)) return 1; /* a block, as for any node; Hera is the first in the pool */
|
||||
if (value < FIRST_REQUEST || !v4_manage_serve(&manage, hera_place, (unsigned)(value - FIRST_REQUEST))) refused++;
|
||||
if (!v4_manage_serve(&manage, hera_place, value)) refused++;
|
||||
return 1;
|
||||
}
|
||||
static const v4_device kernel = { kernel_take, kernel_give, 0 };
|
||||
@@ -378,6 +379,9 @@ int main(void)
|
||||
printf(" not run at this width: the capsules are the 64-bit products'\n");
|
||||
return 0;
|
||||
}
|
||||
manage.f = &f; manage.reg = registry; manage.reg_cap = PLACES;
|
||||
manage.born = host_born; manage.gone = host_gone; manage.open = host_open;
|
||||
manage.cell = host_cell; manage.line = host_line; manage.parity = host_parity;
|
||||
CHECK(storage_made(), "the kernel's blocks are there before any node is");
|
||||
CHECK(rom_build_system(&nucleus, 0), "the host's machine assembles the system with its ROM");
|
||||
if (failures) { printf(" %d checks, %d failures\n", checks, failures); return 1; }
|
||||
@@ -396,6 +400,7 @@ int main(void)
|
||||
place = host_born(0);
|
||||
CHECK(place == 0, "a node is born");
|
||||
hera_place = (unsigned)place;
|
||||
manage.hera = hera_place;
|
||||
for (i = 0; i < 4096 && pool[0].n.mem[i] == 0; i++) { }
|
||||
CHECK(i == 4096 && pool[0].n.p == PORT + (v4_cell)V4_PORT_ANY, "with nothing in it, listening at its ports");
|
||||
CHECK(v4_fabric_wire_kernel(&f, hera_place, 0, &kernel) && v4_fabric_wire_device(&f, hera_place, 1, &console), "the kernel is on its port 0 and the console on its port 1");
|
||||
@@ -404,10 +409,11 @@ int main(void)
|
||||
"it takes in the nucleus through its port, a word at a time, and sleeps until a message comes: %u of %u cells", given_cells, f18_count);
|
||||
|
||||
CHECK(told(0, "10 (ME) ! 1 (CONSOLE) ! 1 1 ROUTE") && ended_from == 10, "it is told it is 10, and where the console is");
|
||||
CHECK(v4_manage_enter(&manage, 10, hera_place, 0), "the kernel enters her in its registry, as v3's enters Hera");
|
||||
{
|
||||
char text[600];
|
||||
size_t at = 0;
|
||||
for (i = 0; i < V4_MANAGE_WORDS; i++) at += (size_t)snprintf(text + at, sizeof text - at, "%u KERNEL-WORD %s ", FIRST_REQUEST + i, v4_manage_words[i]);
|
||||
for (i = 0; i < V4_MANAGE_WORDS; i++) at += (size_t)snprintf(text + at, sizeof text - at, "%d KERNEL-WORD %s ", (int)(V4_REQ_NODE_ME - (v4_cell)i), v4_manage_words[i]);
|
||||
CHECK(told(10, text), "and given the words that ask for nodes and capsules: \"%s\"", printed);
|
||||
}
|
||||
CHECK(strcmp(tell(10, "NODE-ME ."), "0 ") == 0 && refused == 0, "it asks where it is, and is answered");
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
/* 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 <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
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;
|
||||
}
|
||||
Reference in New Issue
Block a user