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:
co-authored by
Claude Opus 5.5
parent
6a7d0aa27f
commit
f1419e739c
Binary file not shown.
+17
-3
@@ -800,6 +800,17 @@ the engine and happens whole.
|
||||
- Each wire has a mark for the node that reads it: the message that
|
||||
look, take, move and drop are about. First and next move it; with no
|
||||
message there, those four answer 3.
|
||||
- **Take, move and drop are done only on the message last looked at.**
|
||||
A node looks, decides, and then takes; the wire may have been cut and
|
||||
made again in between, and another message put where that one was. So
|
||||
the wire keeps whether the message at its mark is the one last looked
|
||||
at: a look that finds a message makes it so, and first, next, the
|
||||
message's going, and the cutting of the wire undo it. Take, move and
|
||||
drop of anything else answer 3 and change nothing, and a take writes
|
||||
the cells of the message looked at and no more. *(Added 2026-10-08, fix
|
||||
round 2 of tasks 2 and 3: a NACK was done as text, and a text of 1,024
|
||||
characters taken where a NACK had been looked at overwrote the node's
|
||||
variables.)*
|
||||
- Only 8 and 9 block. A node blocked there executes nothing.
|
||||
- **Sleep is woken by a message coming, not by one being there.** A node
|
||||
may leave a message for itself on a wire -- text it will not begin
|
||||
@@ -877,9 +888,12 @@ waiting, the words that turned it, the list of NACKs owed, and
|
||||
### 7d.5 The products
|
||||
|
||||
The lone node on the hosted and bare-metal products is given the same
|
||||
addresses by its host (`v4/system/boot.c`): its two wires are the
|
||||
console and the kernel. Nothing else changes for it: the kernel's
|
||||
requests stay a word at a time.
|
||||
addresses by its host (`v4/system/boot.c`): its one wire is the
|
||||
console's. **The kernel's port has no queue** *(ruled 2026-10-08)*, on
|
||||
the products and in a fabric (`v4_fabric_wire_kernel`): the kernel is
|
||||
asked by words written to its port, as before, and takes no messages. A
|
||||
message for that port is refused at once -- error 18, "No one on that
|
||||
port".
|
||||
|
||||
### 7d.6 What will be limits
|
||||
|
||||
|
||||
+2
-2
@@ -206,8 +206,8 @@ hosted-check: $(foreach i,$(HOSTED_ISAS),$(BINDIR)/boot-$(i).txt) $(BINDIR)/star
|
||||
@grep -q '^ok> 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; }
|
||||
|
||||
+21
-3
@@ -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 !
|
||||
|
||||
+17
-11
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
+12
-1
@@ -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);
|
||||
|
||||
|
||||
+12
-3
@@ -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; }
|
||||
|
||||
+6
-1
@@ -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)
|
||||
|
||||
+3
-3
@@ -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;
|
||||
|
||||
+76
-13
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
+156
-13
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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),
|
||||
|
||||
Reference in New Issue
Block a user