fix(v4.0.0): step 6e final review -- texts begun in order; a word no one will read lets its writer go; a cut wire wakes a wait for room; a full console wire is a refusal, not a fault
The final review of step 6e, by a fresh reviewer told to break it, and what was ruled: fix them all, test first. Each has a test that failed before the mend. - Two texts from one node could be begun in the wrong order when room for their answers came between the node's look at the first and at the second. Texts whose answers go by the same port are begun in the order they came: (D-MASK), quit.v4. 102 of 700 timings were wrong before. - A word written with PORT! to a node that speaks whole messages, or to a console's port, blocked its writer for ever and cut off every node beyond it. It is error 18 now, whether that node is asleep, running or stuck: fabric.c, node.c. - A node asleep for room on one wire is woken when another of its wires is cut. - A line there was no room for on the lone node's wire was reported as a fault and ended the hosted program. It is Message refused, and the prompt comes back: V4_BOOT_LINE_REFUSED. hosted-check has the case; no bare-metal boot has typed it. - Ruled: text behind held output for the same port is refused though it would fit. MESH.md 7d.4 said otherwise and is corrected. - The removal test also cuts and re-makes a wire, and has an exchange in which output is held at a full wire: 26,814 disturbances in all. - MESH.md and v4/README.md no longer say that a stuck node holds up no other node, that only printing waits, or that no node is left waiting on one that is gone; 7d.6 lists the limits and what the products do that they did not before. make -C v4 test and sanitize at both widths, hosted-check, lint. Three bare-metal boots: logs/20261013-194827 (amd64), -195131 (aarch64), -195518 (riscv64). POST 538 of 538, word_count=317, dict_hash=0xe5a57f3eb72ff94a on all six; the three sessions are the same character for character. The boots were built with the working tree's uncommitted change to kernel/Makefile that adds the ISA to the kernel's version line. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
06da5700bc
commit
66b26f823c
+3
-1
@@ -90,6 +90,7 @@
|
||||
#define S_ON (BUF0_W - 38) /* and whether its mark has been put at its first message */
|
||||
#define KEEP (BUF0_W - 40) /* the port the answer to the text being done goes by, on whose wire 8 cells are kept for it; -1: none */
|
||||
#define DEFER (BUF0_W - 41) /* a port whose wire had no room for the answer to text that waits to be begun; -1: none */
|
||||
#define D_MASK (BUF0_W - 8) /* and a bit for each such port: text whose answer goes by one is not begun on this pass */
|
||||
#define IN_HDR (BUF0_W - 56) /* the seven words of the message a wire's mark is at */
|
||||
#define ONE (BUF0_W - 49) /* the one word of text of a NACK, a GONE or an answer, coming or going */
|
||||
#define OUT_HDR (BUF0_W - 112) /* the seven words of a message this node is putting on a wire */
|
||||
@@ -104,7 +105,7 @@
|
||||
#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; - 122 .. - 113; BUF0_W - 144 .. - 141 (what a flush makes zero after
|
||||
/* FREE, plain memory: BUF0_W - 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. */
|
||||
@@ -227,6 +228,7 @@ static int host_load(v4_text *tx, v4_node *n, const char *const *files, unsigned
|
||||
v4_text_constant(tx, "(S-ON)", S_ON);
|
||||
v4_text_constant(tx, "(KEEP)", KEEP);
|
||||
v4_text_constant(tx, "(DEFER)", DEFER);
|
||||
v4_text_constant(tx, "(D-MASK)", D_MASK);
|
||||
v4_text_constant(tx, "(IN)", IN_HDR);
|
||||
v4_text_constant(tx, "(ONE)", ONE);
|
||||
v4_text_constant(tx, "(OUT-HDR)", OUT_HDR);
|
||||
|
||||
@@ -350,6 +350,98 @@ int main(void)
|
||||
"a node that can take an error is let go, and the error is raised on it: its P is its handler");
|
||||
CHECK(!v4_node_port_gone(&ND(c)->n, 18), "a node that is not blocked at a port is not touched");
|
||||
|
||||
/* ---- A WORD WRITTEN TO WHAT WILL NOT READ ONE (found by the final review of step 6e) ----
|
||||
* A node that writes a word to a port is blocked until it is taken. Two things will never take it: a
|
||||
* device that takes no words -- one that speaks whole messages only -- and a node that is asleep at its
|
||||
* wires, which executes nothing until a message comes. The writer is let go, as from a port with no one
|
||||
* on it. A neighbour that is doing something else, or that the fabric has put to sleep, may yet read:
|
||||
* the writer waits. */
|
||||
{
|
||||
static v4_wire_queue wq[4];
|
||||
static const v4_device mute = { 0, 0, 0 }; /* it takes and gives no words */
|
||||
static const v4_cell one[1] = { 5 };
|
||||
static stream s;
|
||||
static v4_device giver;
|
||||
v4_fabric_init(&f, places, 4);
|
||||
v4_fabric_queues(&f, wq, 4);
|
||||
a = loaded(0); /* writes a word to its port 0, and then counts in OUT */
|
||||
LIT(PB + 0); O(BANG_B); LIT(7); O(STORE_B);
|
||||
LIT(OUT); O(BANG_A);
|
||||
{
|
||||
v4_cell again = v4_asm_label(&as);
|
||||
O(FETCH_A); LIT(1); O(ADD); O(STORE_A);
|
||||
v4_asm_branch(&as, V4_OP_JUMP, again);
|
||||
}
|
||||
CHECK(v4_asm_ok(&as) && v4_fabric_wire_device(&f, a, 0, &mute), "a writer, wired to a device that takes no words");
|
||||
(void)settle(100);
|
||||
CHECK(ND(a)->n.asking && ND(a)->n.mem[OUT] == 0, "it is blocked writing");
|
||||
v4_fabric_gone_error(&f, 18);
|
||||
(void)settle(100);
|
||||
CHECK(ND(a)->n.asking && ND(a)->n.faults == 0, "a bare node, which cannot take an error, waits there");
|
||||
v4_node_error_attach(&ND(a)->n, OUT + 20);
|
||||
v4_node_fault_attach(&ND(a)->n, 900);
|
||||
(void)v4_fabric_step(&f);
|
||||
CHECK(!ND(a)->n.asking && ND(a)->n.fault_kind == V4_FAULT_RAISED && ND(a)->n.fault_addr == 18 && ND(a)->n.mem[OUT + 20] == 18,
|
||||
"a node that can take one is let go: no one on that port");
|
||||
|
||||
v4_fabric_init(&f, places, 4);
|
||||
v4_fabric_queues(&f, wq, 4);
|
||||
v4_fabric_gone_error(&f, 18);
|
||||
a = loaded(0);
|
||||
LIT(PB + 0); O(BANG_B); LIT(7); O(STORE_B);
|
||||
wait_for_ever();
|
||||
b = loaded(1); /* reads a word from its port 5, and then sleeps at its wires */
|
||||
LIT(PB + 5); O(BANG_B); O(FETCH_B); O(DROP);
|
||||
LIT(P_DO); O(BANG_B); LIT(8); O(STORE_B);
|
||||
wait_for_ever();
|
||||
CHECK(v4_asm_ok(&as) && v4_fabric_wire(&f, a, 0, b, 3), "a writer wired to a node that is waiting to read another port");
|
||||
v4_node_error_attach(&ND(a)->n, OUT + 20);
|
||||
v4_node_fault_attach(&ND(a)->n, 900);
|
||||
(void)settle(200);
|
||||
CHECK(ND(a)->n.asking && ND(a)->n.faults == 0 && ND(b)->n.reading, "the neighbour is not asleep at its wires and may yet read: the writer waits");
|
||||
v4_fabric_sleep(&f, b);
|
||||
memset(&s, 0, sizeof s); s.words = one; s.count = 1;
|
||||
giver.give = stream_give; giver.take = 0; giver.self = &s;
|
||||
CHECK(v4_fabric_wire_device(&f, b, 5, &giver), "the neighbour is put to sleep by the fabric, and a device with a word for it is wired to its port 5");
|
||||
(void)settle(200);
|
||||
CHECK(ND(a)->n.asking && ND(a)->n.faults == 0 && !v4_node_doing(&ND(b)->n), "a neighbour the fabric has put to sleep takes nothing and does nothing: the writer waits");
|
||||
v4_fabric_wake(&f, b);
|
||||
for (i = 0; i < 200 && ND(a)->n.asking; i++) (void)v4_fabric_step(&f);
|
||||
CHECK(v4_node_doing(&ND(b)->n) && ND(b)->n.do_op == 8, "woken, the neighbour takes its word and sleeps at its wires");
|
||||
CHECK(!ND(a)->n.asking && ND(a)->n.fault_kind == V4_FAULT_RAISED && ND(a)->n.fault_addr == 18, "and the writer is let go: no one on that port will read a word");
|
||||
|
||||
/* nor does it matter that such a node is awake and running: once a node has asked for a whole message
|
||||
* it is not one that words are written to. A stuck node does not hold whoever writes it a word. */
|
||||
v4_fabric_init(&f, places, 4);
|
||||
v4_fabric_queues(&f, wq, 4);
|
||||
v4_fabric_gone_error(&f, 18);
|
||||
a = loaded(0);
|
||||
LIT(PB + 0); O(BANG_B); LIT(7); O(STORE_B);
|
||||
wait_for_ever();
|
||||
b = loaded(1); /* reads a word from its port 5, asks whether its wire 3 has room, and then counts for ever */
|
||||
LIT(PB + 5); O(BANG_B); O(FETCH_B); O(DROP);
|
||||
LIT(P_WIRE); O(BANG_B); LIT(3); O(STORE_B);
|
||||
LIT(P_A); O(BANG_B); LIT(8); O(STORE_B);
|
||||
LIT(P_DO); O(BANG_B); LIT(10); O(STORE_B);
|
||||
LIT(OUT); O(BANG_A);
|
||||
{
|
||||
v4_cell again = v4_asm_label(&as);
|
||||
O(FETCH_A); LIT(1); O(ADD); O(STORE_A);
|
||||
v4_asm_branch(&as, V4_OP_JUMP, again);
|
||||
}
|
||||
CHECK(v4_asm_ok(&as) && v4_fabric_wire(&f, a, 0, b, 3), "a writer wired to a node that has not yet asked anything of a wire");
|
||||
v4_node_error_attach(&ND(a)->n, OUT + 20);
|
||||
v4_node_fault_attach(&ND(a)->n, 900);
|
||||
(void)settle(200);
|
||||
CHECK(ND(a)->n.asking && ND(a)->n.faults == 0, "the writer waits");
|
||||
memset(&s, 0, sizeof s); s.words = one; s.count = 1;
|
||||
CHECK(v4_fabric_wire_device(&f, b, 5, &giver), "the neighbour is given its word");
|
||||
for (i = 0; i < 200 && ND(a)->n.asking; i++) (void)v4_fabric_step(&f);
|
||||
CHECK(!ND(a)->n.asking && ND(a)->n.fault_kind == V4_FAULT_RAISED && ND(a)->n.fault_addr == 18, "it asks for room, and the writer is let go");
|
||||
for (i = 0; i < 20; i++) (void)v4_fabric_step(&f);
|
||||
CHECK(!v4_node_doing(&ND(b)->n) && ND(b)->n.mem[OUT] > 0, "though that node is not asleep: it goes on counting");
|
||||
}
|
||||
|
||||
/* ---- asleep, awake, and removed, while looping; and a node born while others run ---- */
|
||||
v4_fabric_init(&f, places, 4);
|
||||
a = loaded(0); /* counts in OUT for ever */
|
||||
@@ -710,6 +802,40 @@ 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");
|
||||
|
||||
/* A node asleep for room on one wire is woken when another of its wires is cut, or the node on it
|
||||
* removed: it may be keeping something to put there, and must find out that it cannot (found by the
|
||||
* final review of step 6e: before, it slept on until something else happened). */
|
||||
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");
|
||||
CHECK(v4_fabric_unwire(&f, a, 1), "its wire 1 is cut");
|
||||
(void)v4_fabric_step(&f);
|
||||
CHECK(!ND(a)->n.doing && ND(a)->n.how == 1, "it wakes, told 1: the wire it named has no room, but something has changed");
|
||||
CHECK(ask(a, 1, 100, 0, 10) == 2, "it asks, and finds no one on port 1");
|
||||
CHECK(ask(a, 0, 100, 0, 9) == -99, "asleep for room on wire 0 again");
|
||||
for (i = 0; i < 100; i++) (void)v4_fabric_step(&f);
|
||||
CHECK(ND(a)->n.doing, "and it is not woken twice for the one cut");
|
||||
CHECK(v4_fabric_wire(&f, a, 1, c, 2), "the wire is made again");
|
||||
fill(f.place[a].wire[1].tx, 0);
|
||||
for (i = 0; i < 100; i++) (void)v4_fabric_step(&f);
|
||||
CHECK(ND(a)->n.doing, "a wire being made does not wake it");
|
||||
CHECK(v4_fabric_remove(&f, c) == &pool[2], "the node on wire 1 is removed");
|
||||
(void)v4_fabric_step(&f);
|
||||
CHECK(!ND(a)->n.doing && ND(a)->n.how == 1, "it wakes, told 1");
|
||||
ND(a)->n.doing = 0;
|
||||
v4_node_store(&ND(a)->n, P_DO, 8);
|
||||
CHECK(v4_fabric_wire(&f, a, 1, b, 4) && v4_fabric_unwire(&f, a, 1), "asleep for a message (8), with a wire made and cut");
|
||||
for (i = 0; i < 100; i++) (void)v4_fabric_step(&f);
|
||||
CHECK(ND(a)->n.doing, "a node asleep for a message is not woken by a cut: it keeps nothing");
|
||||
|
||||
/* 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);
|
||||
|
||||
+149
-27
@@ -262,35 +262,76 @@ static int answers(v4_cell node, unsigned id)
|
||||
size_t len = strlen(got);
|
||||
return len >= 2 && strcmp(got + len - 2, "3 ") == 0 && ended >= 1 && ended_from == node && at_rest(id);
|
||||
}
|
||||
/* The exchange that `text`, sent to `node`, begins is played from the mesh as kept, once for every step it
|
||||
* takes, with the node at place `gone` removed at that step. Every node left must come to rest, blocked in
|
||||
* nothing, and answer. With the middle node gone the far one is wired to the first, as whoever removed it
|
||||
* would, so that it can be asked. How many steps the exchange takes; how many removals went wrong, and
|
||||
* the first of them. */
|
||||
/* AN EXCHANGE, DISTURBED AT EVERY STEP. The exchange that start() begins is played from the mesh as kept:
|
||||
* once whole, to see how many steps it takes, and then once for every one of those steps with the mesh
|
||||
* disturbed at that step --
|
||||
* 1 node 11 removed, and node 12 then wired to node 10, as whoever removed it would, so that it can be asked;
|
||||
* 2 node 12 removed;
|
||||
* 3 the wire between nodes 11 and 12 cut and made again at once: what waited on it is let go.
|
||||
* After each, every node left must come to rest, asleep and blocked at no port, and answer. With `moments`
|
||||
* only the steps at which something the fabric keeps had changed are tried -- what is on a wire, or what a
|
||||
* node asks of one: for an exchange too long to disturb at every step. A removal between two such steps
|
||||
* finds the mesh as the one before it did.
|
||||
* How many steps the exchange takes; how many disturbances were tried; how many went wrong, and the first. */
|
||||
static v4_fabric_node now_pool[3];
|
||||
static v4_place now_places[3];
|
||||
static v4_wire_queue now_queues[QUEUES];
|
||||
static v4_fabric now_f;
|
||||
static unsigned removed_at_every_step(v4_cell node, const char *text, unsigned gone, unsigned *wrong, unsigned *first_wrong)
|
||||
static v4_cell start_node;
|
||||
static const char *start_text;
|
||||
static int start_plain(void) { return put_text(start_node, start_text); }
|
||||
static int hera_busy(void) { return pool[hera].n.mem[QUIET] == 0; }
|
||||
/* Text for node 12 that prints, with node 10 made busy and the wire from node 11 to node 10 full but for 7
|
||||
* cells: what node 12 prints, and how its text ended, are held by node 11, asleep for room (MESH.md 7d.4). */
|
||||
static int start_held(void)
|
||||
{
|
||||
unsigned long before;
|
||||
static v4_cell big[V4_WIRE_HEADER + V4_MSG_MAX_CHARS / 4u];
|
||||
v4_wire_queue *q = places[mid].wire[2].tx;
|
||||
unsigned k;
|
||||
int ok = put_text(12, "A") && put_text(10, "SLOW") && run_until(hera_busy, 5000); /* node 10 has passed the one on and begun the other */
|
||||
memset(big, 0, sizeof big);
|
||||
big[0] = CONSOLE_ID; big[1] = 12; big[2] = V4_MSG_OUTPUT; big[3] = 16; big[6] = 1024;
|
||||
for (k = 0; k < 3; k++) ok = ok && v4_wire_put(q, big, big + V4_WIRE_HEADER) == V4_WIRE_DONE;
|
||||
big[6] = 884;
|
||||
return ok && v4_wire_put(q, big, big + V4_WIRE_HEADER) == V4_WIRE_DONE && V4_WIRE_CELLS - q->used == 7;
|
||||
}
|
||||
/* what the fabric keeps, in a number: what every wire holds and what every node asks of one */
|
||||
static unsigned long fabric_mark(void)
|
||||
{
|
||||
unsigned long h = 1469598103UL;
|
||||
unsigned k;
|
||||
for (k = 0; k < 3; k++) if (v4_fabric_node_at(&f, k)) h = (h ^ (unsigned long)(pool[k].n.doing * 16 + (int)pool[k].n.do_op)) * 16777619UL;
|
||||
for (k = 0; k < QUEUES; k++) h = ((h ^ queues[k].used) * 16777619UL ^ queues[k].head) * 16777619UL;
|
||||
return h;
|
||||
}
|
||||
static unsigned disturbed_at_every_step(int (*start)(void), unsigned how, int moments, unsigned *tried, unsigned *wrong, unsigned *first_wrong)
|
||||
{
|
||||
unsigned long before, mark, last = 0;
|
||||
unsigned whole, s;
|
||||
*wrong = 0; *first_wrong = 0;
|
||||
*tried = 0; *wrong = 0; *first_wrong = 0;
|
||||
put_back();
|
||||
before = steps_run;
|
||||
if (!put_text(node, text) || !run()) { *wrong = 1; return 0; }
|
||||
saw_mid_hold = 0; watch = watch_mid_holds;
|
||||
if (!start() || !run()) { watch = 0; *wrong = 1; return 0; }
|
||||
watch = 0;
|
||||
whole = (unsigned)(steps_run - before);
|
||||
put_back();
|
||||
if (!put_text(node, text)) { *wrong = 1; return 0; }
|
||||
if (!start()) { *wrong = 1; return 0; }
|
||||
for (s = 0; s <= whole; s++) {
|
||||
int ok;
|
||||
memcpy(now_pool, pool, sizeof pool); memcpy(now_places, places, sizeof places); memcpy(now_queues, queues, sizeof queues); now_f = f; /* the exchange, s steps on */
|
||||
ok = v4_fabric_remove(&f, gone) == &pool[gone] && run();
|
||||
ok = ok && at_rest(hera) && (gone == mid ? at_rest(far) : at_rest(mid));
|
||||
if (ok && gone == mid) ok = v4_fabric_wire(&f, hera, 2, far, 2) && answers(12, far) && answers(10, hera);
|
||||
else if (ok) ok = answers(11, mid) && answers(10, hera);
|
||||
if (!ok) { if (!*wrong) *first_wrong = s; (*wrong)++; }
|
||||
memcpy(pool, now_pool, sizeof pool); memcpy(places, now_places, sizeof places); memcpy(queues, now_queues, sizeof queues); f = now_f;
|
||||
mark = fabric_mark();
|
||||
if (!moments || s == 0 || mark != last) {
|
||||
int ok;
|
||||
memcpy(now_pool, pool, sizeof pool); memcpy(now_places, places, sizeof places); memcpy(now_queues, queues, sizeof queues); now_f = f; /* the exchange, s steps on */
|
||||
if (how == 1) ok = v4_fabric_remove(&f, mid) == &pool[mid] && run() && at_rest(hera) && at_rest(far)
|
||||
&& v4_fabric_wire(&f, hera, 2, far, 2) && answers(12, far) && answers(10, hera);
|
||||
else if (how == 2) ok = v4_fabric_remove(&f, far) == &pool[far] && run() && at_rest(hera) && at_rest(mid) && answers(11, mid) && answers(10, hera);
|
||||
else ok = v4_fabric_unwire(&f, mid, 3) && v4_fabric_wire(&f, mid, 3, far, 2) && run() && at_rest(hera) && at_rest(mid) && at_rest(far)
|
||||
&& answers(12, far) && answers(11, mid) && answers(10, hera);
|
||||
(*tried)++;
|
||||
if (!ok) { if (!*wrong) *first_wrong = s; (*wrong)++; }
|
||||
memcpy(pool, now_pool, sizeof pool); memcpy(places, now_places, sizeof places); memcpy(queues, now_queues, sizeof queues); f = now_f;
|
||||
}
|
||||
last = mark;
|
||||
(void)v4_fabric_step(&f); (void)console_takes();
|
||||
}
|
||||
return whole;
|
||||
@@ -995,32 +1036,113 @@ int main(void)
|
||||
CHECK(between_data <= 4 && between_ret <= 7, "between texts: four cells of the data stack, and seven return entries");
|
||||
|
||||
|
||||
/* ---- A NODE REMOVED AT EVERY STEP (MESH.md 7d.7, acceptance 6) ----
|
||||
* Three exchanges, each played once whole and then once for every step it takes, with node 11 removed
|
||||
* at that step, and again with node 12: the nodes left come to rest, blocked in nothing, and answer.
|
||||
/* ---- A WORD WRITTEN TO A PORT, PORT!, HOLDS NO ONE (found by the final review of step 6e) ----
|
||||
* A node's neighbours speak whole messages and are asleep at their wires; so is its console. A word
|
||||
* written to one of them will never be read. The line that wrote it ends in an error and the node goes
|
||||
* on; before, it was blocked for ever, and every node beyond it was cut off. */
|
||||
CHECK(row(), "a row made afresh");
|
||||
step_cap = 2000000;
|
||||
CHECK(strstr(tell(10, "5 2 PORT! 65 EMIT"), "No one on that port") != NULL && strchr(printed, 'A') == NULL && ended == 1 && ended_how == V4_TEXT_ERROR && asleep(hera),
|
||||
"node 10 writes a word to node 11's port: its line ends No one on that port: \"%s\"", printed);
|
||||
CHECK(strcmp(tell(10, "1 2 + ."), "3 ") == 0 && strcmp(tell(11, "20 22 + ."), "42 ") == 0 && strcmp(tell(12, "7 8 * ."), "56 ") == 0, "and all three answer");
|
||||
CHECK(strstr(tell(10, "5 1 PORT!"), "No one on that port") != NULL && ended_how == V4_TEXT_ERROR && strcmp(tell(10, "1 2 + ."), "3 ") == 0,
|
||||
"the same for a word written to the console's port: \"%s\"", printed);
|
||||
CHECK(strstr(tell(11, "5 3 PORT!"), "No one on that port") != NULL && ended_from == 11 && strcmp(tell(12, "7 8 * ."), "56 ") == 0 && strcmp(tell(11, "20 22 + ."), "42 ") == 0,
|
||||
"and for node 11 writing to node 12's: node 12 is not cut off");
|
||||
stuck = 1u << mid;
|
||||
(void)tell(11, ": C BEGIN 0 UNTIL ; C");
|
||||
CHECK(ended == 0 && !asleep(mid), "node 11 is stuck in a loop: it does not sleep");
|
||||
CHECK(strstr(tell(10, "5 2 PORT!"), "No one on that port") != NULL && ended_how == V4_TEXT_ERROR && strcmp(tell(10, "1 2 + ."), "3 ") == 0,
|
||||
"a word node 10 writes to it is refused all the same, and node 10 can be typed to: \"%s\"", printed);
|
||||
stuck = 0;
|
||||
step_cap = 20000000;
|
||||
|
||||
/* ---- TWO TEXTS FROM ONE NODE ARE BEGUN IN THE ORDER THEY CAME (acceptance 8; found by the final review) ----
|
||||
* Node 12 has two texts from node 10 on its wire, and the wire their answers go by has 7 cells free:
|
||||
* neither can be begun. Node 11, which was not taking, then takes a message off that wire -- at every
|
||||
* step in turn, so that the room comes before node 12 looks at the first text, between its look at the
|
||||
* first and at the second, and after both. Whenever it comes, the first is begun first. */
|
||||
{
|
||||
static v4_cell big[V4_WIRE_HEADER + V4_MSG_MAX_CHARS / 4u], hd[V4_WIRE_HEADER];
|
||||
static v4_message m;
|
||||
unsigned k, wrong = 0, first_wrong = 0, undone = 0;
|
||||
v4_wire_queue *out, *in;
|
||||
CHECK(row() && told(12, ": X1 65 EMIT ; : X2 66 EMIT ;"), "a row made afresh; node 12 has two words that print");
|
||||
v4_fabric_sleep(&f, mid); /* node 11 takes nothing for now */
|
||||
out = places[far].wire[2].tx; in = places[far].wire[2].rx;
|
||||
memset(big, 0, sizeof big);
|
||||
big[0] = 1; big[1] = 12; big[2] = V4_MSG_OUTPUT; big[3] = 16;
|
||||
big[6] = 1024; for (i = 0; i < 3; i++) (void)v4_wire_put(out, big, big + V4_WIRE_HEADER);
|
||||
big[6] = 884; (void)v4_wire_put(out, big, big + V4_WIRE_HEADER);
|
||||
CHECK(V4_WIRE_CELLS - out->used == 7, "the wire from node 12 to node 11 has 7 cells free: %u", V4_WIRE_CELLS - out->used);
|
||||
CHECK(v4_message_text(&m, 12, 10, V4_MSG_TEXT, "X1", 2) && (m.word[3] = 16, m.word[5] = 1, v4_wire_put(in, m.word, m.word + V4_MSG_HEADER) == V4_WIRE_DONE)
|
||||
&& v4_message_text(&m, 12, 10, V4_MSG_TEXT, "X2", 2) && (m.word[3] = 16, m.word[5] = 2, v4_wire_put(in, m.word, m.word + V4_MSG_HEADER) == V4_WIRE_DONE),
|
||||
"two texts from node 10 are on the wire to node 12, numbered 1 and 2");
|
||||
keep();
|
||||
for (k = 0; k < 700; k++) {
|
||||
unsigned began = 0, seq[2] = { 0, 0 };
|
||||
put_back();
|
||||
for (i = 0; i < 100000 && began < 2; i++) {
|
||||
const v4_node *n = &pool[far].n;
|
||||
int kept = n->mem[KEEP] >= 0;
|
||||
if (i == k) { v4_wire_first(out); (void)v4_wire_look(out, hd); (void)v4_wire_drop(out); }
|
||||
(void)v4_fabric_step(&f); (void)console_takes();
|
||||
if (!kept && n->mem[KEEP] >= 0) seq[began++] = (unsigned)n->mem[MSG + 5];
|
||||
}
|
||||
if (began < 2) undone++;
|
||||
else if (seq[0] != 1 || seq[1] != 2) { if (!wrong) first_wrong = k; wrong++; }
|
||||
}
|
||||
CHECK(undone == 0 && wrong == 0, "whenever the room came, both texts were done, the first first: %u not done, %u in the wrong order, the first when it came at step %u", undone, wrong, first_wrong);
|
||||
}
|
||||
|
||||
/* ---- A NODE REMOVED, OR A WIRE CUT, AT EVERY STEP (MESH.md 7d.7, acceptance 6) ----
|
||||
* Three exchanges, each played once whole and then once for every step it takes with node 11 removed at
|
||||
* that step, again with node 12 removed, and again with the wire between them cut and made again: the
|
||||
* nodes left come to rest, blocked at nothing, and answer.
|
||||
* What is typed is a word defined beforehand, and so is what a node left is asked (Q, which is
|
||||
* `1 2 + .`): a line of several words is mostly the dictionary searched, thousands of steps in which the
|
||||
* mesh does nothing, and every one of them would be a removal played to the end. */
|
||||
* mesh does nothing, and every one of them would be a removal played to the end.
|
||||
* WHAT "ANSWERS" MEANS HERE. A node that was waiting for an answer from a node now removed, or for one
|
||||
* that was on a wire now cut, is still waiting when the mesh comes to rest: removing a node tells no one
|
||||
* (Hera's KILL does: test_host_unit.c). Its wait is ended by the line it is then typed, "Interrupted",
|
||||
* and it answers. */
|
||||
{
|
||||
static const struct { const char *what, *set10, *set11, *set12; v4_cell node; const char *text; } ex[3] = {
|
||||
{ "text from the console for node 12, what it prints and how it ended", "1 DROP", "1 DROP", ": A 7 8 * . ;", 12, "A" },
|
||||
{ "node 11 SENDs node 12 text and waits for its answer", "1 DROP", ": T S\" Q\" 12 SEND 12 AWAIT . ;", "1 DROP", 11, "T" },
|
||||
{ "a message node 12 has no way for, and its NACK to node 10", "55 2 ROUTE : N S\" 1 DROP\" 55 SEND ;", "55 3 ROUTE", "NO-ROUTES 1 2 ROUTE 10 2 ROUTE 11 2 ROUTE", 10, "N" }
|
||||
};
|
||||
unsigned e, g, whole, wrong, first;
|
||||
static const char *const how_name[4] = { "", "node 11 removed", "node 12 removed", "the wire between nodes 11 and 12 cut and made again" };
|
||||
unsigned e, g, whole, tried, wrong, first, all = 0;
|
||||
step_cap = 2000000;
|
||||
for (e = 0; e < 3; e++) {
|
||||
CHECK(row() && told(10, ex[e].set10) && told(11, ex[e].set11) && told(12, ex[e].set12) && told(10, ASK) && told(11, ASK) && told(12, ASK), "a row made afresh for: %s", ex[e].what);
|
||||
step_cap = 2000000;
|
||||
keep();
|
||||
for (g = 1; g <= 2; g++) {
|
||||
whole = removed_at_every_step(ex[e].node, ex[e].text, g == 1 ? mid : far, &wrong, &first);
|
||||
CHECK(whole > 0 && wrong == 0, "%s, in %u steps: with node %u removed at each of them the rest come to rest and answer: %u did not, the first at step %u",
|
||||
ex[e].what, whole, 10u + g, wrong, first);
|
||||
start_node = ex[e].node; start_text = ex[e].text;
|
||||
for (g = 1; g <= 3; g++) {
|
||||
whole = disturbed_at_every_step(start_plain, g, 0, &tried, &wrong, &first);
|
||||
all += tried;
|
||||
CHECK(whole > 0 && wrong == 0, "%s, in %u steps: with %s at each of them the rest come to rest and answer: %u did not, the first at step %u",
|
||||
ex[e].what, whole, how_name[g], wrong, first);
|
||||
}
|
||||
}
|
||||
put_back();
|
||||
CHECK(is_empty(tell(10, "N")) && pool[hera].n.mem[REFUSED] == 1 && pool[far].n.mem[LOST] == 1, "the last of them, whole: node 12 let the message go and node 10 was told it was refused");
|
||||
|
||||
/* and with what node 12 prints held at node 11, which sleeps for room: at every step at which
|
||||
* anything on a wire, or what a node asks of one, has changed */
|
||||
CHECK(row() && told(12, ": A 7 8 * . ;") && told(10, ": SLOW 3000 0 DO LOOP ;") && told(10, ASK) && told(11, ASK) && told(12, ASK), "a row made afresh for output held at a full wire");
|
||||
step_cap = 2000000;
|
||||
keep();
|
||||
for (g = 1; g <= 3; g++) {
|
||||
whole = disturbed_at_every_step(start_held, g, 1, &tried, &wrong, &first);
|
||||
all += tried;
|
||||
CHECK(whole > 0 && saw_mid_hold > 0, "text for node 12 that prints, node 10 busy and the wire to it full: node 11 held what was printed, asleep for room, for %u of %u steps", saw_mid_hold, whole);
|
||||
CHECK(tried > 20 && wrong == 0, "with %s at each of the %u steps at which a wire or a node's asking changed, the rest come to rest and answer: %u did not, the first at step %u",
|
||||
how_name[g], tried, wrong, first);
|
||||
}
|
||||
printf(" a node removed or a wire cut: %u times in all\n", all);
|
||||
step_cap = 20000000;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user