From f1419e739c2a4ae9f969ebe26835853bcb74f734 Mon Sep 17 00:00:00 2001 From: rajames Date: Thu, 8 Oct 2026 22:36:59 -0400 Subject: [PATCH] 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 --- capsules/v4/nucleus-64.f18 | Bin 57048 -> 57280 bytes docs/v4.0.0/MESH.md | 20 ++++- v4/Makefile | 4 +- v4/capsule/core.v4 | 24 +++++- v4/capsule/quit.v4 | 28 +++--- v4/include/v4/fabric.h | 10 +++ v4/include/v4/wire.h | 13 ++- v4/src/fabric.c | 15 +++- v4/src/wire.c | 7 +- v4/system/boot.c | 6 +- v4/tests/test_fabric.c | 89 ++++++++++++++++--- v4/tests/test_host_depth.c | 6 +- v4/tests/test_host_mesh.c | 169 ++++++++++++++++++++++++++++++++++--- v4/tests/test_host_quit.c | 6 +- v4/tests/test_host_unit.c | 11 +-- 15 files changed, 345 insertions(+), 63 deletions(-) diff --git a/capsules/v4/nucleus-64.f18 b/capsules/v4/nucleus-64.f18 index fa7a270faeeba15a621455fb74aca3b2d9c1c933..183e1bfb18c889d0a3e653486eb0d9ced5e79fce 100644 GIT binary patch literal 57280 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 R(o-Mm$9zJrEQD(a78NyGsud|n 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 Stack overflow$$' $(BINDIR)/deep-amd64.txt && grep -q '^ok> 3 ok$$' $(BINDIR)/deep-amd64.txt || { echo "hosted-check: a line that left 29 values on the stack was not an error the node came back from"; tail -4 $(BINDIR)/deep-amd64.txt; exit 1; } @printf '1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26\nWORDS\nDEPTH .\n' | $(BINDIR)/starforth4-amd64 > $(BINDIR)/deepwords-amd64.txt 2>&1; true @grep -q 'DUP' $(BINDIR)/deepwords-amd64.txt && grep -q '^ok> 26 ok$$' $(BINDIR)/deepwords-amd64.txt || { echo "hosted-check: WORDS with 26 values on the stack did not print and come back"; tail -4 $(BINDIR)/deepwords-amd64.txt | cut -c1-200; exit 1; } - @printf '1 0 ROUTE\n65 EMIT\n: P 400 0 DO I . LOOP ; P\nNO-ROUTES\n1 2 + .\n' | timeout 120 $(BINDIR)/starforth4-amd64 > $(BINDIR)/kport-amd64.txt 2>&1; true - @test $$(grep -c '^ok> ERROR$$' $(BINDIR)/kport-amd64.txt) -eq 2 && grep -q '^ok> 3 ok$$' $(BINDIR)/kport-amd64.txt || { echo "hosted-check: with the way to the console set to the kernel's port, a line that prints did not end in an error at the prompt, or the node did not come back"; tail -6 $(BINDIR)/kport-amd64.txt; exit 1; } + @printf '1 0 ROUTE\n65 EMIT\n: P 400 0 DO I . LOOP ; P\nNO-ROUTES\nDEPTH .\n1 2 + .\n' | timeout 120 $(BINDIR)/starforth4-amd64 > $(BINDIR)/kport-amd64.txt 2>&1; true + @test $$(grep -c '^ok> ERROR$$' $(BINDIR)/kport-amd64.txt) -eq 2 && grep -q '^ok> 3 ok$$' $(BINDIR)/kport-amd64.txt && grep -q '^ok> 0 ok$$' $(BINDIR)/kport-amd64.txt || { echo "hosted-check: with the way to the console set to the kernel's port, a line that prints did not end in an error at the prompt, or left something on the stack, or the node did not come back"; tail -6 $(BINDIR)/kport-amd64.txt; exit 1; } @printf ': S0 S" 1 DROP" 0 0 SEND-ON ; S0\n1 2 + .\n65 1 BLOCK C! 1 BLOCK C@ .\n' | timeout 120 $(BINDIR)/starforth4-amd64 > $(BINDIR)/ksend-amd64.txt 2>&1; true @grep -q '^ok> No one on that port$$' $(BINDIR)/ksend-amd64.txt && grep -q '^ok> 3 ok$$' $(BINDIR)/ksend-amd64.txt && grep -q '^ok> 65 ok$$' $(BINDIR)/ksend-amd64.txt || { echo "hosted-check: a message sent to the kernel's port was not refused with No one on that port, or the kernel's requests no longer work"; tail -6 $(BINDIR)/ksend-amd64.txt; exit 1; } @python3 $(HERE)/tools/depthsweep.py $(BINDIR)/starforth4-amd64 $$(( $(HOST_DATA_RING) + 2 )) $$(( $(HOST_RET_RING) + 1 )) || { echo "hosted-check: at some depth of a stack a line was not answered, or the node did not come back"; exit 1; } diff --git a/v4/capsule/core.v4 b/v4/capsule/core.v4 index 1d0ce132..f53ca385 100644 --- a/v4/capsule/core.v4 +++ b/v4/capsule/core.v4 @@ -229,8 +229,11 @@ header CMOVE \ wire would be; or, if it is about the node this one is waiting on, \ (A-SELF) is set for AWAIT to find (quit.v4). \ (WIRE) is not what it was afterwards: the NACK went by another. +\ If the drop does not drop -- the wire was cut after the message was looked +\ at, which let it go already -- nothing is counted and no one is told. : (REFUSE) - 4 (DO) drop + 4 (DO) if DROPPED drop ; \ it was not there to drop -- its wire has been cut: nothing is counted or told + DROPPED: drop (LOST) a! @ 1 + ! (IN)+2 a! @ -4 + if NOT -1 + if NOT 2 + if ANSWER drop (IN) a! @ (ONE) a! ! (IN)+1 a! @ jump TELL \ about whom it was for; to its sender @@ -350,6 +353,17 @@ header CMOVE \ ( -- ) go past the message (MINE) found: it stays on its wire : (SKIP) (S-WIRE) a! @ (WIRE!) 7 (DO) drop ; +\ ( -- ) A TAKE DID NOT TAKE the message (MINE) found. The fabric takes +\ only the message last looked at (MESH.md 7d.3), so either the wire has +\ been cut -- and there is nothing there, or another message that has not +\ been looked at -- or the message cannot be taken at all. It is dropped, +\ and counted in (LOST); if it cannot be dropped either it is gone past. +\ Either way whoever goes through the messages next is at another one: +\ nothing goes round on one that will not be taken. +: (UNTAKEN) + (S-WIRE) a! @ (WIRE!) 4 (DO) if GONE drop jump (SKIP) + GONE: drop (LOST) a! @ 1 + ! ; + \ ( -- how ) take the message (MINE) found, which has one word of text: \ its seven words stay in (IN), and its word goes to (ONE). 0: it is \ taken. Anything else: it is not -- the wire was cut after the message @@ -388,7 +402,11 @@ header CMOVE \ The three cells and two return entries this needs are tried first. \ WITH NO ONE ON THE PORT -- when it waits for room, or when it comes to \ put -- what was printed cannot be sent. While the text is being done -\ that is its error, 18. Once it has been finished with, what it printed +\ that is its error, 18; and the data stack is emptied first, as a fault +\ empties it. A word that raises an error has taken its own arguments +\ off; this one comes from under whatever word was printing, in the +\ middle of its work, and what that word had on the stack is nothing its +\ caller can use (as first built those values were left there). Once it has been finished with, what it printed \ is let go and counted in (LOST), and the text is said to have ended in \ an error: (LINE-STATUS), which is the word then sent of how it ended. : (FLUSH-OUT) @@ -401,7 +419,7 @@ header CMOVE (WAIT-ROOM) if ROOM NOONE: drop (QUIET) a! @ if TELL drop (OUT) (OUT^) a! ! (LOST) a! @ 1 + ! 2 (LINE-STATUS) a! ! ; - TELL: drop NODE-ERROR b! 18 !b ; + TELL: drop DSTACK-DEPTH b! a !b NODE-ERROR b! 18 !b ; \ the data stack is emptied, as by a fault: see below ROOM: drop (PRINT-TO) a! @ 2 (HDR) (OUT^) a! @ a! 0 !+ 0 !+ 0 ! diff --git a/v4/capsule/quit.v4 b/v4/capsule/quit.v4 index 0f9f7dc5..beb7e27b 100644 --- a/v4/capsule/quit.v4 +++ b/v4/capsule/quit.v4 @@ -107,12 +107,15 @@ macro R-CLEAR RSTACK-DEPTH b! a !b endmacro \ the answer has gone. \ a NACK is taken and counted in (REFUSED) \ a GONE is taken, and believed if this node's centre sent it -\ any other is dropped, and counted in (LOST): an answer no one waits -\ for, what a node printed that came here, or what is not as -\ long as its type says. +\ an answer no one is waiting for is dropped: a SEND that is not waited +\ on is ordinary use, and its answer is not a loss. +\ any other is dropped, and counted in (LOST): what a node printed +\ that came here, a type there is none of, or a NACK or GONE +\ that is not as long as one is. (LOST) counts what should +\ have been delivered or acted on and was not. \ A TAKE THAT DOES NOT TAKE -- the wire was cut after the message was \ looked at -- leaves nothing to act on: no text is done and nothing is -\ counted or believed for it. +\ believed for it, (UNTAKEN). The same for a drop that does not drop. \ With nothing to begin it sleeps, executing nothing, until a message \ comes; or, if text is waiting for room for its answer, or (SERVE) left a \ message for want of room, until there is room on any wire that leads @@ -129,12 +132,15 @@ macro R-CLEAR RSTACK-DEPTH b! a !b endmacro (SERVE) (MINE0) M: (MINE) if REST drop (IN)+2 a! @ -1 + if TEXT -3 + if NACK -1 + if GONE - OTHER: drop 4 (DO) drop (LOST) a! @ 1 + ! jump M \ let go, and counted + OTHER: drop 4 (DO) if LETGO drop (SKIP) jump M \ it was not there to drop: nothing is counted + LETGO: drop (IN)+2 a! @ -3 + if ANSWER \ let go. An answer no one waits for is ordinary: + drop (LOST) a! @ 1 + ! jump M \ anything else should not have come here, and is counted + ANSWER: drop jump M NACK: drop (IN)+6 a! @ -4 + if N1 jump OTHER - N1: drop (TAKE1) if N2 drop jump M \ not taken: nothing is done for it + N1: drop (TAKE1) if N2 drop (UNTAKEN) jump M \ not taken: nothing is done for it N2: drop (REFUSED) a! @ 1 + ! jump M \ a message of this node's was refused: counted GONE: drop (IN)+6 a! @ -4 + if G1 jump OTHER - G1: drop (TAKE1) if G2 drop jump M + G1: drop (TAKE1) if G2 drop (UNTAKEN) jump M G2: drop (GONE?) drop jump M TEXT: drop (IN)+1 a! @ (WAY) -if T1 drop (S-WIRE) a! @ \ the way back; with none known, the wire it came on @@ -150,7 +156,7 @@ macro R-CLEAR RSTACK-DEPTH b! a !b endmacro START: drop (S-WIRE) a! @ dup (REPLY) a! ! (WIRE!) (MSG) (WIRE-A) b! !b TIB 2/ 2/ (WIRE-B) b! !b 2 (DO) \ its seven words, and its text - if TAKEN drop jump L \ not taken: the wire was cut since it was looked at + if TAKEN drop (UNTAKEN) jump M \ not taken: the wire was cut since it was looked at TAKEN: drop 0 (MSG)+6 a! @ TIB + C! \ a zero after the text (MSG)+6 a! @ SPAN a! ! @@ -353,15 +359,15 @@ header AWAIT BREAK: drop 4 jump (A-END) ANSWER: drop (IN)+1 a! @ (AWAIT-FROM) a! @ xor if A1 jump KEEP A1: drop (IN)+6 a! @ -4 + if A2 jump KEEP - A2: drop (TAKE1) if A3 drop jump M \ not taken: nothing is done for it + A2: drop (TAKE1) if A3 drop (UNTAKEN) jump M \ not taken: nothing is done for it A3: drop 1 jump (A-END) NACK: drop (IN)+6 a! @ -4 + if N1 jump KEEP - N1: drop (TAKE1) if N0 drop jump M + N1: drop (TAKE1) if N0 drop (UNTAKEN) jump M N0: drop (ONE) a! @ (AWAIT-FROM) a! @ xor if N2 \ about the node waited for? drop (REFUSED) a! @ 1 + ! jump M \ another: counted N2: drop 2 jump (A-END) GONE: drop (IN)+6 a! @ -4 + if G1 jump KEEP - G1: drop (TAKE1) if G0 drop jump M + G1: drop (TAKE1) if G0 drop (UNTAKEN) jump M G0: drop (GONE?) if M0 \ only its centre is believed drop (ONE) a! @ (AWAIT-FROM) a! @ xor if G2 drop jump M G2: drop 3 jump (A-END) diff --git a/v4/include/v4/fabric.h b/v4/include/v4/fabric.h index f321d054..3d2f16d0 100644 --- a/v4/include/v4/fabric.h +++ b/v4/include/v4/fabric.h @@ -134,6 +134,13 @@ v4_fabric_node *v4_fabric_node_at(const v4_fabric *f, unsigned id); * ends. Each returns 1, or 0 if a node or a port is not there. */ int v4_fabric_wire(v4_fabric *f, unsigned a, unsigned pa, unsigned b, unsigned pb); int v4_fabric_wire_device(v4_fabric *f, unsigned a, unsigned pa, const v4_device *device); +/* THE KERNEL ON A PORT (MESH.md 7d.5, ruled 2026-10-08). A node asks its + * kernel by words written to a port, and the kernel takes no messages: its + * port is wired to the device as above but is given no queues. Words are + * handed over as for any device; every operation on whole messages there + * answers that no one is on the port, so that a message sent to it is + * refused at once and nothing waits for it. */ +int v4_fabric_wire_kernel(v4_fabric *f, unsigned a, unsigned pa, const v4_device *device); int v4_fabric_unwire(v4_fabric *f, unsigned a, unsigned pa); /* A node that is asleep executes nothing, and nothing is handed to it or @@ -145,6 +152,9 @@ void v4_fabric_wake(v4_fabric *f, unsigned id); /* WHOLE MESSAGES. A node that has stored an operation to Do (node.h) is * blocked, and in the step in which it did the operation is done, whole, and * the node is let go: put, take, move and drop and the rest (MESH.md 7d.3). + * Take, move and drop are done only on the message the node last looked at + * on that wire (wire.h, THE LOOK): if the mark is at any other -- the wire + * was cut and made again since -- they answer 3 and nothing is written. * Sleep (8) and sleep for room (9) are the two that wait: the node stays * blocked, executing nothing, until a message has come to one of its wires * since it last fetched which wires have one. A message has "come" when a diff --git a/v4/include/v4/wire.h b/v4/include/v4/wire.h index ad588b2f..a9d5c1a4 100644 --- a/v4/include/v4/wire.h +++ b/v4/include/v4/wire.h @@ -16,6 +16,16 @@ * and those four answer V4_WIRE_NO_MESSAGE. A message put on the back of a * queue whose mark is at no message is the one the mark is then at. * + * THE LOOK. Whoever reads a queue looks at a message, decides, and then + * takes it, moves it or drops it; and the queue may have been emptied and + * filled again in between -- its wire cut and made again. So the queue + * keeps whether the message its mark is at is the one last looked at: + * `looked` is set by a look that finds a message, and cleared whenever the + * mark is moved, the marked message goes, or the queue is reset. A node's + * take, move and drop are done only while it is set (fabric.c): they are + * then about the very message that was looked at, of the length the look + * gave, and no other. + * * Nothing here allocates, and nothing uses the C library: whoever owns the * queue supplies its memory. */ @@ -40,6 +50,7 @@ typedef struct { unsigned mark; /* how many cells from the front the reader's mark is; == used: at no message */ unsigned arrived; /* not 0: a message has come since the reader last asked which wires have one */ unsigned freed; /* not 0: a message has come off since the writer was last woken from a sleep for room */ + unsigned looked; /* not 0: the message the mark is at is the one last looked at: see THE LOOK */ } v4_wire_queue; enum { V4_WIRE_DONE = 0, V4_WIRE_NO_ROOM = 1, V4_WIRE_NO_ONE = 2, V4_WIRE_NO_MESSAGE = 3, V4_WIRE_NOT_A_MESSAGE = 4 }; @@ -70,7 +81,7 @@ int v4_wire_next(v4_wire_queue *q); * `text_cap` cells (V4_WIRE_NOT_A_MESSAGE if that is too few); taken off and * gone. After a take or a drop the mark is at the message that followed. * V4_WIRE_NO_MESSAGE with the mark at no message. */ -int v4_wire_look(const v4_wire_queue *q, v4_cell *header7); +int v4_wire_look(v4_wire_queue *q, v4_cell *header7); int v4_wire_take(v4_wire_queue *q, v4_cell *header7, v4_cell *text, unsigned text_cap); int v4_wire_drop(v4_wire_queue *q); diff --git a/v4/src/fabric.c b/v4/src/fabric.c index 74028a58..218fac0f 100644 --- a/v4/src/fabric.c +++ b/v4/src/fabric.c @@ -157,6 +157,15 @@ int v4_fabric_wire(v4_fabric *f, unsigned a, unsigned pa, unsigned b, unsigned p return 1; } +int v4_fabric_wire_kernel(v4_fabric *f, unsigned a, unsigned pa, const v4_device *device) +{ + if (!there(f, a, pa) || !device) return 0; + cut(f, a, pa); + f->place[a].wire[pa].kind = V4_WIRE_DEVICE; + f->place[a].wire[pa].device = device; /* and no queues: it takes no messages */ + return 1; +} + int v4_fabric_wire_device(v4_fabric *f, unsigned a, unsigned pa, const v4_device *device) { if (!there(f, a, pa) || !device) return 0; @@ -346,7 +355,7 @@ static int operate(v4_fabric *f, unsigned id, v4_cell *how) break; case 2: /* take */ if (!rx) { *how = V4_WIRE_NO_ONE; break; } - if (rx->mark >= rx->used) { *how = V4_WIRE_NO_MESSAGE; break; } + if (rx->mark >= rx->used || !rx->looked) { *how = V4_WIRE_NO_MESSAGE; break; } /* only the message last looked at (wire.h) */ (void)v4_wire_look(rx, h); cells = v4_wire_cells(h[6]) - V4_WIRE_HEADER; if (!span_ok(n, a, V4_WIRE_HEADER) || !span_ok(n, b, cells)) { *how = V4_WIRE_NOT_A_MESSAGE; break; } @@ -354,12 +363,12 @@ static int operate(v4_fabric *f, unsigned id, v4_cell *how) break; case 3: /* move */ if (!rx) { *how = V4_WIRE_NO_ONE; break; } - if (rx->mark >= rx->used) { *how = V4_WIRE_NO_MESSAGE; break; } + if (rx->mark >= rx->used || !rx->looked) { *how = V4_WIRE_NO_MESSAGE; break; } to = a >= 0 && a < (v4_cell)V4_PORTS ? going(f, id, (unsigned)a) : 0; *how = to ? v4_wire_move(rx, to) : V4_WIRE_NO_ONE; break; case 4: /* drop */ - *how = rx ? v4_wire_drop(rx) : V4_WIRE_NO_ONE; + *how = !rx ? V4_WIRE_NO_ONE : rx->looked ? v4_wire_drop(rx) : V4_WIRE_NO_MESSAGE; break; case 5: /* put */ if (!tx) { *how = V4_WIRE_NO_ONE; break; } diff --git a/v4/src/wire.c b/v4/src/wire.c index 7903077d..582f99bb 100644 --- a/v4/src/wire.c +++ b/v4/src/wire.c @@ -14,6 +14,7 @@ void v4_wire_reset(v4_wire_queue *q) q->mark = 0; q->arrived = 0; q->freed = 0; + q->looked = 0; } unsigned v4_wire_cells(v4_cell length) @@ -62,21 +63,24 @@ int v4_wire_put(v4_wire_queue *q, const v4_cell *header7, const v4_cell *text) void v4_wire_first(v4_wire_queue *q) { + q->looked = 0; q->mark = 0; } int v4_wire_next(v4_wire_queue *q) { + q->looked = 0; if (q->mark >= q->used) return V4_WIRE_NO_MESSAGE; q->mark += size_at(q, q->mark); return q->mark < q->used ? V4_WIRE_DONE : V4_WIRE_NO_MESSAGE; } -int v4_wire_look(const v4_wire_queue *q, v4_cell *header7) +int v4_wire_look(v4_wire_queue *q, v4_cell *header7) { unsigned i; if (q->mark >= q->used) return V4_WIRE_NO_MESSAGE; for (i = 0; i < V4_WIRE_HEADER; i++) header7[i] = q->cell[slot(q, q->mark + i)]; + q->looked = 1; return V4_WIRE_DONE; } @@ -91,6 +95,7 @@ static void remove_marked(v4_wire_queue *q) } q->used -= n; q->freed = 1; + q->looked = 0; } int v4_wire_take(v4_wire_queue *q, v4_cell *header7, v4_cell *text, unsigned text_cap) diff --git a/v4/system/boot.c b/v4/system/boot.c index 124e18ce..30e9c559 100644 --- a/v4/system/boot.c +++ b/v4/system/boot.c @@ -138,18 +138,18 @@ static int boot_do(const v4_boot *bt) break; case 2: /* take */ if (!rx) break; - if (v4_wire_look(rx, h) != V4_WIRE_DONE) { how = V4_WIRE_NO_MESSAGE; break; } + if (!rx->looked || v4_wire_look(rx, h) != V4_WIRE_DONE) { how = V4_WIRE_NO_MESSAGE; break; } /* only the message last looked at (wire.h) */ cells = v4_wire_cells(h[6]) - V4_WIRE_HEADER; if (!span_ok(n, a, V4_WIRE_HEADER) || !span_ok(n, b, cells)) how = V4_WIRE_NOT_A_MESSAGE; else how = v4_wire_take(rx, &n->mem[a], cells ? &n->mem[b] : &n->mem[0], cells); break; case 3: /* move */ if (!rx) break; - if (rx->mark >= rx->used) how = V4_WIRE_NO_MESSAGE; + if (rx->mark >= rx->used || !rx->looked) how = V4_WIRE_NO_MESSAGE; else how = a == (v4_cell)CONSOLE_PORT ? v4_wire_move(rx, FROM_NODE) : V4_WIRE_NO_ONE; break; case 4: /* drop */ - if (rx) how = v4_wire_drop(rx); + if (rx) how = rx->looked ? v4_wire_drop(rx) : V4_WIRE_NO_MESSAGE; break; case 5: /* put */ if (!tx) break; diff --git a/v4/tests/test_fabric.c b/v4/tests/test_fabric.c index 39339ae3..68fdd2b2 100644 --- a/v4/tests/test_fabric.c +++ b/v4/tests/test_fabric.c @@ -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(©, f.place[a].wire[0].rx, sizeof copy); { v4_wire_queue copy2; memcpy(©2, 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(©, f.place[a].wire[0].rx, sizeof copy) == 0 && memcmp(©2, 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); diff --git a/v4/tests/test_host_depth.c b/v4/tests/test_host_depth.c index f56b3845..22723d66 100644 --- a/v4/tests/test_host_depth.c +++ b/v4/tests/test_host_depth.c @@ -273,6 +273,7 @@ static int one(unsigned path, const char *what, unsigned depth, const char *text v4_cell lost11 = pool[mid].n.mem[LOST], refused12 = pool[far].n.mem[REFUSED], ways12; int how11, works = 0, clean_error = 0; unsigned left; + v4_uheat_t clock12; if (!sixhundred[0]) memset(sixhundred, 'A', 600); runs++; @@ -281,6 +282,7 @@ static int one(unsigned path, const char *what, unsigned depth, const char *text ways12 = pool[far].n.mem[ROUTE_COUNT]; forget(); saw_room_wait = 0; saw_passed = 0; + clock12 = pool[far].es.anticlock; 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; @@ -319,8 +321,8 @@ static int one(unsigned path, const char *what, unsigned depth, const char *text 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 1: works = how11 == V4_TEXT_COMPLETED && printed_len == 0 && left == values && pool[far].es.anticlock != clock12 && pool[mid].n.mem[LOST] == lost11; /* node 12 did the text */ + clean_error = over && strcmp(printed, over) == 0 && pool[far].es.anticlock == clock12; break; /* nothing was sent */ 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; diff --git a/v4/tests/test_host_mesh.c b/v4/tests/test_host_mesh.c index bc5d0436..dbc58db2 100644 --- a/v4/tests/test_host_mesh.c +++ b/v4/tests/test_host_mesh.c @@ -39,6 +39,10 @@ static v4_wire_queue queues[QUEUES]; /* ---- the console: a device on a port ---- */ #define CONSOLE_ID 1 static const v4_device console = { 0, 0, 0, 0 }; /* it takes and gives no words: only whole messages */ +/* a kernel on a node's port 0: it takes the words written to it, and no messages (MESH.md 7d.5) */ +static unsigned kernel_words; +static int kernel_take(void *self, v4_cell value) { (void)self; (void)value; kernel_words++; return 1; } +static const v4_device kernel_dev = { kernel_take, 0, 0, 0 }; static unsigned hera, mid, far; /* where the three nodes are: 10, 11, 12 */ static char printed[32768]; /* what has come back since the console last sent */ static unsigned printed_len; @@ -227,6 +231,10 @@ static void put_back(void) { memcpy(pool, kept_pool, sizeof pool); memcpy(places 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 v4_cell mid_for; /* whom the message waited for at node 11's wire from node 12 is for */ +static int mid_has_one(void) { return coming_to(mid, 3) && coming_to(mid, 3)->used > 0 && coming_to(mid, 3)->cell[coming_to(mid, 3)->head] == mid_for; } +static v4_cell far_lost_wanted; +static int far_has_lost(void) { return pool[far].n.mem[LOST] >= far_lost_wanted && asleep(far); } 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++; } @@ -317,7 +325,8 @@ int main(void) 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"); + CHECK(told(10, ": TELL2 S\" 1 DROP\" 12 SEND ; TELL2") && pool[hera].n.mem[LOST] == (v4_cell)i && coming_to(hera, 2)->used == 0, + "an answer that comes to a node that is not waiting for it is let go, and is not counted: a SEND that is not waited on is ordinary"); /* ---- waiting ---- */ { @@ -358,10 +367,8 @@ 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"); - /* 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); + 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); } /* ---- THE FLOOD: more than the wires hold (MESH.md 7d.6, acceptance 2) ---- @@ -369,12 +376,13 @@ int main(void) * word that sends ends there, error 19, and the refusal is counted. A message a middle node has no room * to pass on is let go there, counted, and its sender told. Nothing waits, and it comes to rest. */ { - long got = 0, lost = 0, refused = 0, sent = 0, tried = 0, v; + long got = 0, lost = 0, refused = 0, sent = 0, tried = 0, v, let_go, orders = 0; unsigned k, q, left = 0; static const v4_cell who[3] = { 10, 11, 12 }; const char *r; CHECK(is_empty(tell(10, "0 GOT ! 0 SENT ! 0 TRIED !")) && is_empty(tell(11, "0 GOT ! 0 SENT ! 0 TRIED !")) && is_empty(tell(12, "0 GOT ! 0 SENT ! 0 TRIED !")), "the counts begin again"); - r = tell(10, ": GO2 S\" 200 10 BURST 200 12 BURST\" 11 SEND S\" 200 11 BURST 200 10 BURST\" 12 SEND 200 11 BURST 200 12 BURST ; GO2"); + for (k = 0; k < 3; k++) { lost -= pool[k].n.mem[LOST]; refused -= pool[k].n.mem[REFUSED]; } + r = tell(10, ": GO2 S\" 200 12 BURST 200 10 BURST\" 11 SEND S\" 200 10 BURST 200 11 BURST\" 12 SEND 200 12 BURST 200 11 BURST ; GO2"); CHECK(strcmp(r, "(still running)") != 0, "two hundred from each to each: it ends"); CHECK(asleep(hera) && asleep(mid) && asleep(far), "and all three come to rest, asleep"); for (k = 0; k < 3; k++) for (q = 0; q < V4_PORTS; q++) if (coming_to(k, q)) left += coming_to(k, q)->used; @@ -383,15 +391,24 @@ int main(void) v = strtol(tell(who[k], "GOT @ ."), NULL, 10); got += v; CHECK(v >= 0 && v <= 400, "node %ld did %ld of the messages for it", (long)who[k], v); sent += strtol(tell(who[k], "SENT @ ."), NULL, 10); - tried += strtol(tell(who[k], "TRIED @ ."), NULL, 10); + v = strtol(tell(who[k], "TRIED @ ."), NULL, 10); tried += v; + if (v == 0) orders++; /* it never began to send: the text that told it to was itself let go on the way */ lost += pool[k].n.mem[LOST]; refused += pool[k].n.mem[REFUSED]; } - 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); - /* 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"); + let_go = sent - got; + printf(" the flood: %ld sends tried: %ld refused at once, %ld let go on the way, %ld done; %ld of the two texts that set a node sending let go; (LOST) %ld, (REFUSED) %ld\n", + tried, tried - sent, let_go, got, orders, lost, refused); + /* THE ACCOUNT IS EXACT. Every send tried was refused at once (counted once, in (REFUSED), and the word + * that sent ended there); or got on a wire and was done; or got on a wire and was let go on the way by + * a node with no room to pass it on -- counted there in (LOST), and once more where its NACK ended: in + * (REFUSED) at the node that sent it, or in (LOST) if the NACK could not go either. That is so too of the + * text that sets a node sending, which is one more message on those wires. Nothing else is + * counted: an answer waits for room and is not let go, and one that nobody waits for is not a loss. */ + CHECK(tried > sent && got > 0 && let_go >= 0, "there was not room for all of them: some were refused at once"); + CHECK(lost + refused == (tried - sent) + 2 * (let_go + orders), "every message is accounted for, exactly: (LOST) + (REFUSED) is %ld; %ld refused at once and twice %ld let go is %ld", + lost + refused, tried - sent, let_go + orders, (tried - sent) + 2 * (let_go + orders)); + CHECK(let_go > 0, "and some were let go on the way, so that is not an account of nothing"); CHECK(strcmp(tell(12, "7 8 * ."), "56 ") == 0 && printed_from == 12, "and the nodes go on as before"); } @@ -449,6 +466,41 @@ int main(void) 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); } + /* the same with the wire to the stuck node full: the NACK cannot go, and is counted */ + CHECK(triangle(), "the same made afresh"); + { + v4_cell lost; + v4_uheat_t c; + char text[64]; + CHECK(told(12, "NO-ROUTES 1 3 ROUTE 10 3 ROUTE 11 2 ROUTE : F 0 DO S\" HIT\" 11 SEND LOOP ;") && told(11, "55 3 ROUTE"), "node 12 has no way for 55, and a word that sends node 11 short texts"); + stuck = 1u << mid; + (void)tell(11, ": C2 400000 0 DO LOOP S\" 1 DROP\" 55 SEND BEGIN 0 UNTIL ; C2"); + snprintf(text, sizeof text, "%u F", (unsigned)(V4_WIRE_CELLS / 8u)); + CHECK(told(12, text) && going_from(far, 2)->used == V4_WIRE_CELLS, "node 11 is busy; node 12 fills the wire to it"); + lost = pool[far].n.mem[LOST]; + far_lost_wanted = lost + 1; + CHECK(run_until(far_has_lost, 30000000) && run() && pool[far].n.mem[LOST] == lost + 2 && going_from(far, 2)->used == V4_WIRE_CELLS && coming_to(far, 2)->used == 0, + "node 11 then sends 55 a message by node 12 and sticks: node 12 lets it go, and the NACK it owes has no room on the wire: both are counted: %ld", (long)(pool[far].n.mem[LOST] - lost)); + c = clock_of(far); + for (i = 0; i < 500; i++) (void)v4_fabric_step(&f); + CHECK(asleep(far) && clock_of(far) == c && strcmp(tell(12, "7 8 * ."), "56 ") == 0 && strcmp(tell(10, "1 2 + ."), "3 ") == 0, "node 12 is asleep, owing nothing, and it and node 10 answer"); + } + + /* ---- THE KERNEL ON A NODE'S PORT TAKES NO MESSAGES (MESH.md 7d.5, ruled 2026-10-08) ---- */ + CHECK(triangle() && v4_fabric_wire_kernel(&f, far, 0, &kernel_dev), "the same made afresh, with a kernel on node 12's port 0"); + { + kernel_words = 0; + CHECK(strcmp(tell(12, "7 0 PORT! 1 2 + ."), "3 ") == 0 && kernel_words == 1, "a word written to the kernel's port is taken, as ever"); + CHECK(strstr(tell(12, ": S0 S\" 1 DROP\" 0 0 SEND-ON ; S0"), "No one on that port") != NULL && ended == 1 && ended_how == V4_TEXT_ERROR && kernel_words == 1, + "a message sent by that port is refused at once: \"%s\"", printed); + CHECK(told(12, "1 0 ROUTE"), "node 12 is told the way to its console is the kernel's port"); + CHECK(is_empty(tell(12, "65 EMIT")) && ended == 1 && ended_from == 12 && ended_how == V4_TEXT_ERROR && asleep(far), + "a text that prints cannot send what it printed: it ends in an error, said by the wire the text came on, and node 12 sleeps"); + CHECK(is_empty(tell(12, ": P 3000 0 DO I . LOOP ; P")) && ended == 1 && ended_how == V4_TEXT_ERROR && asleep(far) && pool[far].n.ds.depth == 0, + "one that prints more than the buffer holds ends in error 18 and does not wait: %ld", (long)pool[far].n.mem[NODE_ERROR]); + CHECK(told(12, "1 NO-ROUTE") && strcmp(tell(12, "7 8 * ."), "56 ") == 0 && kernel_words == 1, "told to forget that way, it prints again; the kernel was sent nothing"); + } + /* ---- acceptance 5: PRINTING IS NOT LOST ---- */ CHECK(triangle(), "the same made afresh"); { @@ -566,6 +618,7 @@ int main(void) "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(pool[far].n.ds.depth == 0, "with nothing left on its data stack of what the word that was printing had there: %u", (unsigned)pool[far].n.ds.depth); 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 */ @@ -655,6 +708,96 @@ int main(void) 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); } + /* ---- ONLY WHAT WAS LOOKED AT IS TAKEN (fix round 2, NP1; MESH.md 7d.3) ---- + * Between the look and the take the wire is cut, made again, and a different message put on it. */ + { + unsigned k, tried = 0, wrong = 0, hung = 0; + static v4_message big; + static char blanks[1025]; + /* text was looked at; a NACK is there now. The NACK must not be done as text: it is a refusal, and counted as one. */ + 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++) { + static const v4_cell hd[V4_WIRE_HEADER] = { 12, 11, V4_MSG_NACK, 16, 0, 0, 4 }, t[1] = { 'T' | ('2' << 8) }; + 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(); } + if (!far_has_one()) break; + if (!v4_fabric_unwire(&f, mid, 3) || !v4_fabric_wire(&f, mid, 3, far, 2) || v4_wire_put(places[far].wire[2].rx, hd, t) != V4_WIRE_DONE) { wrong += 1000; break; } + tried++; + if (!run()) { hung++; continue; } + if (strcmp(tell(12, "N @ ."), "1 ") != 0 || pool[far].n.mem[REFUSED] != refused + 1 || pool[far].n.mem[ME] != 12) wrong++; + } + CHECK(tried > 100 && wrong == 0 && hung == 0, "text looked at, and a NACK there when it comes to take: at each of %u steps the NACK was counted as a refusal and nothing was done as text: %u wrong, %u hung", tried, wrong, hung); + + /* a NACK was looked at; a text of 1,024 characters is there now. Its 256 cells must not be taken where one was looked for. */ + memset(blanks, ' ', 1024); + CHECK(row() && told(12, ": N77 S\" 1 DROP\" 77 SEND ;") && put_text(12, "N77") && run_until(far_has_nack, 1000000) && v4_message_text(&big, 12, 11, V4_MSG_TEXT, blanks, 1024), "a NACK has come to node 12's wire"); + keep(); + tried = wrong = hung = 0; + for (k = 0; k < 200; k++) { + static v4_cell before[V4_NODE_WORDS]; + v4_cell at; + unsigned changed = 0; + put_back(); + for (i = 0; i < k; i++) { (void)v4_fabric_step(&f); (void)console_takes(); } + if (!far_has_nack()) break; + memcpy(before, pool[far].n.mem, sizeof before); + if (!v4_fabric_unwire(&f, mid, 3) || !v4_fabric_wire(&f, mid, 3, far, 2) || v4_wire_put(places[far].wire[2].rx, big.word, big.word + V4_MSG_HEADER) != V4_WIRE_DONE) { wrong += 1000; break; } + tried++; + if (!run()) { hung++; continue; } + /* the cells after (ONE), as far as the variables go, are as they were: the text went to TIB, where text goes */ + for (at = ONE + 1; at < BUF0_W; at++) if (at != LINE_STATUS && at != QUIET && at != DONE_PORT && at != KEEP && at != REPLY && !(at >= MSG && at < MSG + 7) && at != PRINT_TO && at != PRINT_PORT + && !(at >= S_ON && at <= S_HAVE) && at != DEFER && pool[far].n.mem[at] != before[at]) changed++; + if (changed || pool[far].n.mem[ME] != 12 || strcmp(tell(12, "1 2 + ."), "3 ") != 0) wrong++; + } + CHECK(tried > 100 && wrong == 0 && hung == 0, "a NACK looked at, and a text of 1,024 characters there when it comes to take: at each of %u steps nothing was overwritten, and node 12 answers: %u wrong, %u hung", tried, wrong, hung); + } + + /* ---- A DROP THAT DOES NOT DROP (fix round 2, NP3) ---- + * The wire is cut between the look and the drop: the cut let the message go, and nothing is counted or told for it. */ + { + unsigned k, cut_before = 0, cut_after = 0, wrong = 0; + /* (REFUSE): node 11 has no way for what node 12 sends 99 */ + mid_for = 99; + CHECK(row() && told(11, "NO-ROUTES 1 2 ROUTE 10 2 ROUTE 12 3 ROUTE") && told(12, ": S99 S\" 1 DROP\" 99 SEND ;") && put_text(12, "S99") && run_until(mid_has_one, 1000000), "a message node 11 must refuse has come to its wire"); + keep(); + for (k = 0; k < 320; k++) { + int there; + v4_cell lost; + put_back(); + lost = pool[mid].n.mem[LOST]; + for (i = 0; i < k; i++) { (void)v4_fabric_step(&f); (void)console_takes(); } + there = mid_has_one(); + (void)v4_fabric_unwire(&f, mid, 3); + if (!run() || !asleep(mid)) { wrong += 1000; break; } + if (there) cut_before++; else cut_after++; + /* cut after the drop the message is counted; and its NACK too, if the cut came before that was put */ + if (there ? pool[mid].n.mem[LOST] != lost : !(pool[mid].n.mem[LOST] == lost + 1 || pool[mid].n.mem[LOST] == lost + 2)) wrong++; + } + CHECK(wrong == 0 && cut_before > 20 && cut_after > 0, "a refusal: cut before the drop (%u times) nothing was counted; cut after (%u), the message, and its NACK if it could not go: %u wrong", cut_before, cut_after, wrong); + + /* (IDLE): what node 12 prints comes to node 11, which is not a console: it is let go, and counted */ + cut_before = cut_after = wrong = 0; + mid_for = 11; + CHECK(row() && told(12, "11 (CONSOLE) !") && put_text(12, "65 EMIT") && run_until(mid_has_one, 1000000), "what a node printed has come to the wire of a node that is no console"); + keep(); + for (k = 0; k < 320; k++) { + int there; + v4_cell lost; + put_back(); + lost = pool[mid].n.mem[LOST]; + for (i = 0; i < k; i++) { (void)v4_fabric_step(&f); (void)console_takes(); } + there = mid_has_one(); + (void)v4_fabric_unwire(&f, mid, 3); + if (!run() || !asleep(mid)) { wrong += 1000; break; } + if (there) cut_before++; else cut_after++; + if (pool[mid].n.mem[LOST] != lost + (there ? 0 : 1)) wrong++; + } + CHECK(wrong == 0 && cut_before > 20 && cut_after > 0, "what should not have come: cut before the drop (%u times) nothing was counted; 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 diff --git a/v4/tests/test_host_quit.c b/v4/tests/test_host_quit.c index 8350d140..99e28e09 100644 --- a/v4/tests/test_host_quit.c +++ b/v4/tests/test_host_quit.c @@ -219,12 +219,12 @@ static int wire_do(void) case 1: if (rx) how = span_ok(a, V4_WIRE_HEADER) || rx->mark >= rx->used ? v4_wire_look(rx, span_ok(a, V4_WIRE_HEADER) ? &n.mem[a] : hd) : V4_WIRE_NOT_A_MESSAGE; break; case 2: if (!rx) break; - if (v4_wire_look(rx, hd) != V4_WIRE_DONE) { how = V4_WIRE_NO_MESSAGE; break; } + if (!rx->looked || v4_wire_look(rx, hd) != V4_WIRE_DONE) { how = V4_WIRE_NO_MESSAGE; break; } cells = v4_wire_cells(hd[6]) - V4_WIRE_HEADER; how = span_ok(a, V4_WIRE_HEADER) && span_ok(b, cells) ? v4_wire_take(rx, &n.mem[a], cells ? &n.mem[b] : hd, cells) : V4_WIRE_NOT_A_MESSAGE; break; - case 3: if (rx) how = rx->mark >= rx->used ? V4_WIRE_NO_MESSAGE : a == (v4_cell)CONSOLE_PORT ? v4_wire_move(rx, &wire_out) : V4_WIRE_NO_ONE; break; - case 4: if (rx) how = v4_wire_drop(rx); break; + case 3: if (rx) how = rx->mark >= rx->used || !rx->looked ? V4_WIRE_NO_MESSAGE : a == (v4_cell)CONSOLE_PORT ? v4_wire_move(rx, &wire_out) : V4_WIRE_NO_ONE; break; + case 4: if (rx) how = rx->looked ? v4_wire_drop(rx) : V4_WIRE_NO_MESSAGE; break; case 5: if (!tx) break; cells = span_ok(a, V4_WIRE_HEADER) ? v4_wire_cells(n.mem[a + 6]) : 0; diff --git a/v4/tests/test_host_unit.c b/v4/tests/test_host_unit.c index 33ee7e51..0c0224de 100644 --- a/v4/tests/test_host_unit.c +++ b/v4/tests/test_host_unit.c @@ -143,7 +143,7 @@ static int host_born(void *self) v4_blocks_init(&node_blocks[k], BUF0_W); if (k != 0) { /* Hera's kernel is wired where she is made */ node_kernel[k].take = node_kernel_take; node_kernel[k].give = 0; node_kernel[k].self = &pool[k]; node_kernel[k].pending = 0; - if (!v4_fabric_wire_device(&f, (unsigned)place, 0, &node_kernel[k])) return -1; + if (!v4_fabric_wire_kernel(&f, (unsigned)place, 0, &node_kernel[k])) return -1; /* it takes words, and no messages */ } return place; } @@ -405,7 +405,7 @@ int main(void) hera_place = (unsigned)place; for (i = 0; i < 4096 && pool[0].n.mem[i] == 0; i++) { } CHECK(i == 4096 && pool[0].n.p == PORT + (v4_cell)V4_PORT_ANY, "with nothing in it, listening at its ports"); - CHECK(v4_fabric_wire_device(&f, hera_place, 0, &kernel) && v4_fabric_wire_device(&f, hera_place, 1, &console), "the kernel is on its port 0 and the console on its port 1"); + CHECK(v4_fabric_wire_kernel(&f, hera_place, 0, &kernel) && v4_fabric_wire_device(&f, hera_place, 1, &console), "the kernel is on its port 0 and the console on its port 1"); for (i = 0; i < 400000 && v4_fabric_step(&f) != 0; i++) { } CHECK(given_cells == f18_count && pool[0].n.doing && pool[0].n.do_op == 8 && !pool[0].n.stopped, "it takes in the nucleus through its port, a word at a time, and sleeps until a message comes: %u of %u cells", given_cells, f18_count); @@ -490,8 +490,7 @@ 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) @ ."), "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); + CHECK(strcmp(tell(10, "(LOST) @ ."), "0 ") == 0 && strcmp(tell(11, "(LOST) @ ."), "0 ") == 0 && strcmp(tell(14, "(LOST) @ ."), "0 ") == 0, "nothing was let go"); /* ---- they share the store (acceptance 3) ---- */ step_limit = 30000000ul; @@ -515,7 +514,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) @ ."), "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(strcmp(tell(10, "(LOST) @ ."), "0 ") == 0 && strcmp(tell(12, "(LOST) @ ."), "0 ") == 0, "and no message was let go"); 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) ---- @@ -548,6 +547,8 @@ int main(void) CHECK(waiting(12) && waiting(11) && waiting(10), "every node that is not stuck is asleep again"); CHECK(strcmp(tell(12, "12 100 * ."), "1200 ") == 0 && strcmp(tell(11, "11 100 * ."), "1100 ") == 0 && strcmp(tell(10, "1 2 + ."), "3 ") == 0, "and everything else keeps running: \"%s\"", printed); CHECK(strstr(tell(12, "5 3 PORT!"), "No one on that port") != NULL && waiting(12), "a write to the port node 14 was on is an error at once, not a wait: \"%s\"", printed); + CHECK(strstr(tell(12, ": K0 S\" 1 DROP\" 0 0 SEND-ON ; K0"), "No one on that port") != NULL && strcmp(tell(12, "2100 BLOCK C@ ."), "65 ") == 0, + "a message sent by the port node 12's kernel is on is refused at once -- the kernel takes no messages -- and its blocks are asked for as before: \"%s\"", printed); { v4_cell refused = node_numbered(12)->mem[REFUSED], r11 = node_numbered(11)->mem[REFUSED]; CHECK(tell(12, ": AGAIN14 S\" 65 EMIT\" 14 SEND ; AGAIN14")[0] == 0 && node_numbered(12)->mem[REFUSED] == refused + 1 && waiting(12),