fix(v4.0.0): step 6d after its review -- no error in the middle of the messages waiting, no offer left standing
Ruled 2026-10-08: A, B, C, D and the guard for a half message. Each with a scene in test_host_mesh.c that failed first. A. The words of a message after its first are written from the passes over the messages waiting by (!W): an offer and a wait for that offer only (a new engine address). A reader removed in mid-message makes no error; the rest is let go and counted. Before, the error left the messages waiting in pieces and the node going round for ever. B. A store to the wait withdraws every offer; (GATE1) and (PAY-SET) do it first. Before, text begun after a node went round without sleeping could go to the port of an older offer: text for one node was done by another. C. What a finished text printed is put with the messages waiting, as how it ended is. With no room either is let go and counted; neither is begun as a message from (FINISH) any more, where a stuck node on the way held the whole node. D. Only what a sender is owed from an earlier text -- its output, how it ended -- holds that sender's next text back; a GONE does not; and what cannot be noted holds nothing. A node's own messages keep the order they were made in. - A message half taken in when its writer is removed is let go ((MQ-MEND)), not kept as if whole. - Smaller: a refused line no longer ends a waiting text 'Interrupted'; a node's own queued messages may be passed on by 16 nodes, not 15; no value is left on the stack when a port goes in mid-refusal. make -C v4 test and sanitize at both widths, hosted-check. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
380f1cc6ec
commit
2cf37aab4f
+12
-3
@@ -73,7 +73,7 @@
|
||||
#define OWED (BUF0_W - 112) /* those: for each, the node it is owed to and the node the refused message was for */
|
||||
#define ACL_HOOK (BUF0_W - 6) /* the xt of the access control recheck word, or 0 */
|
||||
#define LINE_STATUS (BUF0_W - 9) /* how the last line ended: 0 QUIT, 1 completed, 2 an error */
|
||||
#define PORT (BUF0_W - 144) /* the node's ports (node.h): V4_PORTS of them, then "any port", "which port", who writes, who reads, an offer for each port, and the wait: 2 * V4_PORTS + 5 cells, between the output buffer and A_FOUND. Port 0 is where its requests go. */
|
||||
#define PORT (BUF0_W - 144) /* the node's ports (node.h): V4_PORTS of them, then "any port", "which port", who writes, who reads, an offer for each port, and the two waits: 2 * V4_PORTS + 6 cells, between the output buffer and A_FOUND. Port 0 is where its requests go. */
|
||||
#define CONSOLE (BUF0_W - 16) /* the node what this one prints is sent to; 0: whoever sent the text being served */
|
||||
#define ROUTE_COUNT (BUF0_W - 17) /* how many entries the table of ways holds */
|
||||
#define ROUTE_DEFAULT (BUF0_W - 18) /* the port address for a node not in the table, or 0: there is none */
|
||||
@@ -87,8 +87,12 @@
|
||||
#define MQ_TAIL (BUF0_W - 36) /* where the next will go, */
|
||||
#define MQ_COUNT (BUF0_W - 37) /* and how many cells they take */
|
||||
#define AWAIT_FROM (BUF0_W - 39) /* the node whose word of how text ended is waited for */
|
||||
#define W_GONE (BUF0_W - 122) /* not 0: the port a message was being written to has nothing on it; its other words are let go */
|
||||
#define TAKING (BUF0_W - 121) /* not 0: a message is being taken in, and is only partly with the messages waiting; */
|
||||
#define T_TAIL (BUF0_W - 120) /* where they ended before it, */
|
||||
#define T_COUNT (BUF0_W - 119) /* and how many cells they took */
|
||||
#define GATE_WORD (BUF0_W - 32) /* the first word of a message that is being offered */
|
||||
#define PAY_K (BUF0_W - 123) /* for each port, which refusal owed is offered there, counted from 1; 0: none. V4_PORTS cells */
|
||||
#define PAY_K (BUF0_W - 31) /* for each port, which refusal owed is offered there, counted from 1; 0: none. V4_PORTS cells */
|
||||
#define GATE_PORT (BUF0_W - 38) /* the port address a message is about to be begun on */
|
||||
#define NEAR (BUF0_W - 64) /* for each port, the number of the node on the other end; 0: not told */
|
||||
#define MQ_HDR (BUF0_W - 56) /* a message being taken in: its seven words and the port it came on */
|
||||
@@ -127,7 +131,7 @@ typedef char host_map_ports_fit[(V4_PORTS <= 8u) ? 1 : -1];
|
||||
/* the messages waiting are above the dictionary */
|
||||
typedef char host_map_queue_fits[(MQ_W >= DICT_END_W) ? 1 : -1];
|
||||
/* the ports, with the offers and the wait after them, end below the cells that follow */
|
||||
typedef char host_map_port_block_fits[(PORT + 2 * (v4_cell)V4_PORTS + 5 <= PAY_K && PAY_K + (v4_cell)V4_PORTS <= BUF0_W - 115) ? 1 : -1];
|
||||
typedef char host_map_port_block_fits[(PORT + 2 * (v4_cell)V4_PORTS + 6 <= W_GONE && PAY_K + (v4_cell)V4_PORTS <= PRINT_TO) ? 1 : -1];
|
||||
|
||||
/* How many slots, from slot 0, a branch may sit in on a node this size: those
|
||||
* whose address field reaches every word of it. */
|
||||
@@ -205,6 +209,11 @@ static int host_load(v4_text *tx, v4_node *n, const char *const *files, unsigned
|
||||
v4_text_constant(tx, "PORTS-1", (v4_cell)V4_PORTS - 1);
|
||||
v4_text_constant(tx, "(OFFER)", PORT + (v4_cell)V4_PORT_OFFER);
|
||||
v4_text_constant(tx, "(WAIT)", PORT + (v4_cell)V4_PORT_WAIT);
|
||||
v4_text_constant(tx, "(WAIT1)", PORT + (v4_cell)V4_PORT_WAIT1);
|
||||
v4_text_constant(tx, "(W-GONE)", W_GONE);
|
||||
v4_text_constant(tx, "(TAKING)", TAKING);
|
||||
v4_text_constant(tx, "(T-TAIL)", T_TAIL);
|
||||
v4_text_constant(tx, "(T-COUNT)", T_COUNT);
|
||||
v4_text_constant(tx, "(AWAIT-FROM)", AWAIT_FROM);
|
||||
v4_text_constant(tx, "(MQ)", MQ_W);
|
||||
v4_text_constant(tx, "(MQ-END)", MQ_W + MQ_CELLS);
|
||||
|
||||
@@ -60,6 +60,12 @@ static void wait_into(v4_cell at)
|
||||
LIT(PB + (v4_cell)V4_PORT_WAIT); O(BANG_B); O(FETCH_B); LIT(at); O(BANG_A); O(STORE_A);
|
||||
LIT(PB + (v4_cell)V4_PORTS + 1); O(BANG_B); O(FETCH_B); LIT(at + 1); O(BANG_A); O(STORE_A);
|
||||
}
|
||||
/* the wait for an offer only */
|
||||
static void wait1_into(v4_cell at)
|
||||
{
|
||||
LIT(PB + (v4_cell)V4_PORT_WAIT1); O(BANG_B); O(FETCH_B); LIT(at); O(BANG_A); O(STORE_A);
|
||||
LIT(PB + (v4_cell)V4_PORTS + 1); O(BANG_B); O(FETCH_B); LIT(at + 1); O(BANG_A); O(STORE_A);
|
||||
}
|
||||
static void done_mark(void) { LIT(1); LIT(OUT + 2); O(BANG_A); O(STORE_A); }
|
||||
static void count_for_ever(void)
|
||||
{
|
||||
@@ -498,6 +504,34 @@ int main(void)
|
||||
CHECK(!v4_node_in_wait(&ND(a)->n) && ND(a)->n.faults == 0 && ND(a)->n.offers == 0 && ND(a)->n.mem[OUT] == 0 && ND(a)->n.mem[OUT + 1] == 2 * (v4_cell)V4_PORTS + 5 && ND(a)->n.mem[OUT + 2] == 1,
|
||||
"one that can take an error is woken and told there is nothing on port 5: no error is raised, and its offer is withdrawn: %ld", (long)ND(a)->n.mem[OUT + 1]);
|
||||
|
||||
/* offers are withdrawn by a store to the wait (found by the review of step 6d) */
|
||||
fresh();
|
||||
a = loaded(0); offer(0, 7); LIT(0); LIT(PB + (v4_cell)V4_PORT_WAIT); O(BANG_A); O(STORE_A); done_mark(); wait_for_ever();
|
||||
for (i = 0; i < 50; i++) (void)v4_fabric_step(&f);
|
||||
CHECK(v4_asm_ok(&as) && ND(a)->n.mem[OUT + 2] == 1 && ND(a)->n.offers == 0 && ND(a)->n.faults == 0, "a node that offers and then stores to the wait has no offer left");
|
||||
|
||||
/* the wait for an offer only: a word that comes for the node does not end it */
|
||||
fresh();
|
||||
a = loaded(0); offer(0, 7); wait1_into(OUT); done_mark(); wait_for_ever();
|
||||
b = loaded(1); count_for_ever();
|
||||
c = loaded(2); LIT(PB + 0); O(BANG_B); LIT(9); O(STORE_B); wait_for_ever();
|
||||
CHECK(v4_asm_ok(&as) && v4_fabric_wire(&f, a, 0, b, 3) && v4_fabric_wire(&f, c, 0, a, 2), "a node waits for its offer only, to a node that never reads, while a third writes to it");
|
||||
for (i = 0; i < 200; i++) (void)v4_fabric_step(&f);
|
||||
CHECK(v4_node_in_wait(&ND(a)->n) && ND(a)->n.mem[OUT + 2] == 0 && ND(c)->n.asking && (ND(c)->n.readers & 1u) == 0, "it goes on waiting; the writer is not served, and does not see it as reading");
|
||||
CHECK(v4_fabric_remove(&f, b) != 0, "the node offered to is removed");
|
||||
v4_fabric_gone_error(&f, 18);
|
||||
v4_node_error_attach(&ND(a)->n, OUT + 20);
|
||||
v4_node_fault_attach(&ND(a)->n, 900);
|
||||
CHECK(v4_fabric_wire_device(&f, a, 7, &sink), "(the port it ends by reading has a device that gives nothing)");
|
||||
for (i = 0; i < 100 && ND(a)->n.mem[OUT + 2] == 0; i++) (void)v4_fabric_step(&f);
|
||||
CHECK(ND(a)->n.mem[OUT + 1] == 2 * (v4_cell)V4_PORTS + 0 && ND(a)->n.faults == 0 && ND(a)->n.offers == 0 && ND(c)->n.asking, "it is told there is nothing on that port; no error is raised; the writer still waits");
|
||||
fresh();
|
||||
a = loaded(0); offer(0, 7); wait1_into(OUT); done_mark(); wait_for_ever();
|
||||
b = loaded(1); LIT(PB + 3); O(BANG_B); O(FETCH_B); LIT(OUT); O(BANG_A); O(STORE_A); wait_for_ever();
|
||||
CHECK(v4_asm_ok(&as) && v4_fabric_wire(&f, a, 0, b, 3), "the same, to a node that reads");
|
||||
(void)settle(300);
|
||||
CHECK(ND(b)->n.mem[OUT] == 7 && ND(a)->n.mem[OUT + 1] == (v4_cell)V4_PORTS + 0 && ND(a)->n.mem[OUT + 2] == 1, "its offer is taken, and it is told");
|
||||
|
||||
/* a host with one node, whose devices always take */
|
||||
fresh();
|
||||
a = loaded(0); offer(1, 21); offer(3, 23); wait_into(OUT); done_mark(); wait_for_ever();
|
||||
|
||||
@@ -121,6 +121,28 @@ static int waiting(unsigned id)
|
||||
return n->reading && !n->given && n->read_port == V4_PORT_ANY;
|
||||
}
|
||||
|
||||
/* Three nodes in a row once more, 10 at the console, 11, 12: a new fabric, each
|
||||
* told who it is and the way to the others. For the scenes at the end. */
|
||||
static unsigned starforth_node(unsigned k);
|
||||
static void row_again(unsigned *hera, unsigned *mid, unsigned *far)
|
||||
{
|
||||
unsigned i;
|
||||
v4_fabric_init(&f, places, 3);
|
||||
v4_fabric_gone_error(&f, V4_ERROR_NO_ONE);
|
||||
*hera = starforth_node(0); *mid = starforth_node(1); *far = starforth_node(2);
|
||||
(void)v4_fabric_wire_device(&f, *hera, 1, &console);
|
||||
(void)v4_fabric_wire(&f, *hera, 2, *mid, 2); (void)v4_fabric_wire(&f, *mid, 3, *far, 2);
|
||||
going.count = 0; going_at = 0;
|
||||
step_cap = 20000000;
|
||||
for (i = 0; i < 20; i++) (void)v4_fabric_step(&f);
|
||||
(void)tell(0, "10 (ME) ! 1 (CONSOLE) ! 1 1 ROUTE 0 2 ROUTE");
|
||||
(void)tell(0, "11 (ME) ! 1 (CONSOLE) ! 2 DEFAULT-ROUTE 0 3 ROUTE");
|
||||
(void)tell(0, "12 (ME) ! 1 (CONSOLE) ! 2 DEFAULT-ROUTE");
|
||||
(void)tell(10, "11 2 ROUTE 12 2 ROUTE 11 2 NEIGHBOUR");
|
||||
(void)tell(11, "12 3 ROUTE 10 2 NEIGHBOUR");
|
||||
(void)tell(12, "11 2 NEIGHBOUR");
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
unsigned hera, mid, far, i;
|
||||
@@ -487,6 +509,92 @@ int main(void)
|
||||
CHECK(v4_fabric_remove(&f, far) == &pool[2], "node 12 is removed");
|
||||
CHECK(strcmp(said(10, "1 2 + ."), "3 ") == 0 && pool[0].n.mem[OWED_COUNT] == 0 && waiting(hera) && !v4_node_in_wait(&pool[0].n), "node 10 lets the refusal go and is at rest: \"%s\"", printed);
|
||||
|
||||
/* ==== what the review of step 6d found: each scene on a row of three, begun again ==== */
|
||||
|
||||
/* a node is removed while a message is being passed on to it */
|
||||
{
|
||||
static char line[240];
|
||||
unsigned steps = 0, k;
|
||||
unsigned depth;
|
||||
row_again(&hera, &mid, &far);
|
||||
CHECK(strcmp(tell(12, "7 8 * ."), "56 ") == 0, "three nodes in a row again");
|
||||
depth = pool[1].n.ds.depth;
|
||||
memset(line, ' ', 200); memcpy(line, "1 DROP", 6); line[200] = 0;
|
||||
printed_len = 0; printed[0] = 0; ended = 0;
|
||||
(void)v4_message_text(&going, 12, CONSOLE_ID, V4_MSG_TEXT, line, 200); going_at = 0;
|
||||
while (steps < 2000000 && !(pool[1].n.mem[MQ_COUNT] > 0 && pool[1].n.mem[MQ_COUNT] < 40 && pool[2].n.reading && pool[2].n.read_port == 2)) { (void)v4_fabric_step(&f); steps++; }
|
||||
CHECK(steps < 2000000, "long text for node 12 is on its way through node 11, part written: %ld cells of it still with node 11", (long)pool[1].n.mem[MQ_COUNT]);
|
||||
CHECK(v4_fabric_remove(&f, far) == &pool[2], "node 12 is removed");
|
||||
for (k = 0; k < 3000000 && v4_fabric_step(&f) != 0; k++) { }
|
||||
CHECK(k < 3000000 && pool[1].n.mem[MQ_COUNT] == 0 && waiting(mid) && pool[1].n.ds.depth == depth, "node 11 comes to rest: it has let the rest go, keeps nothing, and its stack is as it was (%ld cells kept)", (long)pool[1].n.mem[MQ_COUNT]);
|
||||
CHECK(strcmp(tell(11, "20 22 + ."), "42 ") == 0 && strcmp(tell(10, "1 2 + ."), "3 ") == 0, "and it and node 10 go on");
|
||||
}
|
||||
|
||||
/* a node is removed while it is writing a message to its neighbour: the half that came is not kept */
|
||||
{
|
||||
static char line[240];
|
||||
unsigned steps = 0, k;
|
||||
row_again(&hera, &mid, &far);
|
||||
memset(line, ' ', 200); memcpy(line, "1 DROP", 6); line[200] = 0;
|
||||
printed_len = 0; printed[0] = 0; ended = 0;
|
||||
(void)v4_message_text(&going, 12, CONSOLE_ID, V4_MSG_TEXT, line, 200); going_at = 0;
|
||||
while (steps < 2000000 && !(pool[2].n.mem[MQ_COUNT] > 8 && pool[2].n.reading && pool[2].n.read_port == 2)) { (void)v4_fabric_step(&f); steps++; }
|
||||
CHECK(steps < 2000000, "node 12 has taken in part of a long text from node 11: %ld cells", (long)pool[2].n.mem[MQ_COUNT]);
|
||||
CHECK(v4_fabric_remove(&f, mid) == &pool[1], "node 11 is removed");
|
||||
for (k = 0; k < 3000000 && v4_fabric_step(&f) != 0; k++) { }
|
||||
CHECK(k < 3000000 && pool[2].n.mem[MQ_COUNT] == 0 && waiting(far), "node 12 comes to rest, and has not kept the half: %ld cells", (long)pool[2].n.mem[MQ_COUNT]);
|
||||
CHECK(v4_fabric_wire(&f, hera, 3, far, 3) && is_empty(tell(10, "12 NO-ROUTE 12 3 ROUTE")), "node 12 is wired to node 10 instead");
|
||||
(void)tell(12, "NO-ROUTES 1 3 ROUTE 10 3 ROUTE");
|
||||
CHECK(strcmp(tell(12, "7 8 * ."), "56 ") == 0, "and does what it is sent");
|
||||
}
|
||||
|
||||
/* what a finished text printed, with the way to the console through a stuck node */
|
||||
row_again(&hera, &mid, &far);
|
||||
CHECK(v4_fabric_wire(&f, hera, 3, far, 3) && is_empty(tell(10, "NO-ROUTES 1 1 ROUTE 11 2 ROUTE 12 3 ROUTE")) && strcmp(tell(12, "3 4 + ."), "7 ") == 0,
|
||||
"node 12 is reached straight from node 10; what it prints still goes back by node 11");
|
||||
step_cap = 400000;
|
||||
CHECK(strcmp(tell(11, ": ST BEGIN 0 UNTIL ; ST"), "(still running)") == 0, "node 11 is stuck in a loop");
|
||||
(void)said(10, ": P S\" 65 EMIT\" 12 SEND ; P");
|
||||
CHECK(v4_node_in_wait(&pool[2].n) && pool[2].n.mem[MQ_COUNT] > 0, "node 10 sends node 12 text that prints: what it printed, and how it ended, wait with node 12, offered, and it sleeps");
|
||||
(void)said(10, ": Q S\" 9 (REFUSED) !\" 12 SEND ; Q");
|
||||
(void)said(12, "8 (REFUSED) !");
|
||||
CHECK(pool[2].n.mem[REFUSED] == 0 && v4_node_in_wait(&pool[2].n),
|
||||
"the next text from node 10, and text typed at the console for it, are kept back: what it owes each from the last has not gone (MESH.md 7c.3). It sleeps in the wait");
|
||||
{ v4_cell kept = pool[2].n.mem[MQ_COUNT];
|
||||
(void)said(10, ": R S\" 7 (REFUSED) !\" 12 SEND ; R");
|
||||
CHECK(pool[2].n.mem[MQ_COUNT] > kept && v4_node_in_wait(&pool[2].n), "it is not held: it takes in what it is sent, and sleeps again"); }
|
||||
CHECK(v4_fabric_remove(&f, mid) == &pool[1], "node 11 is removed");
|
||||
{ unsigned k; for (k = 0; k < 3000000 && v4_fabric_step(&f) != 0; k++) { }
|
||||
CHECK(k < 3000000 && pool[2].n.mem[REFUSED] == 7 && pool[2].n.mem[MQ_COUNT] == 0 && waiting(far), "what could not go is let go, the texts kept back are done in the order they came, and node 12 is at rest: %ld", (long)pool[2].n.mem[REFUSED]); }
|
||||
|
||||
/* offers left standing when a node goes round to begin text */
|
||||
row_again(&hera, &mid, &far);
|
||||
CHECK(v4_fabric_wire(&f, hera, 3, far, 3) && v4_fabric_wire(&f, hera, 4, far, 4), "nodes 10 and 12 are wired to each other twice over");
|
||||
(void)tell(10, "NO-ROUTES 1 1 ROUTE 11 2 ROUTE 12 3 ROUTE 77 3 ROUTE");
|
||||
(void)tell(10, ": SLOW 400000 0 DO LOOP ; : Z S\" 1 DROP\" 77 SEND SLOW ;");
|
||||
(void)tell(12, ": FWD99 S\" 67 EMIT\" 99 SEND ;");
|
||||
(void)tell(12, "NO-ROUTES 10 3 ROUTE 11 2 ROUTE 1 4 ROUTE 99 4 ROUTE");
|
||||
CHECK(strcmp(tell(12, "3 4 + ."), "7 ") == 0, "node 12's way to the console, and to 99, is by its other wire to node 10");
|
||||
step_cap = 300000;
|
||||
CHECK(strcmp(tell(11, ": C S\" 1 DROP\" 12 SEND S\" FWD99\" 12 SEND BEGIN 0 UNTIL ; C"), "(still running)") == 0, "node 11 sends node 12 two texts and is stuck");
|
||||
step_cap = 30000;
|
||||
(void)said(10, "Z");
|
||||
CHECK(pool[2].n.mem[OWED_COUNT] == 1, "node 10 sends 77 a message by node 12, which has no way, and is then busy: node 12 owes it a refusal, offered on its first wire");
|
||||
CHECK(v4_fabric_remove(&f, mid) == &pool[1], "node 11 is removed: node 12 lets its answer go, and begins the text it kept back");
|
||||
printed_len = 0; printed[0] = 0; printed_from = -1;
|
||||
{ unsigned k; for (k = 0; k < 20000000 && v4_fabric_step(&f) != 0; k++) { }
|
||||
CHECK(k < 20000000, "all come to rest"); }
|
||||
CHECK(strchr(printed, 'C') == NULL, "the text node 12 sent to 99 was not done by node 10: \"%s\" from %ld", printed, (long)printed_from);
|
||||
CHECK(pool[0].n.mem[REFUSED] == 1 && pool[2].n.mem[OWED_COUNT] == 0, "node 10 was told its message to 77 was refused: %ld", (long)pool[0].n.mem[REFUSED]);
|
||||
|
||||
/* many of a node's own messages waiting for a stuck node */
|
||||
row_again(&hera, &mid, &far);
|
||||
step_cap = 300000;
|
||||
CHECK(strcmp(tell(11, ": ST BEGIN 0 UNTIL ; ST"), "(still running)") == 0, "node 11 is stuck in a loop");
|
||||
{ int k, good = 0;
|
||||
for (k = 1; k <= 12; k++) { (void)said(10, "12 11 GONE"); if (strcmp(said(10, "1 2 + ."), "3 ") == 0) good++; }
|
||||
CHECK(good == 12 && pool[0].n.mem[MQ_COUNT] == 108, "node 10 is told twelve times to tell it a node is gone, and after each still does what is typed: %d of 12, %ld cells kept", good, (long)pool[0].n.mem[MQ_COUNT]); }
|
||||
|
||||
printf(" %d checks, %d failures\n", checks, failures);
|
||||
return failures != 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user