fix(v4.0.0): step 6e review round 1 -- a take that does not take does nothing; output and answers wait at a middle node; no queue on the kernel's port; a printing wait is not interrupted
- The result of every take is looked at: a wire cut between the look and the take leaves nothing acted on (the text before run again, a refusal counted, a wait ended by an old answer, a way forgotten). - A node with no room to pass on what another printed, or its word of how text ended, leaves it on the wire it came by and moves it when there is room; what is behind it for the same port waits too. Sleep for room (9) is woken by room on any of the node's wires (engine; MESH.md 7d.3). - The lone node's host keeps no queue for the kernel's port: a message to it is error 18. The step limit is counted while the node waits for room. - A text waiting for room to print is not interrupted by console text; AWAIT is, as before. (BREAK?) is gone. - A GONE is believed only by the centre's port; unawaited answers let go are counted; nothing past a message's last character goes on a wire; AWAIT sets (AWAIT-FROM) only once the stack has been tried. - test_host_depth.c: ten paths, each run works or ends in one named stack error; it fails with (ROOM) or (ROOM-SERVE) taken out. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
b1d09af043
commit
6a7d0aa27f
+11
-2
@@ -95,12 +95,17 @@
|
||||
#define OUT_HDR (BUF0_W - 112) /* the seven words of a message this node is putting on a wire */
|
||||
#define W_NEED (BUF0_W - 105) /* the wait for room: how many cells, */
|
||||
#define W_PORT (BUF0_W - 104) /* and on which port's wire */
|
||||
#define P_WAY (BUF0_W - 103) /* the port a message being passed on goes by */
|
||||
#define P_WAY (BUF0_W - 103) /* the port a message being passed on goes by, */
|
||||
#define P_NEED (BUF0_W - 100) /* and the cells it needs there */
|
||||
#define H_PORT (BUF0_W - 99) /* the port of the first message left on its wire for want of room on this pass; -1: none */
|
||||
#define H_NEED (BUF0_W - 98) /* the cells it needs there */
|
||||
#define H_MASK (BUF0_W - 97) /* and a bit for each port something was left for */
|
||||
#define A_SELF (BUF0_W - 102) /* 1: a message of this node's own, for the node it waits on, came back and was let go here */
|
||||
#define SEND_PTR (BUF0_W - 101) /* where the next word of a text being made ready to send goes */
|
||||
#define OUT_TEXT (OUT_W - 64) /* what was printed, four characters to a cell, as it goes on the wire: 64 cells */
|
||||
#define SEND_TEXT (OUT_W - 400) /* the text of a message SEND is putting, four characters to a cell: 256 cells */
|
||||
/* FREE, plain memory: BUF0_W - 8; - 32 .. - 21; - 100 .. - 97; - 115 .. - 113; and OUT_W - 144 .. OUT_W - 65. They and
|
||||
/* FREE, plain memory: BUF0_W - 8; - 32 .. - 21; - 122 .. - 113; BUF0_W - 144 .. - 141 (what a flush makes zero after
|
||||
* a full output buffer); and OUT_W - 144 .. OUT_W - 65. They and
|
||||
* the cells above held the messages waiting, the refusals owed and what went with them (MESH.md 7a, 7b), which a
|
||||
* node no longer keeps. */
|
||||
#define AWAIT_FROM (BUF0_W - 39) /* the node whose word of how text ended is waited for */
|
||||
@@ -228,6 +233,10 @@ static int host_load(v4_text *tx, v4_node *n, const char *const *files, unsigned
|
||||
v4_text_constant(tx, "(W-NEED)", W_NEED);
|
||||
v4_text_constant(tx, "(W-PORT)", W_PORT);
|
||||
v4_text_constant(tx, "(P-WAY)", P_WAY);
|
||||
v4_text_constant(tx, "(P-NEED)", P_NEED);
|
||||
v4_text_constant(tx, "(H-PORT)", H_PORT);
|
||||
v4_text_constant(tx, "(H-NEED)", H_NEED);
|
||||
v4_text_constant(tx, "(H-MASK)", H_MASK);
|
||||
v4_text_constant(tx, "(A-SELF)", A_SELF);
|
||||
v4_text_constant(tx, "(SEND^)", SEND_PTR);
|
||||
v4_text_constant(tx, "(OUT-TEXT)", OUT_TEXT);
|
||||
|
||||
+49
-2
@@ -743,9 +743,11 @@ int main(void)
|
||||
CHECK(ask(a, 0, 250, 0, 9) == -99, "asleep for 250");
|
||||
v4_wire_first(f.place[b].wire[3].rx);
|
||||
CHECK(ask(b, 3, 400, 420, 4) == 0 && V4_WIRE_CELLS - f.place[a].wire[0].tx->used == 200, "the reader drops the small one: 200 are free");
|
||||
CHECK(!ND(a)->n.doing && ND(a)->n.how == 1, "the sleeper, which wants 250, is woken, and told 1: a wire of its own has gained room, and not enough");
|
||||
CHECK(ask(a, 0, 250, 0, 9) == -99, "it asks again, and sleeps");
|
||||
for (i = 0; i < 50; i++) (void)v4_fabric_step(&f);
|
||||
CHECK(ND(a)->n.doing, "and the sleeper, which wants 250, is not woken by a take that did not give it room");
|
||||
CHECK(ask(b, 3, 400, 420, 4) == 0 && !ND(a)->n.doing && ND(a)->n.how == 0, "the next take does, with 0");
|
||||
CHECK(ND(a)->n.doing, "and sleeps on while nothing changes");
|
||||
CHECK(ask(b, 3, 400, 420, 4) == 0 && !ND(a)->n.doing && ND(a)->n.how == 0, "the next take gives it the room: 0");
|
||||
/* woken by a message coming instead */
|
||||
v4_wire_reset(f.place[a].wire[0].tx);
|
||||
fill(f.place[a].wire[0].tx, 100);
|
||||
@@ -757,6 +759,51 @@ int main(void)
|
||||
(void)v4_fabric_step(&f);
|
||||
CHECK(!ND(a)->n.doing && ND(a)->n.how == 1 && f.place[a].wire[0].tx->used + 200 > V4_WIRE_CELLS, "it wakes, and how it went says 1: no room, but a message");
|
||||
|
||||
/* SLEEP FOR ROOM IS WOKEN BY ROOM ON ANY OF THE NODE'S WIRES (MESH.md 7d.3): a node may be waiting to
|
||||
* put on more than one, and names only one */
|
||||
v4_fabric_init(&f, more_places, 8);
|
||||
v4_fabric_queues(&f, qpool, 8);
|
||||
a = (unsigned)v4_fabric_add(&f, &pool[0], PB);
|
||||
b = (unsigned)v4_fabric_add(&f, &pool[1], PB);
|
||||
c = (unsigned)v4_fabric_add(&f, &pool[2], PB);
|
||||
CHECK(v4_fabric_wire(&f, a, 0, b, 3) && v4_fabric_wire(&f, a, 1, c, 2), "a node with two wires");
|
||||
fill(f.place[a].wire[0].tx, 0);
|
||||
fill(f.place[a].wire[1].tx, 0);
|
||||
(void)v4_node_fetch(&ND(a)->n, P_HAVE);
|
||||
(void)v4_fabric_step(&f);
|
||||
CHECK(ask(a, 0, 100, 0, 9) == -99, "both full: asleep for room on wire 0");
|
||||
for (i = 0; i < 100; i++) (void)v4_fabric_step(&f);
|
||||
CHECK(ND(a)->n.doing, "and it sleeps on while nothing changes");
|
||||
v4_wire_first(f.place[c].wire[2].rx);
|
||||
CHECK(ask(c, 2, 400, 420, 4) == 0, "the reader of wire 1 drops a message");
|
||||
CHECK(!ND(a)->n.doing && ND(a)->n.how == 1 && f.place[a].wire[0].tx->used == V4_WIRE_CELLS, "it wakes, told 1: the wire it named has no room, but something has changed");
|
||||
CHECK(ask(a, 0, 100, 0, 10) == 1 && ask(a, 1, 100, 0, 10) == 0, "it asks which: wire 1 has the room");
|
||||
CHECK(ask(a, 0, 100, 0, 9) == -99, "asleep for room on wire 0 again: that room on wire 1 was seen");
|
||||
for (i = 0; i < 100; i++) (void)v4_fabric_step(&f);
|
||||
CHECK(ND(a)->n.doing, "and it is not woken twice for it");
|
||||
/* the room comes in the very step the node goes to sleep: the reader's take is done before the sleep is looked at */
|
||||
v4_node_store(&ND(c)->n, P_WIRE, 2); v4_node_store(&ND(c)->n, P_DO, 4);
|
||||
ND(a)->n.doing = 0;
|
||||
v4_node_store(&ND(a)->n, P_WIRE, 0); v4_node_store(&ND(a)->n, P_A, 100); v4_node_store(&ND(a)->n, P_DO, 9);
|
||||
(void)v4_fabric_step(&f);
|
||||
CHECK(!ND(c)->n.doing && !ND(a)->n.doing && ND(a)->n.how == 1, "room that comes in the step the node goes to sleep wakes it: no wake-up is lost");
|
||||
/* a device's take is a take */
|
||||
{
|
||||
static const v4_device dev = { 0, 0, 0, 0 };
|
||||
v4_cell hh[7], tt[V4_WIRE_CELLS / 4];
|
||||
CHECK(v4_fabric_wire_device(&f, a, 4, &dev), "a device on a third wire");
|
||||
fill(f.place[a].wire[4].tx, 0);
|
||||
CHECK(ask(a, 0, 100, 0, 9) == -99, "asleep for room on wire 0");
|
||||
CHECK(v4_fabric_device_take(&f, a, 4, hh, tt, V4_WIRE_CELLS / 4) == V4_WIRE_DONE, "the device takes a message between steps");
|
||||
(void)v4_fabric_step(&f);
|
||||
CHECK(!ND(a)->n.doing && ND(a)->n.how == 1, "and that wakes it");
|
||||
}
|
||||
/* a plain sleep is not woken by room */
|
||||
(void)v4_node_fetch(&ND(a)->n, P_HAVE);
|
||||
(void)v4_fabric_step(&f);
|
||||
CHECK(ask(a, 0, 0, 0, 8) == -99, "asleep, for a message");
|
||||
CHECK(ask(c, 2, 400, 420, 4) == 0 && ND(a)->n.doing, "room on a wire does not wake a node that sleeps for a message only");
|
||||
|
||||
/* move from one of a node's wires to another */
|
||||
v4_fabric_init(&f, more_places, 8);
|
||||
v4_fabric_queues(&f, qpool, 8);
|
||||
|
||||
+148
-39
@@ -7,11 +7,19 @@
|
||||
* attempt at step 6d was tested on a shallow stack with every node there,
|
||||
* and hung on a deep one. Here node 11 does each thing a node does with a
|
||||
* message -- print, SEND, SEND with no way, AWAIT answered, AWAIT ended by a
|
||||
* GONE, pass a message on while its own text waits for room -- with its data
|
||||
* stack holding 20 to 32 values, and from 20 to 32 calls deep. After each,
|
||||
* every node must answer, and node 11's stack must be as deep as the text
|
||||
* made it, or its text must have ended in an error it reported. It may
|
||||
* refuse; it may not hang, and it may not swallow a line.
|
||||
* GONE, pass a message on while its own text waits for room, AWAIT broken
|
||||
* by a line from the console, refuse a message while its own text waits for
|
||||
* room, send a GONE -- with its data stack holding 20 to 32 values, and from
|
||||
* 20 to 33 calls deep.
|
||||
*
|
||||
* EVERY RUN ENDS ONE OF TWO WAYS, and which is checked: the path WORKS, all
|
||||
* of it, as it does on a shallow stack; or node 11's text ends in "Stack
|
||||
* overflow" or "Return stack overflow" and nothing else -- with nothing
|
||||
* half done: no message lost that was owed, no line swallowed. Where the
|
||||
* stack is shallow enough it must work. And after each, every node must
|
||||
* answer. "It came back" is not enough: with the nucleus's tries of the
|
||||
* stack taken out -- (ROOM-SERVE), (ROOM) -- this test fails (it was
|
||||
* mutated to see).
|
||||
*/
|
||||
#include "v4/image.h"
|
||||
#include "v4/fabric.h"
|
||||
@@ -173,16 +181,32 @@ static int row(void)
|
||||
}
|
||||
|
||||
/* ---- the sweep ---- */
|
||||
static unsigned runs, bad;
|
||||
static unsigned runs, bad, worked, refused_clean;
|
||||
|
||||
/* what the last path watches for: node 11 asleep for room, and, while it is, a message it has passed on to
|
||||
* the wire to node 12 */
|
||||
/* what the paths that wait for room watch for: node 11 asleep for room, and, while it is, a message it has
|
||||
* passed on to the wire to node 12 */
|
||||
static unsigned saw_room_wait, saw_passed;
|
||||
static void watch_mid(void)
|
||||
{
|
||||
if (pool[mid].n.doing && pool[mid].n.do_op == 9) { saw_room_wait++; if (places[mid].wire[3].tx && places[mid].wire[3].tx->used > 0) saw_passed++; }
|
||||
}
|
||||
|
||||
/* A MESSAGE THAT HAS BEEN PASSED ON AS OFTEN AS IT MAY BE comes to node 11 from node 12, for a node 99: its
|
||||
* fourth word is 1. No row of three can make one -- it takes sixteen nodes, or a ring -- so it is put on the
|
||||
* wire here, once, the first time node 11 is asleep for room. Node 11 refuses it without looking for its way:
|
||||
* the one refusal in which a message is dropped before the node's deepest use of its stacks. */
|
||||
static int spent_put;
|
||||
static void put_spent(void)
|
||||
{
|
||||
static const v4_cell hd[V4_WIRE_HEADER] = { 99, 12, V4_MSG_TEXT, 1, 0, 0, 4 }, text[1] = { 0x31 };
|
||||
if (!spent_put && places[mid].wire[3].rx) spent_put = v4_wire_put(places[mid].wire[3].rx, hd, text) == V4_WIRE_DONE;
|
||||
}
|
||||
static void watch_mid_spent(void)
|
||||
{
|
||||
watch_mid();
|
||||
if (saw_room_wait) put_spent();
|
||||
}
|
||||
|
||||
/* words that push 1, 2, 4, 8 and 16 values and call nothing while they do */
|
||||
static void fill(char *text, size_t cap, unsigned count)
|
||||
{
|
||||
@@ -193,17 +217,27 @@ static void fill(char *text, size_t cap, unsigned count)
|
||||
for (bit = 8; bit; bit >>= 1) if (count >= bit) { at += (size_t)snprintf(text + at, cap - at, "P%u ", bit); count -= bit; }
|
||||
}
|
||||
|
||||
#define PATHS 10u
|
||||
static const char *const path_name[PATHS] = { "a text that prints", "SEND", "SEND with no way", "AWAIT answered", "AWAIT ended by a GONE", "passing on while its text waits for room",
|
||||
"AWAIT broken by a line from the console", "a refusal while its text waits for room", "GONE",
|
||||
"a spent message refused while its text waits" };
|
||||
static char numbers[8192]; /* what 1200 0 DO I . LOOP prints */
|
||||
|
||||
/* A row made afresh for one path; node 11 is given the word ACT that takes it, and the words that fill the
|
||||
* stack and that call each other. */
|
||||
static int made(unsigned path)
|
||||
{
|
||||
static const char *const act[] = {
|
||||
static const char *const act[PATHS] = {
|
||||
": ACT 600 0 DO 65 EMIT LOOP ;",
|
||||
": ACT S\" 1 DROP\" 12 SEND ;",
|
||||
": ACT S\" 1 DROP\" 99 SEND ;",
|
||||
": ACT S\" 1 DROP\" 12 SEND 12 AWAIT DROP ;",
|
||||
": ACT 12 AWAIT DROP ;",
|
||||
": ACT 1200 0 DO I . LOOP ;",
|
||||
": ACT 12 AWAIT DROP ;",
|
||||
": ACT 1200 0 DO I . LOOP ;",
|
||||
": ACT 44 12 GONE ;",
|
||||
": ACT 1200 0 DO I . LOOP ;",
|
||||
};
|
||||
char line[64];
|
||||
unsigned k;
|
||||
@@ -216,30 +250,60 @@ static int made(unsigned path)
|
||||
if (path == 2 && !told(11, "NO-ROUTES 1 2 ROUTE 10 2 ROUTE 12 3 ROUTE")) return 0; /* no way for any node it has not been told of */
|
||||
if (path == 4 && !told(12, ": LL 400000 0 DO LOOP ;")) return 0;
|
||||
if (path == 5 && !told(10, ": SLOW 500000 0 DO LOOP ; : BUSY SLOW S\" 1 DROP\" 12 SEND SLOW ;")) return 0;
|
||||
if (path == 9 && !told(10, ": SLOW 500000 0 DO LOOP ; : BUSY SLOW SLOW ;")) return 0;
|
||||
/* 320 characters: more than fits beside what node 11 itself is waiting to print */
|
||||
if (path == 7 && !(told(10, ": SLOW 500000 0 DO LOOP ; : BUSY SLOW SLOW ;") && told(12, ": LATE 250000 0 DO LOOP PAD 320 BLANK PAD 320 99 SEND ;"))) return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Node 11 takes the path with `text`; `values` is how many values the text leaves on its stack if it completes. */
|
||||
static int one(unsigned path, const char *what, unsigned depth, const char *text, unsigned values)
|
||||
/* 1 if `s` is `lead`, then some of `body` from its start, then exactly `end`. */
|
||||
static int is_cut_short(const char *s, const char *body, const char *end)
|
||||
{
|
||||
static const char *const name[] = { "a text that prints", "SEND", "SEND with no way", "AWAIT answered", "AWAIT ended by a GONE", "passing on while its text waits for room" };
|
||||
const char *why = 0;
|
||||
int how11;
|
||||
size_t n = strlen(s), e = strlen(end);
|
||||
return n >= e && strcmp(s + n - e, end) == 0 && strncmp(s, body, n - e) == 0 && n - e <= strlen(body);
|
||||
}
|
||||
|
||||
/* Node 11 takes the path with `text`; `values` is how many values the text leaves on its stack if it completes;
|
||||
* `must` says the stack is shallow enough that it must work. Returns 1 if it worked, 0 if it ended in a clean
|
||||
* stack error, and counts a failure if it did neither. */
|
||||
static int one(unsigned path, const char *what, unsigned depth, const char *text, unsigned values, int must)
|
||||
{
|
||||
static char sixhundred[601];
|
||||
const char *why = 0, *over = 0;
|
||||
v4_cell lost11 = pool[mid].n.mem[LOST], refused12 = pool[far].n.mem[REFUSED], ways12;
|
||||
int how11, works = 0, clean_error = 0;
|
||||
unsigned left;
|
||||
|
||||
if (!sixhundred[0]) memset(sixhundred, 'A', 600);
|
||||
runs++;
|
||||
forget();
|
||||
step_cap = 60000000;
|
||||
if (path == 4) { /* node 12 is busy for a long while and sends no answer; node 11 waits; its centre says 12 is gone */
|
||||
if (path == 8 && !told(12, "44 2 ROUTE")) why = "node 12 did not take a way";
|
||||
ways12 = pool[far].n.mem[ROUTE_COUNT];
|
||||
forget();
|
||||
saw_room_wait = 0; saw_passed = 0;
|
||||
if (why) { }
|
||||
else if (path == 4) { /* node 12 is busy for a long while and sends no answer; node 11 waits; its centre says 12 is gone */
|
||||
stuck = 1u << far;
|
||||
if (!put_text(12, "LL") || !put_text(11, text) || !run()) why = "it did not come to wait";
|
||||
else if (!put_text(10, "12 11 GONE") || !run()) why = "the GONE was not done";
|
||||
stuck = 0;
|
||||
if (!why && !run()) why = "the nodes did not come to rest";
|
||||
} else if (path == 5) { /* node 10, by which node 11 prints, is busy, and sends node 12 a message by node 11 meanwhile */
|
||||
saw_room_wait = 0; saw_passed = 0; watch = watch_mid;
|
||||
watch = watch_mid;
|
||||
if (!put_text(11, text) || !put_text(10, "BUSY") || !run()) why = "the nodes did not come to rest";
|
||||
watch = 0;
|
||||
} else if (path == 6) { /* node 12 sends no answer; node 11 waits, and is typed to */
|
||||
if (!put_text(11, text) || !run()) why = "it did not come to wait";
|
||||
else if (!put_text(11, "77 .") || !run()) why = "the nodes did not come to rest";
|
||||
} else if (path == 7) { /* node 10 is busy; node 12 sends a message for 99 by node 11, whose way for it is the full wire */
|
||||
watch = watch_mid;
|
||||
if (!put_text(11, text) || !put_text(10, "BUSY") || !put_text(12, "LATE") || !run()) why = "the nodes did not come to rest";
|
||||
watch = 0;
|
||||
} else if (path == 9) { /* node 10 is busy; a spent message comes while node 11 waits for room -- or, if it never does, afterwards */
|
||||
spent_put = 0; watch = watch_mid_spent;
|
||||
if (!put_text(11, text) || !put_text(10, "BUSY") || !run()) why = "the nodes did not come to rest";
|
||||
watch = 0;
|
||||
if (!why && !spent_put) { put_spent(); if (!run()) why = "the nodes did not come to rest"; }
|
||||
} else {
|
||||
if (!put_text(11, text) || !run()) why = "the nodes did not come to rest";
|
||||
}
|
||||
@@ -247,15 +311,43 @@ static int one(unsigned path, const char *what, unsigned depth, const char *text
|
||||
left = (unsigned)pool[mid].n.ds.depth;
|
||||
if (!why && !(asleep(hera) && asleep(mid) && asleep(far))) why = "a node is not asleep";
|
||||
if (!why && ended < 1) why = "node 11's text was not answered";
|
||||
if (!why && how11 == V4_TEXT_COMPLETED && left != values) why = "its stack is not as deep as the text made it";
|
||||
if (!why && how11 == V4_TEXT_ERROR && !(strstr(printed, "overflow") || strstr(printed, "Argument out of range") || strstr(printed, "Node gone") || strstr(printed, "Message refused")))
|
||||
why = "its text ended in an error it did not report";
|
||||
if (!why && how11 != V4_TEXT_COMPLETED && how11 != V4_TEXT_ERROR) why = "its text ended neither way";
|
||||
if (!why && path == 0 && how11 == V4_TEXT_COMPLETED && printed_len != 600) why = "not all it printed arrived";
|
||||
if (!why && path == 5 && how11 == V4_TEXT_COMPLETED && !strstr(printed, " 1199 ")) why = "not all it printed arrived";
|
||||
if (!why && path == 5 && how11 == V4_TEXT_COMPLETED && !(saw_room_wait > 0 && saw_passed > 0)) why = "it did not wait for room, or passed nothing on while it did";
|
||||
|
||||
/* which of the two ways did it end? */
|
||||
if (!why) {
|
||||
if (strstr(printed, "Return stack overflow\n")) over = "Return stack overflow\n";
|
||||
else if (strstr(printed, "Stack overflow\n")) over = "Stack overflow\n";
|
||||
switch (path) {
|
||||
case 0: works = how11 == V4_TEXT_COMPLETED && strcmp(printed, sixhundred) == 0 && left == values;
|
||||
clean_error = over && is_cut_short(printed, sixhundred, over); break;
|
||||
case 1: works = how11 == V4_TEXT_COMPLETED && printed_len == 0 && left == values && pool[mid].n.mem[LOST] == lost11 + 1; /* node 12's answer, which it did not wait for */
|
||||
clean_error = over && strcmp(printed, over) == 0 && pool[mid].n.mem[LOST] == lost11; break;
|
||||
case 2: works = how11 == V4_TEXT_ERROR && strcmp(printed, "Argument out of range\n") == 0 && left == values;
|
||||
clean_error = over && strcmp(printed, over) == 0; break;
|
||||
case 3: works = how11 == V4_TEXT_COMPLETED && printed_len == 0 && left == values && pool[mid].n.mem[LOST] == lost11;
|
||||
clean_error = over && strcmp(printed, over) == 0; break;
|
||||
case 4: works = how11 == V4_TEXT_ERROR && strcmp(printed, "Node gone\n") == 0 && left == values;
|
||||
clean_error = over && strcmp(printed, over) == 0; break;
|
||||
case 5: works = how11 == V4_TEXT_COMPLETED && strcmp(printed, numbers) == 0 && left == values && saw_room_wait > 0 && saw_passed > 0;
|
||||
clean_error = over && is_cut_short(printed, numbers, over); break;
|
||||
case 6: works = strcmp(printed, "Interrupted\n77 ") == 0 && ended == 2;
|
||||
if (over) { char want[64]; snprintf(want, sizeof want, "%s77 ", over); clean_error = strcmp(printed, want) == 0 && ended == 2; }
|
||||
break;
|
||||
case 7: works = how11 == V4_TEXT_COMPLETED && strcmp(printed, numbers) == 0 && left == values && saw_room_wait > 0
|
||||
&& pool[mid].n.mem[LOST] == lost11 + 1 && pool[far].n.mem[REFUSED] == refused12 + 1;
|
||||
clean_error = over && is_cut_short(printed, numbers, over) && pool[far].n.mem[REFUSED] == refused12 + 1; break; /* whoever refused it, node 12 was told */
|
||||
case 8: works = how11 == V4_TEXT_COMPLETED && printed_len == 0 && left == values && pool[far].n.mem[ROUTE_COUNT] == ways12 - 1;
|
||||
clean_error = over && strcmp(printed, over) == 0; break;
|
||||
case 9: works = how11 == V4_TEXT_COMPLETED && strcmp(printed, numbers) == 0 && left == values && saw_room_wait > 0
|
||||
&& pool[mid].n.mem[LOST] == lost11 + 1 && pool[far].n.mem[REFUSED] == refused12 + 1;
|
||||
clean_error = over && is_cut_short(printed, numbers, over) && pool[mid].n.mem[LOST] == lost11 + 1 && pool[far].n.mem[REFUSED] == refused12 + 1; break; /* it was dropped: its sender was told */
|
||||
}
|
||||
if (clean_error && !(path == 6 || (how11 == V4_TEXT_ERROR && left == 0))) clean_error = 0; /* a stack fault empties the stack */
|
||||
if (works && over) works = 0;
|
||||
if (!works && !clean_error) why = "it neither worked nor ended in a stack error and nothing else";
|
||||
else if (!works && must) why = "it ended in a stack error where the stack is shallow enough for it to work";
|
||||
}
|
||||
/* every node comes back */
|
||||
if (!why && !(strcmp(tell(11, "ABORT"), "") == 0 && ended == 1 && pool[mid].n.ds.depth == 0)) why = "node 11 did not answer ABORT";
|
||||
if (!why && !(strcmp(tell(11, "ABORT"), "") == 0 && ended == 1 && pool[mid].n.ds.depth == 0 && pool[mid].n.mem[AWAIT_FROM] == 0)) why = "node 11 did not answer ABORT";
|
||||
if (!why && path == 4 && !told(11, "12 3 ROUTE")) why = "node 11 did not take the way to 12 again";
|
||||
if (!why && strcmp(tell(11, "1 2 + ."), "3 ") != 0) why = "node 11 did not answer";
|
||||
if (!why && strcmp(tell(10, "1 2 + ."), "3 ") != 0) why = "node 10 did not answer";
|
||||
@@ -264,11 +356,14 @@ static int one(unsigned path, const char *what, unsigned depth, const char *text
|
||||
checks++;
|
||||
if (why) {
|
||||
failures++; bad++;
|
||||
printf("FAIL %s, %s %u: %s: \"%.60s\"\n", name[path], what, depth, why, printed);
|
||||
printf("FAIL %s, %s %u: %s: how %d, %u left, lost %+ld, refused %+ld, waits %u/%u: \"%.50s\"\n", path_name[path], what, depth, why, how11, left,
|
||||
(long)(pool[mid].n.mem[LOST] - lost11), (long)(pool[far].n.mem[REFUSED] - refused12), saw_room_wait, saw_passed, printed);
|
||||
show_stuck(text);
|
||||
(void)made(path); /* the next begins on a row that is whole */
|
||||
return -1;
|
||||
}
|
||||
return how11;
|
||||
if (works) worked++; else refused_clean++;
|
||||
return works;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
@@ -299,30 +394,39 @@ int main(void)
|
||||
nucleus.mem[SRC] = TIB;
|
||||
nucleus.mem[LINE_STATUS] = 1;
|
||||
HOST_MESSAGES_EMPTY(&nucleus);
|
||||
{ size_t at = 0; for (k = 0; k < 1200; k++) at += (size_t)snprintf(numbers + at, sizeof numbers - at, "%u ", k); }
|
||||
|
||||
for (path = 0; path < 6; path++) {
|
||||
unsigned before = bad, done = 0, errors = 0;
|
||||
for (path = 0; path < PATHS; path++) {
|
||||
unsigned before = bad, w0 = worked, e0 = refused_clean, first_error = 0;
|
||||
int r;
|
||||
CHECK(made(path), "a row for path %u", path);
|
||||
if (failures > (int)bad) break;
|
||||
/* the data stack at every depth from 20 to full */
|
||||
/* the data stack at every depth from 20 to full: with 24 or fewer every path must work */
|
||||
for (k = V4_DATA_DEPTH - 12u; k <= V4_DATA_DEPTH; k++) {
|
||||
size_t at;
|
||||
fill(text, sizeof text, k);
|
||||
at = strlen(text);
|
||||
snprintf(text + at, sizeof text - at, "ACT");
|
||||
if (one(path, "values on the stack", k, text, k) == V4_TEXT_COMPLETED) done++; else errors++;
|
||||
r = one(path, "values on the stack", k, text, k, k + 8u <= V4_DATA_DEPTH);
|
||||
if (r == 0 && !first_error) first_error = k;
|
||||
}
|
||||
/* and from every depth of calls from 20 to past the end of the return stack */
|
||||
for (k = V4_RET_DEPTH - 12u; k <= V4_RET_DEPTH + 1u; k++) {
|
||||
printf(" %-42s %2u..%u values: works to %u, then a stack error and nothing else;", path_name[path], (unsigned)(V4_DATA_DEPTH - 12u), (unsigned)V4_DATA_DEPTH, first_error ? first_error - 1u : (unsigned)V4_DATA_DEPTH);
|
||||
/* and from every depth of calls from 14 to past the end of the return stack: 21 deep or less every path
|
||||
* must work -- 16 or less for the two that print and are woken while they wait for room, which go
|
||||
* deepest: the loop, the word that prints, EMIT, the wait, and (SERVE) under it */
|
||||
first_error = 0;
|
||||
for (k = V4_RET_DEPTH - 18u; k <= V4_RET_DEPTH + 1u; k++) {
|
||||
snprintf(text, sizeof text, "N%u", k);
|
||||
(void)one(path, "calls deep", k, text, 0);
|
||||
r = one(path, "calls deep", k, text, 0, k + ((path == 5 || path == 7 || path == 9) ? 16u : 11u) <= V4_RET_DEPTH);
|
||||
if (r == 0 && !first_error) first_error = k;
|
||||
}
|
||||
printf(" path %u: with %u to %u values on the stack, %u completed and %u ended in an error reported; %u failures\n", path, (unsigned)(V4_DATA_DEPTH - 12u), (unsigned)V4_DATA_DEPTH, done, errors, bad - before);
|
||||
printf(" calls: works to %u deep; %u worked, %u clean errors, %u failures\n", first_error ? first_error - 1u : (unsigned)(V4_RET_DEPTH + 1u), worked - w0, refused_clean - e0, bad - before);
|
||||
}
|
||||
/* ---- between texts: node 11 passes on, and refuses, with as many values waiting on its stack as a text
|
||||
* may leave (MESH.md 7d.4: the rule of 28). Not one fault. ---- */
|
||||
|
||||
/* ---- between texts: node 11 passes on, refuses, and holds what it has no room to pass on, with as many
|
||||
* values waiting on its stack as a text may leave (MESH.md 7d.4: the rule of 28). Not one fault. ---- */
|
||||
CHECK(row() && told(11, ": P1 7 ; : P2 7 7 ; : P4 7 7 7 7 ; : P8 7 7 7 7 7 7 7 7 ;") && told(11, ": P16 P8 P8 ;") && told(11, "77 5 ROUTE")
|
||||
&& told(12, ": S77 S\" 1 DROP\" 77 SEND ;"), "a row; node 11's way to 77 is a port with no one on it");
|
||||
&& told(12, ": S77 S\" 1 DROP\" 77 SEND ; : PR 1200 0 DO I . LOOP ;") && told(10, ": SLOW 500000 0 DO LOOP ; : BUSY SLOW SLOW ;"), "a row; node 11's way to 77 is a port with no one on it");
|
||||
for (k = V4_DATA_DEPTH - 12u; k + 4u <= V4_DATA_DEPTH; k++) {
|
||||
unsigned faults;
|
||||
v4_cell lost = pool[mid].n.mem[LOST], refused = pool[far].n.mem[REFUSED];
|
||||
@@ -331,11 +435,16 @@ int main(void)
|
||||
faults = pool[mid].n.faults;
|
||||
CHECK(strcmp(tell(12, "1 2 + ."), "3 ") == 0 && ended == 1, "with %u values waiting it passes text on to node 12, and what comes back", k);
|
||||
CHECK(told(12, "S77") && pool[mid].n.mem[LOST] == lost + 1 && pool[far].n.mem[REFUSED] == refused + 1, "and refuses a message it has no way for, and tells node 12");
|
||||
forget();
|
||||
saw_room_wait = 0; watch = watch_mid;
|
||||
CHECK(put_text(12, "PR") && put_text(10, "BUSY") && run() && strcmp(printed, numbers) == 0 && saw_room_wait > 0,
|
||||
"and holds what node 12 prints while node 10 is busy, asleep for room, and passes all of it on: %u characters", printed_len);
|
||||
watch = 0;
|
||||
CHECK(pool[mid].n.faults == faults && pool[mid].n.ds.depth == k && asleep(mid), "without a fault, its stack as it was: %u values", (unsigned)pool[mid].n.ds.depth);
|
||||
CHECK(strcmp(tell(11, "ABORT"), "") == 0 && strcmp(tell(11, "1 2 + ."), "3 ") == 0, "and it goes on");
|
||||
}
|
||||
|
||||
printf(" %u runs; %u in which a node did not come back\n", runs, bad);
|
||||
printf(" %u runs: %u worked, %u ended in a stack error and nothing else, %u did neither\n", runs, worked, refused_clean, bad);
|
||||
printf(" %d checks, %d failures\n", checks, failures);
|
||||
return failures != 0;
|
||||
}
|
||||
|
||||
+194
-22
@@ -46,6 +46,7 @@ static v4_cell printed_from; /* which node the last of it came from
|
||||
static int ended; /* how many messages of how text ended have come, */
|
||||
static v4_cell ended_from, ended_how; /* and the last of them */
|
||||
static unsigned ended_at; /* and how much had been printed when it came */
|
||||
static unsigned stale_out; /* output messages whose last word had anything in it past the last character */
|
||||
static int nacks; /* how many NACKs have come to the console, */
|
||||
static v4_cell nack_from, nack_about; /* and the last of them: who refused, and the node the refused message was for */
|
||||
|
||||
@@ -60,6 +61,7 @@ static unsigned console_takes(void)
|
||||
for (i = 0; i < (unsigned)hd[6] && printed_len + 1 < sizeof printed; i++) printed[printed_len++] = (char)(((v4_ucell)text[i / 4u] >> (8u * (i % 4u))) & 0xFFu);
|
||||
printed[printed_len] = 0;
|
||||
printed_from = hd[1];
|
||||
if (hd[6] % 4 != 0 && ((v4_ucell)text[hd[6] / 4] >> (8u * (unsigned)(hd[6] % 4))) != 0) stale_out++;
|
||||
} else if (hd[2] == V4_MSG_DONE) {
|
||||
ended++; ended_from = hd[1]; ended_how = text[0]; ended_at = printed_len;
|
||||
} else if (hd[2] == V4_MSG_NACK) {
|
||||
@@ -129,6 +131,17 @@ static int run(void)
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
/* Let time pass until `when` says so; 0 if it never does. */
|
||||
static int run_until(int (*when)(void), unsigned cap)
|
||||
{
|
||||
unsigned steps;
|
||||
for (steps = 0; steps < cap; steps++) {
|
||||
if (when()) return 1;
|
||||
(void)v4_fabric_step(&f); (void)console_takes(); steps_run++;
|
||||
if (watch) watch();
|
||||
}
|
||||
return when();
|
||||
}
|
||||
/* The console sends text to a node: the message is put on the wire to the node the console is on. */
|
||||
static int put_text(v4_cell node, const char *text)
|
||||
{
|
||||
@@ -202,6 +215,22 @@ static int triangle(void)
|
||||
}
|
||||
static v4_uheat_t clock_of(unsigned id) { return pool[id].es.anticlock; }
|
||||
|
||||
/* The whole of the mesh as it stands, kept and put back: so that one moment can be gone on from many times. */
|
||||
static v4_fabric_node kept_pool[3];
|
||||
static v4_place kept_places[3];
|
||||
static v4_wire_queue kept_queues[QUEUES];
|
||||
static v4_fabric kept_f;
|
||||
static void keep(void) { memcpy(kept_pool, pool, sizeof pool); memcpy(kept_places, places, sizeof places); memcpy(kept_queues, queues, sizeof queues); kept_f = f; }
|
||||
static void put_back(void) { memcpy(pool, kept_pool, sizeof pool); memcpy(places, kept_places, sizeof places); memcpy(queues, kept_queues, sizeof queues); f = kept_f; forget(); }
|
||||
|
||||
/* what a scene waits for */
|
||||
static int far_has_one(void) { return coming_to(far, 2) && coming_to(far, 2)->used > 0; }
|
||||
static int far_has_nack(void) { return far_has_one() && coming_to(far, 2)->cell[(coming_to(far, 2)->head + 2u) % V4_WIRE_CELLS] == V4_MSG_NACK; }
|
||||
static int far_awaits_and_has_one(void) { return pool[far].n.mem[AWAIT_FROM] == 11 && far_has_one(); }
|
||||
static int mid_holds(void) { return pool[mid].n.doing && pool[mid].n.do_op == 9 && pool[mid].n.mem[H_PORT] >= 0; }
|
||||
static unsigned saw_mid_hold;
|
||||
static void watch_mid_holds(void) { if (pool[mid].n.doing && pool[mid].n.do_op == 9 && pool[mid].n.mem[H_PORT] >= 0) saw_mid_hold++; }
|
||||
|
||||
/* what a scene watches for while it runs */
|
||||
static unsigned saw_room_wait, saw_full;
|
||||
static void watch_far_waits(void)
|
||||
@@ -281,11 +310,14 @@ int main(void)
|
||||
|
||||
/* ---- no way ---- */
|
||||
nacks = 0;
|
||||
i = (unsigned)pool[hera].n.mem[LOST];
|
||||
CHECK(is_empty(tell(99, "1 2 + .")) && ended == 0, "text for a node there is no way to is not done by anyone");
|
||||
CHECK(nacks == 1 && nack_from == 10 && nack_about == 99, "the console, which sent it, is told it was refused, by the node that could not pass it on: %d from %ld about %ld",
|
||||
nacks, (long)nack_from, (long)nack_about);
|
||||
CHECK(strcmp(tell(10, "(LOST) @ ."), "1 ") == 0, "and that node has counted it");
|
||||
CHECK(pool[hera].n.mem[LOST] == (v4_cell)i + 1, "and that node has counted it");
|
||||
CHECK(strcmp(tell(11, "(LOST) @ ."), "0 ") == 0 && strcmp(tell(12, "(LOST) @ ."), "0 ") == 0, "the others have lost nothing");
|
||||
i = (unsigned)pool[hera].n.mem[LOST];
|
||||
CHECK(told(10, ": TELL2 S\" 1 DROP\" 12 SEND ; TELL2") && pool[hera].n.mem[LOST] == (v4_cell)i + 1, "an answer that comes to a node that is not waiting for it is let go, and counted");
|
||||
|
||||
/* ---- waiting ---- */
|
||||
{
|
||||
@@ -326,8 +358,10 @@ int main(void)
|
||||
const char *r = tell(10, ": GO S\" 6 10 BURST 6 12 BURST\" 11 SEND S\" 6 11 BURST 6 10 BURST\" 12 SEND 6 11 BURST 6 12 BURST ; GO");
|
||||
CHECK(is_empty(r), "six messages from each node to each other, all at once: \"%s\"", r);
|
||||
CHECK(asleep(hera) && asleep(mid) && asleep(far), "all three come to rest");
|
||||
CHECK(strcmp(tell(10, "GOT @ . (LOST) @ ."), "12 1 ") == 0 && strcmp(tell(11, "GOT @ . (LOST) @ ."), "12 0 ") == 0 && strcmp(tell(12, "GOT @ . (LOST) @ ."), "12 0 ") == 0,
|
||||
"every one of them arrived, and none was let go: \"%s\"", printed);
|
||||
/* each has let go, and counted, the answers to what it sent, which it did not wait for: node 10 to its
|
||||
* 12 messages and its two texts, and two from before and the message for 99; the others to 12 */
|
||||
CHECK(strcmp(tell(10, "GOT @ . (LOST) @ ."), "12 17 ") == 0 && strcmp(tell(11, "GOT @ . (LOST) @ ."), "12 12 ") == 0 && strcmp(tell(12, "GOT @ . (LOST) @ ."), "12 13 ") == 0,
|
||||
"every one of them arrived; what each has counted is the answers it did not wait for: \"%s\"", printed);
|
||||
}
|
||||
|
||||
/* ---- THE FLOOD: more than the wires hold (MESH.md 7d.6, acceptance 2) ----
|
||||
@@ -353,16 +387,11 @@ int main(void)
|
||||
lost += pool[k].n.mem[LOST];
|
||||
refused += pool[k].n.mem[REFUSED];
|
||||
}
|
||||
lost -= 1; /* the one for node 99, earlier */
|
||||
printf(" the flood: %ld sends tried, %ld on a wire, %ld refused at once; %ld done; (LOST) %ld, (REFUSED) %ld in all\n", tried, sent, tried - sent, got, lost, refused);
|
||||
CHECK(tried > sent && sent >= got && got > 0, "there was not room for all of them: some were refused at once");
|
||||
/* Every message that got on a wire and was let go on its way is counted exactly twice: once in (LOST)
|
||||
* where it was let go, and once where its NACK ended -- in (REFUSED) at the node it was for, or in
|
||||
* (LOST) if it could not go either. Every SEND refused at once is counted once, in (REFUSED). So
|
||||
* (LOST) + (REFUSED) - the refused at once is twice the messages let go; and those are the messages
|
||||
* the nodes sent each other that were not done, and answers to them. */
|
||||
CHECK((lost + refused - (tried - sent)) % 2 == 0 && lost + refused - (tried - sent) >= 2 * (sent - got),
|
||||
"every message sent was done or was counted: %ld on a wire, %ld done; (LOST) + (REFUSED) %ld, of which %ld were refused at once", sent, got, lost + refused, tried - sent);
|
||||
/* A SEND refused at once ends the word that sends, so the flood holds itself back: what got on a wire
|
||||
* is what there was room for. (LOST) counts, with what was let go on the way, every answer that
|
||||
* came to a node that did not wait for it, so it is not a count of what was lost. */
|
||||
CHECK(tried > sent && sent >= got && got > 0 && refused >= tried - sent, "there was not room for all of them: some were refused at once, and counted");
|
||||
CHECK(strcmp(tell(12, "7 8 * ."), "56 ") == 0 && printed_from == 12, "and the nodes go on as before");
|
||||
}
|
||||
|
||||
@@ -413,6 +442,10 @@ int main(void)
|
||||
c = clock_of(far);
|
||||
for (i = 0; i < 500; i++) (void)v4_fabric_step(&f);
|
||||
CHECK(asleep(far) && clock_of(far) == c, "node 12 is asleep, not going round: it owes nothing");
|
||||
/* nothing but the text goes on the wire: not what lies after it in the node's memory */
|
||||
(void)tell(12, ": S5 S\" ABCDEXYZ\" DROP 5 11 SEND ; S5");
|
||||
CHECK(coming_to(mid, 3)->used == 8 + 9 && coming_to(mid, 3)->cell[(coming_to(mid, 3)->head + 16u) % V4_WIRE_CELLS] == 'E',
|
||||
"five characters sent of eight: the last word on the wire holds the fifth and nothing else: %lx", (unsigned long)coming_to(mid, 3)->cell[(coming_to(mid, 3)->head + 16u) % V4_WIRE_CELLS]);
|
||||
CHECK(strcmp(tell(12, "7 8 * ."), "56 ") == 0 && printed_from == 12 && strcmp(tell(10, "1 2 + ."), "3 ") == 0, "and it and node 10 answer: \"%s\"", printed);
|
||||
}
|
||||
|
||||
@@ -472,7 +505,24 @@ int main(void)
|
||||
CHECK(strcmp(tell(12, "7 8 * ."), "56 ") == 0 && printed_from == 12 && asleep(far) && asleep(hera), "told another way to the console, it goes on: \"%s\"", printed);
|
||||
}
|
||||
|
||||
/* ---- a line from the console breaks a wait for room (MESH.md 7d.4) ---- */
|
||||
/* ---- NOTHING BREAKS A WAIT FOR ROOM TO PRINT (MESH.md 7d.4, ruled 2026-10-08) ----
|
||||
* A line typed ahead while a text prints more than the wire holds does not cut the printing short. */
|
||||
CHECK(triangle(), "the same made afresh");
|
||||
{
|
||||
static char want[32768];
|
||||
size_t at = 0;
|
||||
for (i = 0; i < 3000; i++) at += (size_t)snprintf(want + at, sizeof want - at, "%u ", i);
|
||||
snprintf(want + at, sizeof want - at, "56 ");
|
||||
CHECK(told(12, ": P 3000 0 DO I . LOOP ;") && told(10, ": SLOW 600000 0 DO LOOP ;"), "node 12 has a word that prints 3000 numbers, and node 10 one that takes a while");
|
||||
forget();
|
||||
saw_room_wait = 0; watch = watch_far_waits;
|
||||
CHECK(put_text(10, "SLOW") && put_text(12, "P") && put_text(12, "7 8 * .") && put_text(10, "SLOW") && put_text(10, "SLOW") && run(),
|
||||
"node 12 is typed two lines, the first of which prints more than the wire holds, while node 10 is busy");
|
||||
watch = 0;
|
||||
CHECK(saw_room_wait > 0 && strstr(printed, "Interrupted") == NULL, "node 12 slept for room to print with the second line waiting on its wire, and was not interrupted");
|
||||
CHECK(strcmp(printed, want) == 0 && ended == 5 && pool[far].n.mem[LOST] == 0, "the first text printed everything, and then the second line was done: %u characters of %u, %d ended", printed_len, (unsigned)strlen(want), ended);
|
||||
}
|
||||
/* and with a stuck node on the way, where the room never comes, text for the node is left on its wire */
|
||||
CHECK(triangle(), "the same made afresh");
|
||||
{
|
||||
CHECK(told(12, ": P 3000 0 DO I . LOOP ;") && told(12, "NO-ROUTES 2 DEFAULT-ROUTE 10 3 ROUTE"), "node 12's way to the console is by node 11 again");
|
||||
@@ -480,20 +530,131 @@ int main(void)
|
||||
(void)tell(11, ": C BEGIN 0 UNTIL ; C");
|
||||
(void)tell(12, "P");
|
||||
CHECK(ended == 0 && waits_for_room(far) && pool[far].n.mem[NODE_ERROR] == 0, "node 11 is stuck, and node 12 sleeps for room to print");
|
||||
(void)tell(11, "1 DROP");
|
||||
(void)tell(10, ": T12 S\" 1 DROP\" 12 SEND ; T12");
|
||||
CHECK(waits_for_room(far) && pool[far].n.mem[NODE_ERROR] == 0 && coming_to(far, 3)->used == 9,
|
||||
"text for node 12 from another node does not break the wait: it is left on the wire, and node 12 sleeps on: %u cells", coming_to(far, 3)->used);
|
||||
(void)tell(12, "65 EMIT");
|
||||
CHECK(pool[far].n.mem[NODE_ERROR] == 21 && waits_for_room(far) && coming_to(far, 3)->used == 18,
|
||||
"text for it from its console does: the text it was doing ends in error 21, Interrupted; the line that broke it stays on the wire: error %ld, %u cells",
|
||||
CHECK(waits_for_room(far) && pool[far].n.mem[NODE_ERROR] == 0 && pool[far].n.mem[QUIET] == 0 && coming_to(far, 3)->used == 18,
|
||||
"text for node 12, from another node and from its console, is left on the wire: its text is not ended, and it sleeps on: error %ld, %u cells",
|
||||
(long)pool[far].n.mem[NODE_ERROR], coming_to(far, 3)->used);
|
||||
(void)tell(12, "66 EMIT");
|
||||
CHECK(pool[far].n.mem[NODE_ERROR] == 21 && waits_for_room(far) && coming_to(far, 3)->used == 27 && pool[far].n.mem[OUT_PTR] <= OUT_W + OUT_CELLS,
|
||||
"its message has still to wait for the room, and is not broken a second time: MESH.md 7d.6");
|
||||
CHECK(strcmp(tell(10, "1 2 + ."), "3 ") == 0, "node 10 answers meanwhile");
|
||||
}
|
||||
|
||||
/* ---- PRINTING THROUGH A MIDDLE NODE IS NOT LOST (ruled 2026-10-08) ----
|
||||
* In the row, what node 12 prints goes by node 11 to node 10, which is busy. The wire from 11 to 10
|
||||
* fills; node 11 does not let go what it has no room to pass on: it leaves it on the wire from 12, which
|
||||
* fills in its turn, and node 12 waits. */
|
||||
CHECK(row(), "a row made afresh");
|
||||
{
|
||||
static char want[32768];
|
||||
size_t at = 0;
|
||||
for (i = 0; i < 3000; i++) at += (size_t)snprintf(want + at, sizeof want - at, "%u ", i);
|
||||
CHECK(told(12, ": P 3000 0 DO I . LOOP ;") && told(10, ": SLOW 600000 0 DO LOOP ;"), "node 12 has a word that prints 3000 numbers, and node 10 one that takes a while");
|
||||
forget();
|
||||
saw_room_wait = 0; saw_mid_hold = 0; watch = watch_mid_holds;
|
||||
CHECK(put_text(12, "P") && put_text(10, "SLOW") && put_text(10, "SLOW") && put_text(10, "SLOW") && put_text(10, "SLOW") && run(),
|
||||
"node 12 prints them while node 10 is busy with one line after another");
|
||||
watch = 0;
|
||||
CHECK(saw_mid_hold > 0, "node 11 left output on the wire it came by, for want of room, and slept for room");
|
||||
CHECK(strcmp(printed, want) == 0, "every number arrived, in order: %u characters of %u", printed_len, (unsigned)strlen(want));
|
||||
CHECK(ended == 5 && ended_at <= printed_len && pool[far].n.mem[LINE_STATUS] == 1 && pool[hera].n.mem[LOST] == 0 && pool[mid].n.mem[LOST] == 0 && pool[far].n.mem[LOST] == 0 && pool[far].n.mem[REFUSED] == 0,
|
||||
"and then how the text ended; nothing was let go anywhere: %ld %ld %ld", (long)pool[hera].n.mem[LOST], (long)pool[mid].n.mem[LOST], (long)pool[far].n.mem[LOST]);
|
||||
CHECK(asleep(hera) && asleep(mid) && asleep(far) && pool[mid].n.mem[H_PORT] == -1, "all three sleep, node 11 for a message and not for room");
|
||||
|
||||
/* the middle node removed while it holds output: node 12's text ends in an error; node 10 goes on */
|
||||
CHECK(row() && told(12, ": P 3000 0 DO I . LOOP ;") && told(10, ": SLOW 600000 0 DO LOOP ;"), "the same again");
|
||||
forget();
|
||||
CHECK(put_text(12, "P") && put_text(10, "SLOW") && put_text(10, "SLOW") && put_text(10, "SLOW") && put_text(10, "SLOW") && run_until(mid_holds, 20000000),
|
||||
"node 11 is holding output, asleep for room");
|
||||
CHECK(v4_fabric_remove(&f, mid) == &pool[1] && run(), "node 11 is removed, and the two that are left come to rest");
|
||||
CHECK(asleep(far) && pool[far].n.mem[NODE_ERROR] == 18 && pool[far].n.mem[LINE_STATUS] == 2 && pool[far].n.mem[KEEP] == -1, "node 12's text has ended in error 18, No one on that port, and it sleeps");
|
||||
CHECK(strncmp(printed, want, printed_len) == 0 && strcmp(tell(10, "1 2 + ."), "3 ") == 0 && asleep(hera), "what had arrived was in order, and node 10 goes on");
|
||||
|
||||
/* the node that prints removed while the middle node holds its output: what is on the wires arrives */
|
||||
CHECK(row() && told(12, ": P 3000 0 DO I . LOOP ;") && told(10, ": SLOW 600000 0 DO LOOP ;"), "and again");
|
||||
forget();
|
||||
CHECK(put_text(12, "P") && put_text(10, "SLOW") && put_text(10, "SLOW") && put_text(10, "SLOW") && put_text(10, "SLOW") && run_until(mid_holds, 20000000), "node 11 is holding output again");
|
||||
CHECK(v4_fabric_remove(&f, far) == &pool[2] && run(), "node 12 is removed, and the two that are left come to rest");
|
||||
CHECK(asleep(mid) && asleep(hera) && pool[mid].n.mem[H_PORT] == -1 && coming_to(hera, 2)->used == 0, "node 11 sleeps, holding nothing; nothing is left on the wire to node 10");
|
||||
CHECK(printed_len > 0 && strncmp(printed, want, printed_len) == 0 && ended >= 4, "what node 12 had printed before it went arrived, in order: %u characters", printed_len);
|
||||
CHECK(strcmp(tell(11, "20 22 + ."), "42 ") == 0 && strcmp(tell(10, "1 2 + ."), "3 ") == 0, "and nodes 10 and 11 go on");
|
||||
}
|
||||
|
||||
/* ---- A TAKE THAT DOES NOT TAKE (fix round 1, F1) ----
|
||||
* The wire is cut at every step from a message's coming to its being taken. If it was still on the wire
|
||||
* when the wire was cut it was not taken, and nothing may be done for it: not the text before it run
|
||||
* again, not a refusal counted, not a wait ended with an old answer, not a way forgotten. */
|
||||
{
|
||||
unsigned k, cut_before = 0, cut_after = 0, wrong = 0;
|
||||
CHECK(row() && told(12, "VARIABLE N : T1 1 N +! ; : T2 10 N +! ;") && told(12, "T1") && put_text(12, "T2") && run_until(far_has_one, 1000000), "text has come to node 12's wire");
|
||||
keep();
|
||||
for (k = 0; k < 200; k++) { /* text, taken by (IDLE) */
|
||||
int there;
|
||||
v4_cell lost;
|
||||
put_back();
|
||||
lost = pool[far].n.mem[LOST];
|
||||
for (i = 0; i < k; i++) { (void)v4_fabric_step(&f); (void)console_takes(); }
|
||||
there = far_has_one();
|
||||
(void)v4_fabric_unwire(&f, mid, 3);
|
||||
if (!run() || !asleep(far) || !v4_fabric_wire(&f, mid, 3, far, 2)) { wrong += 1000; break; }
|
||||
if (there) cut_before++; else { if (!cut_after && getenv("V4_MESH_STUCK")) printf(" first cut after the take: %u steps\n", k); cut_after++; }
|
||||
if (strcmp(tell(12, "N @ ."), there ? "1 " : "11 ") != 0 || pool[far].n.mem[LOST] != lost + (there ? 0 : 1)) {
|
||||
if (!wrong) printf(" cut %u steps after the text came, it %s on the wire: N is %s, %ld counted lost\n", k, there ? "still" : "no longer", printed, (long)(pool[far].n.mem[LOST] - lost));
|
||||
wrong++;
|
||||
}
|
||||
}
|
||||
CHECK(wrong == 0 && cut_before > 20 && cut_after > 0, "text: cut before it was taken (%u times) nothing was done; cut after (%u), the text was done once, and its answer counted: %u wrong", cut_before, cut_after, wrong);
|
||||
|
||||
cut_before = cut_after = wrong = 0;
|
||||
CHECK(row() && told(12, ": N77 S\" 1 DROP\" 77 SEND ;") && put_text(12, "N77") && run_until(far_has_nack, 1000000), "a NACK has come to node 12's wire");
|
||||
keep();
|
||||
for (k = 0; k < 200; k++) { /* a NACK, taken by (IDLE) */
|
||||
int there;
|
||||
v4_cell refused;
|
||||
put_back();
|
||||
refused = pool[far].n.mem[REFUSED];
|
||||
for (i = 0; i < k; i++) { (void)v4_fabric_step(&f); (void)console_takes(); }
|
||||
there = far_has_nack();
|
||||
(void)v4_fabric_unwire(&f, mid, 3);
|
||||
if (!run() || !asleep(far)) { wrong += 1000; break; }
|
||||
if (there) cut_before++; else { if (!cut_after && getenv("V4_MESH_STUCK")) printf(" first cut after the take: %u steps\n", k); cut_after++; }
|
||||
if (pool[far].n.mem[REFUSED] != refused + (there ? 0 : 1)) wrong++;
|
||||
}
|
||||
CHECK(wrong == 0 && cut_before > 20 && cut_after > 0, "a NACK: cut before it was taken (%u times) no refusal was counted; cut after (%u), one: %u wrong", cut_before, cut_after, wrong);
|
||||
|
||||
cut_before = cut_after = wrong = 0;
|
||||
CHECK(row() && told(12, ": Q S\" 1 DROP\" 11 SEND 11 AWAIT 7 + . ;") && put_text(12, "Q") && run_until(far_awaits_and_has_one, 1000000), "an answer has come to the wire of node 12, which waits for it");
|
||||
keep();
|
||||
for (k = 0; k < 200; k++) { /* an answer, taken by AWAIT */
|
||||
int there;
|
||||
put_back();
|
||||
for (i = 0; i < k; i++) { (void)v4_fabric_step(&f); (void)console_takes(); }
|
||||
there = far_has_one();
|
||||
(void)v4_fabric_unwire(&f, mid, 3);
|
||||
if (!run() || !asleep(far)) { wrong += 1000; break; }
|
||||
if (there) cut_before++; else { if (!cut_after && getenv("V4_MESH_STUCK")) printf(" first cut after the take: %u steps\n", k); cut_after++; }
|
||||
/* not taken: it waits on. Taken: the wait ended with the answer, 1, and "8 " was printed -- to a wire with no one on it. */
|
||||
if (there ? !(pool[far].n.mem[AWAIT_FROM] == 11 && pool[far].n.mem[QUIET] == 0) : !(pool[far].n.mem[AWAIT_FROM] == 0 && pool[far].n.mem[QUIET] == 1)) wrong++;
|
||||
}
|
||||
CHECK(wrong == 0 && cut_before > 5 && cut_after > 0, "an answer: cut before it was taken (%u times) the wait went on; cut after (%u), it ended: %u wrong", cut_before, cut_after, wrong);
|
||||
|
||||
cut_before = cut_after = wrong = 0;
|
||||
CHECK(row() && told(12, "55 2 ROUTE 44 2 ROUTE") && told(11, "55 12 GONE") && told(12, "55 2 ROUTE") && told(11, ": G44 44 12 GONE ;") && pool[far].n.mem[ROUTE_COUNT] == 2
|
||||
&& put_text(11, "G44") && run_until(far_has_one, 1000000), "a GONE from its centre has come to node 12's wire");
|
||||
keep();
|
||||
for (k = 0; k < 200; k++) { /* a GONE, taken by (IDLE): node 11 is node 12's centre */
|
||||
int there;
|
||||
v4_cell ways;
|
||||
put_back();
|
||||
ways = pool[far].n.mem[ROUTE_COUNT];
|
||||
for (i = 0; i < k; i++) { (void)v4_fabric_step(&f); (void)console_takes(); }
|
||||
there = far_has_one();
|
||||
(void)v4_fabric_unwire(&f, mid, 3);
|
||||
if (!run() || !asleep(far)) { wrong += 1000; break; }
|
||||
if (there) cut_before++; else { if (!cut_after && getenv("V4_MESH_STUCK")) printf(" first cut after the take: %u steps\n", k); cut_after++; }
|
||||
if (pool[far].n.mem[ROUTE_COUNT] != ways - (there ? 0 : 1)) wrong++;
|
||||
}
|
||||
CHECK(wrong == 0 && cut_before > 20 && cut_after > 0, "a GONE: cut before it was taken (%u times) no way was forgotten; cut after (%u), one: %u wrong", cut_before, cut_after, wrong);
|
||||
}
|
||||
|
||||
/* ---- Review Focus 4: THE ANSWER HAS ROOM KEPT ----
|
||||
* Node 11 sends the console 127 short messages by way of node 12, which passes them on to the wire to
|
||||
* node 10 while node 10 is busy: that wire is then full but for 8 cells. Node 10 has sent node 12 text
|
||||
@@ -508,7 +669,7 @@ int main(void)
|
||||
(void)tell(10, "G");
|
||||
watch = 0;
|
||||
CHECK(saw_full > 0, "node 12 slept for room to print with the wire to node 10 holding exactly the 8 cells kept for its answer (%u, %u)", saw_room_wait, saw_full);
|
||||
CHECK(strcmp(printed, "A1 ") == 0 && pool[far].n.mem[LOST] == 0 && pool[hera].n.mem[LOST] == 0,
|
||||
CHECK(strcmp(printed, "A1 ") == 0 && pool[far].n.mem[LOST] == 0,
|
||||
"what it printed arrived, and then how the text ended, which node 10 waited for: the answer was not refused: \"%s\"", printed);
|
||||
CHECK(asleep(hera) && asleep(mid) && asleep(far) && pool[far].n.mem[KEEP] == -1, "all three sleep, and node 12 keeps no room");
|
||||
}
|
||||
@@ -579,6 +740,16 @@ int main(void)
|
||||
(void)tell(11, "33 AWAIT .");
|
||||
CHECK(ended == 0 && told(10, "44 11 GONE") && pool[mid].n.mem[AWAIT_FROM] == 33 && asleep(mid), "a GONE from its centre about another node does not end a wait");
|
||||
}
|
||||
/* a GONE that says it is from the centre but comes by another port is not believed (MESH.md 7b.4) */
|
||||
CHECK(triangle(), "a row made afresh, with nodes 10 and 12 wired straight to each other");
|
||||
{
|
||||
v4_cell ways;
|
||||
CHECK(told(11, "44 3 ROUTE") && told(12, ": FORGE 10 (ME) ! 44 11 GONE 12 (ME) ! ;"), "node 11 has a way to 44; node 12 has a word that sends a GONE saying it is from node 10");
|
||||
ways = pool[mid].n.mem[ROUTE_COUNT];
|
||||
CHECK(told(12, "FORGE") && asleep(mid) && coming_to(mid, 3)->used == 0 && pool[mid].n.mem[ROUTE_COUNT] == ways,
|
||||
"it comes to node 11 by the wire from node 12, not by its centre's: it is not believed, and the way stays: %ld ways of %ld", (long)pool[mid].n.mem[ROUTE_COUNT], (long)ways);
|
||||
CHECK(told(10, "44 11 GONE") && pool[mid].n.mem[ROUTE_COUNT] == ways - 1, "from node 10, by its centre's port, it is");
|
||||
}
|
||||
/* interrupted: text from the console */
|
||||
CHECK(row(), "a row made afresh");
|
||||
{
|
||||
@@ -626,6 +797,7 @@ int main(void)
|
||||
* how it ended. It is looked at once an instruction word, so it is the least that was used; that 28
|
||||
* values may wait is measured in test_host_quit.c and, for passing on and refusing, in test_host_depth.c.
|
||||
* The return stack is empty when a text ends; (FINISH)'s wait for room goes seven deep on it. */
|
||||
CHECK(stale_out == 0, "no message of what a node printed carried anything past its last character: %u did", stale_out);
|
||||
printf(" between texts the nucleus used at most %u cells of the data stack and %u entries of the return stack\n", between_data, between_ret);
|
||||
CHECK(between_data <= 4 && between_ret <= 7, "between texts: four cells of the data stack, and seven return entries");
|
||||
|
||||
|
||||
@@ -237,7 +237,8 @@ static int wire_do(void)
|
||||
if (!tx) break;
|
||||
if (a < 0 || a > (v4_cell)V4_WIRE_CELLS) { how = V4_WIRE_NO_ROOM; break; }
|
||||
if (v4_wire_room(tx, (unsigned)a) == V4_WIRE_DONE) { how = V4_WIRE_DONE; break; }
|
||||
if (!wire_in.arrived) return 0;
|
||||
if (!wire_in.arrived && !wire_out.freed) return 0;
|
||||
wire_out.freed = 0;
|
||||
how = V4_WIRE_NO_ROOM;
|
||||
break;
|
||||
case 10: if (tx) how = a < 0 ? V4_WIRE_NOT_A_MESSAGE : a <= (v4_cell)V4_WIRE_CELLS ? v4_wire_room(tx, (unsigned)a) : V4_WIRE_NO_ROOM; break;
|
||||
|
||||
@@ -490,7 +490,8 @@ int main(void)
|
||||
CHECK(strcmp(tell(11, ": X S\" 66 EMIT\" 14 SEND ; X"), "B") == 0 && printed_from == 14, "and to the one across from it, which it is not wired to: \"%s\"", printed);
|
||||
CHECK(clock_of(10) > hera, "that went by a node in between");
|
||||
}
|
||||
CHECK(strcmp(tell(10, "(LOST) @ ."), "0 ") == 0 && strcmp(tell(11, "(LOST) @ ."), "0 ") == 0 && strcmp(tell(14, "(LOST) @ ."), "0 ") == 0, "nothing was let go");
|
||||
CHECK(strcmp(tell(10, "(LOST) @ ."), "0 ") == 0 && strcmp(tell(11, "(LOST) @ ."), "2 ") == 0 && strcmp(tell(14, "(LOST) @ ."), "0 ") == 0,
|
||||
"nothing was let go but the two answers that came back to node 11, which did not wait for them: \"%s\"", printed);
|
||||
|
||||
/* ---- they share the store (acceptance 3) ---- */
|
||||
step_limit = 30000000ul;
|
||||
@@ -514,7 +515,7 @@ int main(void)
|
||||
CHECK(told(10, ": GO S\" W\" 12 SEND W ; GO"), "Hera sets node 12 writing and writes her own meanwhile: \"%s\"", printed);
|
||||
for (i = 0; i < 10 && common_dev[(12 + i) * V4_BLOCK_BYTES] == 'a' && common_dev[(22 + i) * V4_BLOCK_BYTES] == 'b'; i++) { }
|
||||
CHECK(i == 10, "all twenty blocks reached the store: the first that did not is %u", i);
|
||||
CHECK(strcmp(tell(10, "(LOST) @ ."), "0 ") == 0 && strcmp(tell(12, "(LOST) @ ."), "0 ") == 0, "and no message was let go");
|
||||
CHECK(strcmp(tell(10, "(LOST) @ ."), "1 ") == 0 && strcmp(tell(12, "(LOST) @ ."), "0 ") == 0, "and no message was let go but node 12's answer to Hera, who did not wait for it: \"%s\"", printed);
|
||||
CHECK(waiting(10) && waiting(11) && waiting(12) && waiting(13) && waiting(14), "all five are waiting at their ports again");
|
||||
|
||||
/* ---- Hera kills a node by number, and tells the others it is gone (MESH.md 7b, 7d) ----
|
||||
|
||||
Reference in New Issue
Block a user