fix(v4.0.0): what a node passes on waits with it, offered -- a relay is not held by a stuck node

Found after the last commit: a message being passed on toward a node that
was stuck held the node passing it on in the wait, taking in what came
but serving none of it; text typed for a stuck node left Hera deaf.  And
a node that had done text for a sender that then stuck waited on it to
give its answer.

A node with nothing to do now deals first with what is for itself; then
offers at once the first message for each port and each refusal owed,
and sleeps in the wait.  What is taken is written and done with; the
rest stays with the messages waiting, in order.  How text ended is put
with the messages waiting, not written from (FINISH).  The stack is kept
as shallow between lines as it was.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
rajames
2026-10-07 23:51:30 -04:00
co-authored by Claude Opus 5.5
parent 482cc75faf
commit 1c720ab2c7
6 changed files with 201 additions and 71 deletions
+22 -3
View File
@@ -398,6 +398,13 @@ int main(void)
CHECK(is_empty(tell(10, "66 NO-ROUTE")) && waiting(hera) && waiting(mid) && waiting(far), "the way is forgotten, and all three are at rest");
}
/* ---- what a node passes on keeps its order, however long it must be kept (MESH.md 7c) ---- */
CHECK(is_empty(tell(11, "12 2 ROUTE")), "node 11 is told the way to node 12 is by node 10");
CHECK(is_empty(tell(12, ": SLOW 30000 0 DO LOOP ;")), "node 12 is given a word that takes a while");
CHECK(strcmp(tell(11, ": TRIO S\" SLOW 68 EMIT\" 12 SEND S\" 69 EMIT\" 12 SEND S\" 70 EMIT\" 12 SEND ; TRIO"), "DEF") == 0,
"it sends node 12 three texts, the first slow to do: node 10 keeps the other two until node 12 reads again, and they are done in order: \"%s\"", printed);
CHECK(is_empty(tell(11, "12 3 ROUTE")) && waiting(hera) && waiting(mid) && waiting(far) && pool[0].n.mem[MQ_COUNT] == 0, "the way is told again; all three are at rest, keeping nothing");
/* ---- a node that is stuck holds up no one (MESH.md 7c) ------------------- */
v4_fabric_gone_error(&f, V4_ERROR_NO_ONE);
@@ -406,7 +413,7 @@ int main(void)
/* a refusal owed to a stuck node whose number is the lower */
CHECK(is_empty(said(12, "NO-ROUTES 1 3 ROUTE 10 3 ROUTE 11 2 ROUTE")) && is_empty(said(11, "55 3 ROUTE")), "node 12 has no way for 55; node 11 is told the way to 55 is by node 12");
CHECK(strcmp(tell(11, ": C1 S\" 1 DROP\" 55 SEND BEGIN 0 UNTIL ; C1"), "(still running)") == 0, "node 11 sends 55 a message and is then stuck in a loop");
CHECK(strcmp(tell(11, ": C1 S\" 1 DROP\" 55 SEND S\" 1 DROP\" 12 SEND BEGIN 0 UNTIL ; C1"), "(still running)") == 0, "node 11 sends 55 a message, and node 12 text, and is then stuck in a loop");
CHECK(pool[2].n.mem[OWED_COUNT] == 1, "node 12 owes node 11 a refusal, and cannot give it: node 11 is not reading");
CHECK(strcmp(said(12, "7 8 * ."), "56 ") == 0 && printed_from == 12, "node 12 goes on doing what it is sent: \"%s\"", printed);
{
@@ -423,6 +430,18 @@ int main(void)
"node 10 sends 77 a message and waits: node 12, which still owes the stuck node, tells node 10 it was refused: \"%s\"", printed);
CHECK(is_empty(said(10, "77 NO-ROUTE")), "the way is forgotten");
/* a message passed on toward the stuck node waits with the node passing it on, which goes on serving */
{
v4_cell kept = pool[0].n.mem[MQ_COUNT];
CHECK(is_empty(said(11, "1 DROP")) && ended == 0 && pool[0].n.mem[MQ_COUNT] > kept && v4_node_in_wait(&pool[0].n),
"text from the console for the stuck node comes to node 10, which cannot hand it on: it keeps it, offered, and sleeps");
CHECK(strcmp(said(10, "1 2 + ."), "3 ") == 0 && strcmp(said(12, "7 8 * ."), "56 ") == 0 && printed_from == 12,
"node 10 goes on doing what it is sent, and passing on what is for others: \"%s\"", printed);
CHECK(pool[0].n.mem[MQ_COUNT] > kept && v4_node_in_wait(&pool[0].n), "still keeping what is for the stuck node");
}
/* the stuck node's own answer: node 12 did text node 11 sent before it stuck, and cannot give it the answer */
CHECK(pool[2].n.mem[MQ_COUNT] > 0 && v4_node_in_wait(&pool[2].n), "node 12 keeps the answer it has for node 11, offered, and sleeps");
/* a line that must begin a message to the stuck node waits, and the node takes in what comes */
CHECK(is_empty(said(10, ": TO11 S\" 1 DROP\" 11 SEND 65 EMIT ; TO11")) && ended == 0 && v4_node_in_wait(&pool[0].n) && !pool[0].n.asking,
"node 10 sends the stuck node a message: its line waits in the wait, not blocked writing");
@@ -447,8 +466,8 @@ int main(void)
CHECK(strstr(printed, "No one on that port") != NULL && strstr(printed, "A") == NULL && ended == 1, "the line ends in an error that says no one is there: \"%s\"", printed);
}
step_cap = 20000000;
CHECK(strcmp(said(12, "7 8 * ."), "56 ") == 0 && pool[2].n.mem[OWED_COUNT] == 0 && waiting(far) && !v4_node_in_wait(&pool[2].n) && waiting(hera),
"node 12 goes on, the refusal it owed node 11 let go: it owes nothing and is at rest");
CHECK(strcmp(said(12, "7 8 * ."), "56 ") == 0 && pool[2].n.mem[OWED_COUNT] == 0 && pool[2].n.mem[MQ_COUNT] == 0 && pool[0].n.mem[MQ_COUNT] == 0 && waiting(far) && !v4_node_in_wait(&pool[2].n) && waiting(hera),
"node 12 goes on; what it owed node 11 and kept for it, and what node 10 kept for it, are let go: they owe and keep nothing, and are at rest");
/* a refusal owed to a stuck node whose number is the higher */
step_cap = 3000000;
+2 -2
View File
@@ -63,7 +63,7 @@ static v4_heat h;
static v4_text tx;
static v4_cell w_key, w_key_end, w_fault, capsule_latest;
static v4_cell w_idle; /* (IDLE): where the node waits for a message */
static v4_cell w_idle_end; /* (PASS-ON): the word after it */
static v4_cell w_idle_end; /* (LINE): the word after it */
static char out[V4_CONSOLE_CAP + 1];
/* STORAGE (MESH.md section 8). The node asks its kernel for its blocks, and
@@ -871,7 +871,7 @@ int main(void)
printf(" capsule: %ld words\n", (long)v4_text_here(&tx) - 16);
if (failures) { printf(" %d checks, %d failures\n", checks, failures); return 1; }
w_idle = v4_text_word(&tx, "(IDLE)");
w_idle_end = v4_text_word(&tx, "(PASS-ON)"); /* the word after (IDLE) in quit.v4 */
w_idle_end = v4_text_word(&tx, "(LINE)"); /* the word after (IDLE) in quit.v4 */
w_key = v4_text_word(&tx, "KEY");
w_key_end = v4_text_word(&tx, "CR"); /* the word after KEY in core.v4 */
w_fault = v4_text_word(&tx, "(FAULTS)");
+9
View File
@@ -530,7 +530,16 @@ int main(void)
CHECK(strcmp(tell(13, ": SPIN BEGIN 0 UNTIL ; SPIN"), "(still running)") == 0, "node 13 is stuck in a loop");
CHECK(strcmp(tell(12, ": W13 13 AWAIT . ; W13"), "(still running)") == 0 && node_numbered(12)->mem[AWAIT_FROM] == 13 && waiting(12),
"node 12, which is not wired to it, waits for its answer");
/* while it is stuck it holds up no one (MESH.md 7c): text for it waits with Hera, who goes on */
(void)tell(13, "1 DROP");
CHECK(ended == 0 && v4_node_in_wait(node_numbered(10)) && !node_numbered(10)->asking && node_numbered(10)->mem[MQ_COUNT] > 0,
"text typed for the stuck node waits with Hera, offered: she sleeps in the wait, not blocked writing");
(void)tell(11, "11 100 * .");
CHECK(strcmp(printed, "1100 ") == 0 && ended == 1, "text typed for another node is passed on and done meanwhile: \"%s\"", printed);
(void)tell(10, "1 2 + .");
CHECK(strcmp(printed, "3 ") == 0 && ended == 1, "and so is text for Hera herself: \"%s\"", printed);
(void)tell(10, "13 KILL");
CHECK(node_numbered(10)->mem[MQ_COUNT] == 0, "killing it, she lets go what she kept for it");
CHECK(node_numbered(13) == NULL && strstr(printed, "Node gone") != NULL && node_numbered(12)->mem[AWAIT_FROM] == 0 && waiting(12),
"Hera kills node 13: node 12's wait ends, with an error that says the node is gone: \"%s\"", printed);
step_limit = 30000000ul;