fix(v4.0.0): step 6e review round 2 -- only the message looked at is taken, moved or dropped; the kernel's port has no queue in a fabric; an answer nobody awaits is not a loss

- Engine: a wire keeps whether the message at its mark is the one last
  looked at; a node's take, move and drop of any other answer 3 and write
  nothing (MESH.md 7d.3).  A wire cut and made again between a look and a
  take no longer has a NACK done as text, or 256 cells written where one
  was looked for.
- Engine: v4_fabric_wire_kernel wires a kernel to a port with no queues;
  a message for it is refused at once, error 18, on a mesh as on the
  products.
- The result of a drop is looked at; a take that does not take has the
  message dropped, or gone past: nothing goes round.
- An answer nobody waits for is let go uncounted; (LOST) counts what should
  have been delivered or acted on and was not.  The flood's account is
  exact.
- Error 18 from under a word that was printing empties the data stack, as
  a fault does: that word's own values are not left there.
- Tests: the take and drop windows cut at every step, with and without
  another message put; a refusal owed to a stuck node whose wire is full.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
rajames
2026-10-08 22:36:59 -04:00
co-authored by Claude Opus 5.5
parent 6a7d0aa27f
commit f1419e739c
15 changed files with 345 additions and 63 deletions
+76 -13
View File
@@ -165,6 +165,14 @@ static v4_cell ask(unsigned id, v4_cell wire, v4_cell a, v4_cell b, v4_cell op)
return n->doing ? -99 : v4_node_fetch(n, P_HOW);
}
/* The same for a take, a move or a drop, which are done only on the message
* last looked at: the node looks first, as a node does. */
static v4_cell seen(unsigned id, v4_cell wire, v4_cell a, v4_cell b, v4_cell op)
{
(void)ask(id, wire, 500, 0, 1);
return ask(id, wire, a, b, op);
}
int main(void)
{
unsigned a, b, c, i, steps;
@@ -507,6 +515,7 @@ int main(void)
LIT(P_DO); O(BANG_B); LIT(8); O(STORE_B);
LIT(P_HAVE); O(BANG_B); O(FETCH_B); LIT(OUT + 1); O(BANG_A); O(STORE_A);
LIT(P_A); O(BANG_B); LIT(200); O(STORE_B);
LIT(P_DO); O(BANG_B); LIT(1); O(STORE_B); /* it looks, and then takes what it looked at */
LIT(P_B); O(BANG_B); LIT(220); O(STORE_B);
LIT(P_DO); O(BANG_B); LIT(2); O(STORE_B);
LIT(P_HOW); O(BANG_B); O(FETCH_B); LIT(OUT); O(BANG_A); O(STORE_A);
@@ -573,6 +582,7 @@ int main(void)
for (i = 0; i < 7; i++) t[i] = ND(a)->n.mem[100 + i];
for (i = 0; i < 8; i++) t[8 + i] = ND(a)->n.mem[110 + i];
CHECK(v4_wire_put(f.place[a].wire[0].rx, t, t + 8) == V4_WIRE_DONE, "(a message waits)");
f.place[a].wire[0].rx->looked = 1; /* and has been looked at */
memcpy(snap, ND(a)->n.mem, sizeof snap);
memcpy(&before_rx, f.place[a].wire[0].rx, sizeof before_rx);
memcpy(&before_tx, f.place[a].wire[0].tx, sizeof before_tx);
@@ -592,6 +602,7 @@ int main(void)
for (i = 0; i < 7; i++) t[i] = ND(a)->n.mem[100 + i];
for (i = 0; i < 8; i++) t[8 + i] = ND(a)->n.mem[110 + i];
(void)v4_wire_put(f.place[a].wire[0].rx, t, t + 8);
f.place[a].wire[0].rx->looked = 1;
memcpy(snap, ND(a)->n.mem, sizeof snap);
memcpy(&before_rx, f.place[a].wire[0].rx, sizeof before_rx);
CHECK(ask(a, 0, 300, bad[k], 2) == 4 && memcmp(snap, ND(a)->n.mem, sizeof snap) == 0 && memcmp(&before_rx, f.place[a].wire[0].rx, sizeof before_rx) == 0,
@@ -621,9 +632,9 @@ int main(void)
CHECK(ask(a, 0, 200, 220, 7) == 0 && ask(a, 0, 200, 220, 1) == 0 && ND(a)->n.mem[200] == 200, "next: the second");
CHECK(ask(a, 0, 200, 220, 2) == 0 && msg_is(&ND(a)->n, 200, 220, 21, 2), "take of the second");
CHECK(ask(a, 0, 200, 220, 1) == 0 && ND(a)->n.mem[200] == 300, "the mark is at the third");
CHECK(ask(a, 0, 200, 220, 6) == 0 && ask(a, 0, 200, 220, 4) == 0 && ask(a, 0, 200, 220, 1) == 0 && ND(a)->n.mem[200] == 300, "first and drop: the first is gone, the third is left");
CHECK(ask(a, 0, 200, 220, 6) == 0 && seen(a, 0, 200, 220, 4) == 0 && ask(a, 0, 200, 220, 1) == 0 && ND(a)->n.mem[200] == 300, "first and drop: the first is gone, the third is left");
CHECK(ask(a, 0, 200, 220, 7) == 3 && ask(a, 0, 200, 220, 1) == 3, "next at the end answers 3 and the mark is at no message");
CHECK(ask(a, 0, 200, 220, 6) == 0 && ask(a, 0, 200, 220, 2) == 0 && msg_is(&ND(a)->n, 200, 220, 4, 3) && f.place[a].wire[0].rx->used == 0, "and the third is taken");
CHECK(ask(a, 0, 200, 220, 6) == 0 && seen(a, 0, 200, 220, 2) == 0 && msg_is(&ND(a)->n, 200, 220, 4, 3) && f.place[a].wire[0].rx->used == 0, "and the third is taken");
/* "which wires have a message": a bit to a wire; fetching it clears arrived */
v4_fabric_init(&f, places, 4);
@@ -720,9 +731,9 @@ int main(void)
for (i = 0; i < 100; i++) (void)v4_fabric_step(&f);
CHECK(ND(a)->n.doing, "and still blocked");
v4_wire_first(f.place[b].wire[3].rx);
CHECK(ask(b, 3, 400, 420, 4) == 0, "the reader drops the front message (which is 263 cells)");
CHECK(seen(b, 3, 400, 420, 4) == 0, "the reader drops the front message (which is 263 cells)");
while (ND(a)->n.doing && f.place[b].wire[3].rx->used > 0) {
(void)ask(b, 3, 400, 420, 4);
(void)seen(b, 3, 400, 420, 4);
}
CHECK(!ND(a)->n.doing && ND(a)->n.how == 0 && f.place[a].wire[0].tx->used + 200 <= V4_WIRE_CELLS, "woken, with how it went 0");
/* a small message in front: dropping it frees little, and the sleeper wants more than that leaves */
@@ -742,12 +753,12 @@ int main(void)
(void)v4_fabric_step(&f);
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(seen(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 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");
CHECK(seen(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);
@@ -775,14 +786,14 @@ int main(void)
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(seen(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);
(void)ask(c, 2, 500, 0, 1); 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);
@@ -802,7 +813,58 @@ int main(void)
(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");
CHECK(seen(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");
/* TAKE, MOVE AND DROP ARE ABOUT THE MESSAGE LAST LOOKED AT (MESH.md 7d.3). A node looks, decides, and
* then takes: if the wire was cut and made again in between, and another message put, what is at the
* mark is not what it looked at. Then each answers 3, no such message, and nothing is written or moved. */
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 between two");
put_msg(&ND(b)->n, 100, 110, 4, 1);
CHECK(ask(b, 3, 100, 110, 5) == 0 && ask(a, 0, 0, 0, 6) == 0, "a message of four characters comes, and the mark is at it");
memcpy(snap, ND(a)->n.mem, sizeof snap);
CHECK(ask(a, 0, 200, 220, 2) == 3 && ask(a, 0, 1, 0, 3) == 3 && ask(a, 0, 0, 0, 4) == 3 && f.place[a].wire[0].rx->used == 8 && memcmp(snap, ND(a)->n.mem, sizeof snap) == 0,
"not looked at: take, move and drop each answer 3, and nothing is written or taken");
CHECK(ask(a, 0, 200, 220, 1) == 0 && ND(a)->n.mem[206] == 4, "it is looked at: four characters");
/* the wire is cut and made again, and a message of 1,024 characters put where the short one was */
CHECK(v4_fabric_unwire(&f, a, 0) && v4_fabric_wire(&f, a, 0, b, 3), "the wire is cut, and made again");
put_msg(&ND(b)->n, 100, 110, 1024, 2);
CHECK(ask(b, 3, 100, 110, 5) == 0 && f.place[a].wire[0].rx->used == 263, "and a message of 1,024 characters is put on it");
memcpy(snap, ND(a)->n.mem, sizeof snap);
CHECK(ask(a, 0, 200, 220, 2) == 3 && memcmp(snap, ND(a)->n.mem, sizeof snap) == 0 && f.place[a].wire[0].rx->used == 263,
"a take answers 3: it is not the message that was looked at. Nothing is written: not one cell, where 256 would have been");
CHECK(ask(a, 0, 1, 0, 3) == 3 && ask(a, 0, 0, 0, 4) == 3 && f.place[a].wire[0].rx->used == 263 && f.place[a].wire[1].tx->used == 0, "nor is it moved, or dropped");
CHECK(ask(a, 0, 200, 220, 1) == 0 && ND(a)->n.mem[206] == 1024 && ask(a, 0, 200, 220, 2) == 0 && msg_is(&ND(a)->n, 200, 220, 1024, 2), "looked at, it is taken");
/* a look is good for one take, move or drop, and not after the mark has been moved */
put_msg(&ND(b)->n, 100, 110, 4, 3); CHECK(ask(b, 3, 100, 110, 5) == 0, "another comes");
put_msg(&ND(b)->n, 100, 110, 8, 4); CHECK(ask(b, 3, 100, 110, 5) == 0, "and another behind it");
CHECK(ask(a, 0, 200, 220, 1) == 0 && ask(a, 0, 0, 0, 7) == 0 && ask(a, 0, 200, 220, 2) == 3, "looked at, then the mark moved on with next: a take answers 3");
CHECK(ask(a, 0, 0, 0, 6) == 0 && ask(a, 0, 200, 220, 1) == 0 && ask(a, 0, 0, 0, 6) == 0 && ask(a, 0, 0, 0, 4) == 3, "looked at, then first: a drop answers 3");
CHECK(ask(a, 0, 200, 220, 1) == 0 && ask(a, 0, 0, 0, 4) == 0 && ask(a, 0, 0, 0, 4) == 3 && f.place[a].wire[0].rx->used == 9, "looked at and dropped; the one that followed has not been looked at, and is not dropped");
CHECK(ask(a, 0, 200, 220, 1) == 0 && ND(a)->n.mem[206] == 8 && ask(a, 0, 1, 0, 3) == 0 && f.place[a].wire[1].tx->used == 9, "looked at, it is moved");
/* a message put behind the one looked at does not undo the look */
put_msg(&ND(b)->n, 100, 110, 4, 5); CHECK(ask(b, 3, 100, 110, 5) == 0 && ask(a, 0, 200, 220, 1) == 0, "one comes and is looked at");
put_msg(&ND(b)->n, 100, 110, 4, 6); CHECK(ask(b, 3, 100, 110, 5) == 0 && ask(a, 0, 200, 220, 2) == 0 && msg_is(&ND(a)->n, 200, 220, 4, 5), "another comes behind it: the first is still the one looked at, and is taken");
/* THE KERNEL ON A PORT has no queue: it is asked by words, and takes no messages */
{
static stream ks;
static v4_device kern;
unsigned free_before = f.queue_free;
ks.took_n = 0; kern.take = stream_take; kern.give = 0; kern.self = &ks; kern.pending = 0;
CHECK(v4_fabric_wire_kernel(&f, a, 5, &kern) && f.queue_free == free_before && !f.place[a].wire[5].rx && !f.place[a].wire[5].tx, "a kernel is wired to a port, and is given no queues");
put_msg(&ND(a)->n, 100, 110, 4, 7);
CHECK(ask(a, 5, 100, 110, 5) == 2 && ask(a, 5, 8, 0, 10) == 2 && ask(a, 5, 8, 0, 9) == 2 && ask(a, 5, 200, 220, 1) == 2 && ask(a, 5, 0, 0, 6) == 2 && !ND(a)->n.doing,
"a message put to it, room asked of it, a sleep for room on it, a look: each answers 2 at once -- no one takes messages there");
v4_node_store(&ND(a)->n, PB + 5, 77);
(void)v4_fabric_step(&f);
CHECK(!ND(a)->n.asking && ks.took_n == 1 && ks.took[0] == 77, "a word written to it is handed over as before");
CHECK(v4_fabric_unwire(&f, a, 5) && f.queue_free == free_before, "and cutting it gives back no queue that it never had");
}
/* move from one of a node's wires to another */
v4_fabric_init(&f, more_places, 8);
@@ -814,19 +876,20 @@ int main(void)
put_msg(&ND(b)->n, 100, 110, 21, 4);
ND(b)->n.mem[103] = 9;
CHECK(ask(b, 3, 100, 110, 5) == 0 && ask(a, 0, 0, 0, 6) == 0, "a message comes on wire 0");
CHECK(ask(a, 0, 1, 0, 3) == 0, "and is moved to wire 1");
CHECK(seen(a, 0, 1, 0, 3) == 0, "and is moved to wire 1");
CHECK(f.place[a].wire[0].rx->used == 0 && f.place[c].wire[2].rx->used == 7 + 6, "it is off the one and on the other");
CHECK(ask(c, 2, 200, 220, 6) == 0 && ask(c, 2, 200, 220, 2) == 0 && ND(c)->n.mem[203] == 8 && msg_is_but(&ND(c)->n, 200, 220, 21, 4, 3),
CHECK(ask(c, 2, 200, 220, 6) == 0 && seen(c, 2, 200, 220, 2) == 0 && ND(c)->n.mem[203] == 8 && msg_is_but(&ND(c)->n, 200, 220, 21, 4, 3),
"and arrives whole, its fourth word one fewer");
/* the other full: 1, and nothing moved */
put_msg(&ND(b)->n, 100, 110, 21, 4);
CHECK(ask(b, 3, 100, 110, 5) == 0 && ask(a, 0, 0, 0, 6) == 0, "another comes");
fill(f.place[a].wire[1].tx, 0);
(void)ask(a, 0, 500, 0, 1);
memcpy(&copy, f.place[a].wire[0].rx, sizeof copy);
{
v4_wire_queue copy2;
memcpy(&copy2, f.place[a].wire[1].tx, sizeof copy2);
CHECK(ask(a, 0, 1, 0, 3) == 1, "moved to a wire with no room: 1");
CHECK(ask(a, 0, 1, 0, 3) == 1, "looked at, and moved to a wire with no room: 1");
CHECK(memcmp(&copy, f.place[a].wire[0].rx, sizeof copy) == 0 && memcmp(&copy2, f.place[a].wire[1].tx, sizeof copy2) == 0, "and neither wire has changed");
}
CHECK(ask(a, 0, 5, 0, 3) == 2 && ask(a, 0, -1, 0, 3) == 2 && ask(a, 5, 1, 0, 3) == 2, "move to a port with nothing, or from one: 2");
@@ -872,7 +935,7 @@ int main(void)
for (w = 0; w < 7; w++) h[w] = ND(a)->n.mem[100 + w];
for (w = 0; w < 5; w++) t[w] = ND(a)->n.mem[110 + w];
CHECK(v4_fabric_device_put(&f, a, 4, h, t) == 0, "the device puts a message");
CHECK(ask(a, 4, 200, 220, 6) == 0 && ask(a, 4, 200, 220, 2) == 0 && msg_is(&ND(a)->n, 200, 220, 17, 8), "and the node takes it, whole");
CHECK(ask(a, 4, 200, 220, 6) == 0 && seen(a, 4, 200, 220, 2) == 0 && msg_is(&ND(a)->n, 200, 220, 17, 8), "and the node takes it, whole");
put_msg(&ND(a)->n, 100, 110, 3, 9);
CHECK(ask(a, 4, 100, 110, 5) == 0, "the node puts one");
memset(h, 0, sizeof h); memset(t, 0, sizeof t);