revert(v4.0.0): step 6d, the wait, is backed out -- two reviews found it unsound
Ruled 2026-10-08. The engine, the nucleus and their tests are as they
were at dfabfa46, before step 6d; the code of the wait and its tests are
in history at 075385ab..2cf37aab. The limit of MESH.md 7b.7 stands: a
node that is stuck can hold up its neighbours until Hera kills it.
MESH.md 7c is kept as the record of the design as approved, and 7c.6 says
what was built, what the two reviews found, the cause the findings share
-- a node's message machinery runs on the stacks its text is using, and a
fault abandons whatever was in progress -- and what a second attempt
must settle before anything is built.
Kept from it, in hosted-check: a line that leaves 29 values on the stack,
and WORDS with 26 values on it. Both hung the products at some commit of
the wait.
make -C v4 test and sanitize at both widths, hosted-check, lint; three
bare-metal boots typing both. POST 538 of 538, word_count=317,
dict_hash=0xc0769523a47b7dc3 on all six: the hash of step 6c's code.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
10f5ffd00b
commit
6015c87157
+33
-206
@@ -71,7 +71,8 @@ static int console_take(void *self, v4_cell value)
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
static const v4_device console = { console_take, console_give, 0 };
|
||||
static int console_pending(void *self) { (void)self; return going_at < going.count; }
|
||||
static const v4_device console = { console_take, console_give, 0, console_pending };
|
||||
|
||||
static int is_empty(const char *s) { return s[0] == 0; }
|
||||
|
||||
@@ -111,38 +112,12 @@ static unsigned starforth_node(unsigned k)
|
||||
return (unsigned)id;
|
||||
}
|
||||
|
||||
/* The same, answering with what has been printed: for when a node is
|
||||
* spinning and the fabric never comes to rest. */
|
||||
static const char *said(v4_cell node, const char *text) { (void)tell(node, text); return printed; }
|
||||
|
||||
static int waiting(unsigned id)
|
||||
{
|
||||
const v4_node *n = &v4_fabric_node_at(&f, id)->n;
|
||||
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;
|
||||
@@ -327,12 +302,12 @@ int main(void)
|
||||
|
||||
/* no room: a node that is waiting keeps what it is sent, and has room for only so much */
|
||||
{
|
||||
v4_cell lost = pool[1].n.mem[LOST], refused = pool[0].n.mem[REFUSED], got, not_taken, told_of;
|
||||
v4_cell lost = pool[1].n.mem[LOST], refused = pool[0].n.mem[REFUSED], got, not_taken;
|
||||
CHECK(is_empty(tell(11, "0 GOT !")) && is_empty(tell(11, "12 AWAIT .")) && ended == 0 && pool[1].n.mem[AWAIT_FROM] == 12, "node 11 waits for an answer from 12, which is not going to send one");
|
||||
CHECK(is_empty(tell(10, ": F 49 0 DO S\" HIT\" 11 SEND LOOP ; F")) && ended == 1 && ended_from == 10, "node 10 sends it 49 messages meanwhile");
|
||||
CHECK(pool[1].n.mem[MQ_COUNT] > 300 && pool[1].n.mem[MQ_COUNT] <= MQ_CELLS - 36, "node 11 has kept what it has room for, and left the room that is kept for refusals: %ld cells", (long)pool[1].n.mem[MQ_COUNT]);
|
||||
CHECK(pool[1].n.mem[OWED_COUNT] == 0 && pool[0].n.mem[REFUSED] >= refused + 8, "it owed a refusal for each of the rest, as many as it could owe at once, and paid them while it waited: node 10 was told of %ld",
|
||||
(long)(pool[0].n.mem[REFUSED] - refused));
|
||||
CHECK(pool[1].n.mem[OWED_COUNT] == 8, "it owes a refusal for each of the rest, as many as it can owe: %ld", (long)pool[1].n.mem[OWED_COUNT]);
|
||||
CHECK(pool[0].n.mem[REFUSED] == refused, "and has sent none yet: it is still waiting");
|
||||
|
||||
/* one from its centre ends the wait */
|
||||
CHECK(is_empty(tell(10, "12 NO-ROUTE")), "node 10 forgets the way to 12");
|
||||
@@ -341,10 +316,9 @@ int main(void)
|
||||
got = strtol(tell(11, "GOT @ ."), NULL, 10);
|
||||
not_taken = 49 - got;
|
||||
CHECK(got >= 30 && not_taken > 8, "it then did every message it had kept: %ld of the 49", (long)got);
|
||||
told_of = pool[0].n.mem[REFUSED] - refused;
|
||||
CHECK(told_of >= 8 && told_of <= not_taken, "node 10 was told of %ld of them", (long)told_of);
|
||||
CHECK(pool[1].n.mem[LOST] == lost + not_taken + (not_taken - told_of), "it counted each message it did not take, and each refusal it could not owe: %ld for %ld not taken, %ld told",
|
||||
(long)(pool[1].n.mem[LOST] - lost), (long)not_taken, (long)told_of);
|
||||
CHECK(pool[0].n.mem[REFUSED] == refused + 8, "and sent the eight refusals it owed: node 10 was told of %ld", (long)(pool[0].n.mem[REFUSED] - refused));
|
||||
CHECK(pool[1].n.mem[LOST] == lost + not_taken + (not_taken - 8), "it counted each message it did not take, and each refusal it could not owe: %ld for %ld not taken",
|
||||
(long)(pool[1].n.mem[LOST] - lost), (long)not_taken);
|
||||
/* the way to 12 is forgotten: what is sent there now is refused */
|
||||
refused = pool[1].n.mem[REFUSED];
|
||||
CHECK(is_empty(tell(11, ": P12 S\" 1 DROP\" 12 SEND ; P12")) && pool[1].n.mem[REFUSED] == refused + 1, "having forgotten the way to 12, what node 11 sends there goes toward its centre, which has no way either, and is refused");
|
||||
@@ -352,11 +326,11 @@ int main(void)
|
||||
}
|
||||
/* a line from the console breaks a wait (MESH.md 7b.6) */
|
||||
CHECK(is_empty(tell(10, "12 AWAIT .")) && ended == 0 && pool[0].n.mem[AWAIT_FROM] == 12 && waiting(hera), "node 10 waits for an answer from 12, which is not going to send one");
|
||||
CHECK(strcmp(tell(11, "20 22 + ."), "42 ") == 0 && ended == 1 && pool[0].n.mem[AWAIT_FROM] == 12, "text from the console for node 11 comes to node 10 on its way: node 10 passes it on, and goes on waiting: \"%s\"", printed);
|
||||
CHECK(is_empty(tell(11, "20 22 + .")) && ended == 0 && pool[0].n.mem[AWAIT_FROM] == 12, "text from the console for node 11 comes to node 10 on its way: node 10 keeps it, and goes on waiting");
|
||||
{
|
||||
const char *r = tell(10, "65 EMIT");
|
||||
CHECK(strstr(r, "Interrupted\n") != NULL && pool[0].n.mem[AWAIT_FROM] == 0, "text from the console for node 10 itself ends its wait, with an error that says so: \"%s\"", r);
|
||||
CHECK(strstr(r, "A") != NULL && ended == 2, "the line is then done as usual: \"%s\", %d ended", r, ended);
|
||||
CHECK(strstr(r, "A") != NULL && strstr(r, "42 ") != NULL && ended == 3, "the line is then done as usual, and so is what was kept for node 11: \"%s\", %d ended", r, ended);
|
||||
CHECK(strstr(r, "Interrupted\n") < strstr(r, "A"), "the wait ends before the line that ended it is done");
|
||||
}
|
||||
CHECK(strcmp(tell(10, "1 2 + ."), "3 ") == 0 && waiting(hera) && waiting(mid) && waiting(far), "and all three go on as before");
|
||||
@@ -390,10 +364,9 @@ int main(void)
|
||||
{
|
||||
CHECK(is_empty(tell(11, "0 GOT !")) && is_empty(tell(11, "12 AWAIT .")) && pool[1].n.mem[AWAIT_FROM] == 12, "node 11 waits on 12 again");
|
||||
CHECK(is_empty(tell(10, ": F40 40 0 DO S\" HIT\" 11 SEND LOOP ; F40")) && pool[1].n.mem[OWED_COUNT] == 0, "node 10 sends it as many messages as it has room for");
|
||||
/* node 11 does what it has kept before it pays what it owes (7b.3): forty answers come first */
|
||||
(void)tell(10, ": ONE S\" HIT\" 11 SEND 12 NO-ROUTE 12 11 GONE 41 0 DO 11 AWAIT DROP LOOP ; ONE");
|
||||
(void)tell(10, ": ONE S\" HIT\" 11 SEND 12 NO-ROUTE 12 11 GONE 11 AWAIT . ; ONE");
|
||||
CHECK(strstr(printed, "Message refused") != NULL && pool[0].n.mem[AWAIT_FROM] == 0,
|
||||
"it sends one more, for which there is no room, ends node 11's wait, and waits for node 11's answers: after the forty, its wait ends, the message refused: \"%s\"", printed);
|
||||
"it sends one more, for which there is no room, ends node 11's wait, and waits for node 11's answer: its own wait ends, the message refused: \"%s\"", printed);
|
||||
CHECK(strtol(tell(11, "GOT @ ."), NULL, 10) == 40 && is_empty(tell(10, "12 3 ROUTE")) && is_empty(tell(11, "12 3 ROUTE")), "node 11 did the forty it had kept; the ways are told again");
|
||||
}
|
||||
|
||||
@@ -420,180 +393,34 @@ 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);
|
||||
|
||||
/* From here a node is spinning, so the fabric never comes to rest: said()
|
||||
* is tell(), answering with what was printed by the time it gave up. */
|
||||
|
||||
/* 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 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");
|
||||
/* a node that owes a refusal to a node that is stuck is not stuck itself */
|
||||
CHECK(is_empty(tell(12, "NO-ROUTES 1 3 ROUTE 10 3 ROUTE 11 2 ROUTE")) && is_empty(tell(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(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);
|
||||
{
|
||||
v4_uheat_t clock = pool[2].es.anticlock;
|
||||
unsigned k;
|
||||
for (k = 0; k < 1000; k++) (void)v4_fabric_step(&f);
|
||||
CHECK(v4_node_in_wait(&pool[2].n) && pool[2].es.anticlock == clock && pool[2].n.mem[OWED_COUNT] == 1, "between times it sleeps in the wait, still owing, and executes nothing");
|
||||
}
|
||||
CHECK(strcmp(said(10, ": X12 S\" 66 EMIT\" 12 SEND ; X12"), "B") == 0 && printed_from == 12, "another node writes to it and is answered: \"%s\"", printed);
|
||||
(void)tell(12, "7 8 * .");
|
||||
CHECK(strcmp(printed, "56 ") == 0 && printed_from == 12, "node 12 goes on doing what it is sent: \"%s\"", printed);
|
||||
|
||||
/* two refusals owed: one to the stuck node, one to a node that waits for it */
|
||||
CHECK(is_empty(said(10, "77 3 ROUTE")), "node 10 is told the way to 77 is by node 12, which has none");
|
||||
CHECK(strstr(said(10, ": W77B S\" 1 DROP\" 77 SEND 77 AWAIT . ; W77B"), "Message refused") != NULL && pool[0].n.mem[AWAIT_FROM] == 0 && pool[2].n.mem[OWED_COUNT] == 1,
|
||||
"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");
|
||||
{
|
||||
v4_cell kept = pool[0].n.mem[MQ_COUNT];
|
||||
CHECK(is_empty(said(12, "7 8 * .")) && ended == 0 && pool[0].n.mem[MQ_COUNT] > kept && v4_node_in_wait(&pool[0].n),
|
||||
"text from the console for node 12 comes to node 10: it keeps it, and its line goes on waiting");
|
||||
}
|
||||
{
|
||||
const char *r = said(10, "66 EMIT");
|
||||
CHECK(strstr(r, "Interrupted\n") != NULL && strstr(r, "A") == NULL && strstr(r, "B") != NULL && strstr(r, "Interrupted\n") < strstr(r, "B"),
|
||||
"text from the console for node 10 itself ends the line that waited, with an error that says so, and is then done: \"%s\"", r);
|
||||
CHECK(strstr(r, "56 ") != NULL && ended == 3, "and what it kept for node 12 is passed on and done: \"%s\", %d ended", r, ended);
|
||||
}
|
||||
|
||||
/* the stuck node is removed while a line waits to begin a message to it */
|
||||
CHECK(is_empty(said(10, "TO11")) && ended == 0 && v4_node_in_wait(&pool[0].n), "node 10's line waits on the stuck node again");
|
||||
/* a node that is waiting to write to a node finds out when that node is removed */
|
||||
(void)tell(12, ": TO11 S\" 1 DROP\" 11 SEND 65 EMIT ; TO11");
|
||||
CHECK(ended == 0, "node 12 sends node 11 a message: node 11 is not reading, and node 12 waits to write to it");
|
||||
CHECK(v4_fabric_remove(&f, mid) == &pool[1], "node 11 is removed");
|
||||
{
|
||||
unsigned k;
|
||||
for (k = 0; k < 200000 && ended == 0; k++) (void)v4_fabric_step(&f);
|
||||
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);
|
||||
}
|
||||
(void)tell(10, "1 DROP");
|
||||
CHECK(strstr(printed, "No one on that port") != NULL && strstr(printed, "A") == NULL, "node 12's 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 && 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");
|
||||
CHECK(strcmp(tell(12, "7 8 * ."), "56 ") == 0 && pool[2].n.mem[OWED_COUNT] == 0 && waiting(far) && waiting(hera),
|
||||
"it goes on, the refusal it owed node 11 let go: it owes nothing and is at rest");
|
||||
|
||||
/* a refusal owed to a stuck node whose number is the higher */
|
||||
/* THE LIMIT (MESH.md 7b.7): a refusal owed to a neighbour whose number is the higher is written
|
||||
* without looking, as any message to it is, so a node that is stuck holds up a neighbour that owes
|
||||
* it one -- until it is removed */
|
||||
step_cap = 3000000;
|
||||
CHECK(is_empty(said(12, "55 3 ROUTE")), "node 12 is told the way to 55 is by node 10, which has none");
|
||||
CHECK(strcmp(tell(12, ": C2 S\" 1 DROP\" 55 SEND BEGIN 0 UNTIL ; C2"), "(still running)") == 0 && pool[0].n.mem[OWED_COUNT] == 1 && !pool[0].n.asking,
|
||||
"node 12 sends 55 a message and is then stuck in a loop: node 10 owes it a refusal, and is not blocked writing it");
|
||||
step_cap = 20000000;
|
||||
CHECK(strcmp(said(10, "1 2 + ."), "3 ") == 0 && pool[0].n.mem[OWED_COUNT] == 1, "node 10 goes on doing what it is sent, still owing: \"%s\"", printed);
|
||||
{
|
||||
v4_uheat_t clock;
|
||||
unsigned k;
|
||||
for (k = 0; k < 1000; k++) (void)v4_fabric_step(&f);
|
||||
clock = pool[0].es.anticlock;
|
||||
for (k = 0; k < 1000; k++) (void)v4_fabric_step(&f);
|
||||
CHECK(v4_node_in_wait(&pool[0].n) && pool[0].es.anticlock == clock, "and sleeps in the wait between times");
|
||||
}
|
||||
CHECK(is_empty(tell(12, "55 3 ROUTE")), "node 12 is told the way to 55 is by node 10, which has none");
|
||||
CHECK(strcmp(tell(12, ": C2 S\" 1 DROP\" 55 SEND BEGIN 0 UNTIL ; C2"), "(still running)") == 0 && pool[0].n.asking && pool[0].n.mem[OWED_COUNT] == 1,
|
||||
"node 12 sends 55 a message and is then stuck in a loop: node 10 owes it a refusal, writes it, and is blocked");
|
||||
v4_fabric_gone_error(&f, V4_ERROR_NO_ONE);
|
||||
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]); }
|
||||
step_cap = 20000000;
|
||||
CHECK(strcmp(tell(10, "1 2 + ."), "3 ") == 0 && pool[0].n.mem[OWED_COUNT] == 0 && waiting(hera), "node 10 is let go, lets the refusal go, and goes on: \"%s\"", printed);
|
||||
|
||||
printf(" %d checks, %d failures\n", checks, failures);
|
||||
return failures != 0;
|
||||
|
||||
Reference in New Issue
Block a user