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
+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