feat(v4.0.0): messages are whole and wait on the wire -- a node keeps none but the one it is doing
Step 6e, tasks 2 and 3 (docs/v4.0.0/MESH.md 7d). The nucleus puts, looks at, takes, moves and drops whole messages through the six addresses after its ports; its own ring of messages waiting, the refusals owed, (GATE) and the looking words' use are gone. SEND, an answer, a NACK and a GONE go or are refused at once; what a text prints waits for room; a text is not begun until the wire its answer goes by has room for it, and that room is kept. The lone node's host (v4/system/boot.c) keeps its two wires and does its operations; the test hosts' consoles put and take whole messages through the fabric. New: tests/test_host_depth.c, every message path of a mesh node at every depth of both stacks. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
83694b18be
commit
b1d09af043
+90
-36
@@ -42,6 +42,7 @@
|
||||
*/
|
||||
#include "v4/text.h"
|
||||
#include "v4/message.h"
|
||||
#include "v4/wire.h"
|
||||
#include "v4/testcode.h"
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
@@ -63,7 +64,6 @@ static v4_heat h;
|
||||
static v4_text tx;
|
||||
static v4_cell w_key, w_key_end, w_fault, capsule_latest;
|
||||
static v4_cell w_idle; /* (IDLE): where the node waits for a message */
|
||||
static v4_cell w_idle_end; /* (PASS-ON): the word after it */
|
||||
static char out[V4_CONSOLE_CAP + 1];
|
||||
|
||||
/* STORAGE (MESH.md section 8). The node asks its kernel for its blocks, and
|
||||
@@ -190,17 +190,81 @@ static const char *node_name(v4_cell xt)
|
||||
static char typed[8192]; /* typed and not yet taken */
|
||||
static unsigned typed_len;
|
||||
static int line_open; /* a line has been sent and has not ended */
|
||||
static v4_message to_node, from_node; /* the message going in, and the one coming out */
|
||||
static unsigned to_sent; /* how many words of to_node the node has taken */
|
||||
static v4_message to_node; /* the message going in */
|
||||
static char shown[V4_CONSOLE_CAP]; /* what the node has printed since the console last looked */
|
||||
static unsigned shown_len;
|
||||
static int shown_dropped;
|
||||
static int text_ended; /* how the text ended, when it has */
|
||||
|
||||
/* 1 if the node is where it waits for a message, with nothing given it. */
|
||||
/* THE WIRES (MESH.md 7d.5). The node is alone and has no fabric: these
|
||||
* tests keep the queues of its console's wire, do what it asks of them, and
|
||||
* are what is on the other end, as v4/system/boot.c is for the products.
|
||||
* Every other port has no one on it. */
|
||||
static v4_wire_queue wire_in, wire_out; /* to the node from its console, and from it */
|
||||
|
||||
static int span_ok(v4_cell addr, unsigned count)
|
||||
{
|
||||
if (count == 0) return 1;
|
||||
if (addr < 0 || (v4_ucell)addr > (v4_ucell)(V4_NODE_WORDS - count)) return 0;
|
||||
return !(addr <= PORT + (v4_cell)V4_PORT_HAVE && addr + (v4_cell)count > PORT);
|
||||
}
|
||||
/* Do the operation the node is blocked at. 0: it sleeps on. */
|
||||
static int wire_do(void)
|
||||
{
|
||||
v4_wire_queue *rx = n.wire == CONSOLE_PORT ? &wire_in : 0, *tx = n.wire == CONSOLE_PORT ? &wire_out : 0;
|
||||
v4_cell a = n.wire_a, b = n.wire_b, hd[V4_WIRE_HEADER], how = V4_WIRE_NO_ONE;
|
||||
unsigned cells;
|
||||
|
||||
switch (n.do_op) {
|
||||
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; }
|
||||
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 5:
|
||||
if (!tx) break;
|
||||
cells = span_ok(a, V4_WIRE_HEADER) ? v4_wire_cells(n.mem[a + 6]) : 0;
|
||||
how = cells && span_ok(b, cells - V4_WIRE_HEADER) ? v4_wire_put(tx, &n.mem[a], cells > V4_WIRE_HEADER ? &n.mem[b] : hd) : V4_WIRE_NOT_A_MESSAGE;
|
||||
break;
|
||||
case 6: if (rx) { v4_wire_first(rx); how = V4_WIRE_DONE; } break;
|
||||
case 7: if (rx) how = v4_wire_next(rx); break;
|
||||
case 8: if (!wire_in.arrived) return 0; how = V4_WIRE_DONE; break;
|
||||
case 9:
|
||||
if (!tx) break;
|
||||
if (a < 0 || a > (v4_cell)V4_WIRE_CELLS) { how = V4_WIRE_NO_ROOM; break; }
|
||||
if (v4_wire_room(tx, (unsigned)a) == V4_WIRE_DONE) { how = V4_WIRE_DONE; break; }
|
||||
if (!wire_in.arrived) return 0;
|
||||
how = V4_WIRE_NO_ROOM;
|
||||
break;
|
||||
case 10: if (tx) how = a < 0 ? V4_WIRE_NOT_A_MESSAGE : a <= (v4_cell)V4_WIRE_CELLS ? v4_wire_room(tx, (unsigned)a) : V4_WIRE_NO_ROOM; break;
|
||||
default: how = V4_WIRE_NOT_A_MESSAGE; break;
|
||||
}
|
||||
v4_node_done(&n, how);
|
||||
return 1;
|
||||
}
|
||||
/* One instruction word, and then what the node asked of its wires; as the
|
||||
* fabric orders a step (fabric.c). */
|
||||
static void node_step(void)
|
||||
{
|
||||
v4_node_have(&n, wire_in.used > 0 ? 1u << CONSOLE_PORT : 0u);
|
||||
(void)v4_exec_step_word(&n, &es, &h);
|
||||
if (n.have_fetched) { wire_in.arrived = 0; n.have_fetched = 0; }
|
||||
if (v4_node_doing(&n)) (void)wire_do();
|
||||
}
|
||||
/* The console puts a line on the node's wire. */
|
||||
static int console_put(const v4_message *m)
|
||||
{
|
||||
return v4_wire_put(&wire_in, m->word, m->word + V4_MSG_HEADER) == V4_WIRE_DONE;
|
||||
}
|
||||
|
||||
/* 1 if the node is asleep with nothing to do: where it waits for a message. */
|
||||
static int node_idle(void)
|
||||
{
|
||||
return n.reading && !n.given && n.read_port == V4_PORT_ANY && n.p > w_idle && n.p <= w_idle_end; /* in (IDLE), where it reads */
|
||||
return v4_node_doing(&n) && n.do_op == 8 && wire_in.used == 0 && n.mem[AWAIT_FROM] == 0;
|
||||
}
|
||||
|
||||
/* Run until the text ends, or until it has been inside KEY with nothing to
|
||||
@@ -208,35 +272,27 @@ static int node_idle(void)
|
||||
* for a character, 0: neither. */
|
||||
static int run_line(long max)
|
||||
{
|
||||
static v4_cell hd[V4_WIRE_HEADER], text[V4_MSG_MAX_CHARS / 4u];
|
||||
long steps = 0;
|
||||
unsigned idle = 0;
|
||||
while (steps < max && idle < 64) {
|
||||
(void)v4_exec_step_word(&n, &es, &h);
|
||||
node_step();
|
||||
steps++;
|
||||
if (n.asking && n.ask_port == 0) { kernel_serve(); v4_node_port_served(&n); continue; } /* a request */
|
||||
if (n.asking && n.ask_port == CONSOLE_PORT) { /* a word of a message from the node */
|
||||
v4_cell word = n.request;
|
||||
v4_node_port_served(&n);
|
||||
if (v4_message_word(&from_node, word)) {
|
||||
unsigned i, chars = v4_message_length(&from_node);
|
||||
if (v4_message_type(&from_node) == V4_MSG_OUTPUT) {
|
||||
for (i = 0; i < chars; i++) {
|
||||
if (shown_len < sizeof shown) shown[shown_len++] = v4_message_char(&from_node, i);
|
||||
else shown_dropped = 1;
|
||||
}
|
||||
} else if (v4_message_type(&from_node) == V4_MSG_DONE) {
|
||||
text_ended = (int)from_node.word[V4_MSG_HEADER];
|
||||
from_node.count = 0;
|
||||
last_steps = steps;
|
||||
return 1;
|
||||
if (n.asking) { (void)v4_node_port_gone(&n, 18); continue; } /* a port with no one on it */
|
||||
v4_wire_first(&wire_out);
|
||||
while (v4_wire_take(&wire_out, hd, text, V4_MSG_MAX_CHARS / 4u) == V4_WIRE_DONE) { /* what the node has sent */
|
||||
if (hd[2] == V4_MSG_OUTPUT) {
|
||||
unsigned i;
|
||||
for (i = 0; i < (unsigned)hd[6]; i++) {
|
||||
if (shown_len < sizeof shown) shown[shown_len++] = (char)(((v4_ucell)text[i / 4u] >> (8u * (i % 4u))) & 0xFFu);
|
||||
else shown_dropped = 1;
|
||||
}
|
||||
from_node.count = 0;
|
||||
} else if (hd[2] == V4_MSG_DONE) {
|
||||
text_ended = (int)text[0];
|
||||
last_steps = steps;
|
||||
return 1;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (n.reading && !n.given && to_sent < to_node.count && (n.read_port == CONSOLE_PORT || n.read_port == V4_PORT_ANY)) {
|
||||
v4_node_port_give(&n, CONSOLE_PORT, to_node.word[to_sent++]); /* a word of the message to the node */
|
||||
continue;
|
||||
}
|
||||
if (n.input_pos == n.input_len && n.p >= w_key && n.p < w_key_end) idle++; else idle = 0;
|
||||
}
|
||||
@@ -279,7 +335,7 @@ static void boot_with(unsigned depth)
|
||||
for (i = 0; i < depth; i++) v4_dstack_push(&n.ds, (v4_cell)(0x5A000000 + i));
|
||||
v4_dstack_push(&n.ds, CANARY);
|
||||
v4_node_port_attach(&n, PORT);
|
||||
v4_node_port_status(&n, 0, (1u | 1u << CONSOLE_PORT) * (1u + (1u << V4_PORTS))); /* the kernel and the console take what is written when it is written */
|
||||
v4_wire_reset(&wire_in); v4_wire_reset(&wire_out);
|
||||
n.mem[WORD_DEFINED] = 0; /* no one is told of entries, until a test says so */
|
||||
n.mem[WORD_FORGOTTEN] = 0;
|
||||
known_n = 0;
|
||||
@@ -287,13 +343,13 @@ static void boot_with(unsigned depth)
|
||||
n.mem[LINE_STATUS] = 1;
|
||||
n.mem[ME] = 0; /* it has no number yet: a message to 0 is for it */
|
||||
HOST_MESSAGES_EMPTY(&n);
|
||||
n.mem[REPLY] = PORT + CONSOLE_PORT;
|
||||
n.mem[REPLY] = CONSOLE_PORT;
|
||||
n.p = w_idle; /* waiting for a message */
|
||||
typed_len = 0;
|
||||
line_open = 0;
|
||||
to_node.count = 0; to_sent = 0; from_node.count = 0;
|
||||
to_node.count = 0;
|
||||
shown_len = 0; shown_dropped = 0;
|
||||
for (i = 0; i < 64 && !n.reading; i++) (void)v4_exec_step_word(&n, &es, &h); /* no message waits: it reads its ports, and is blocked */
|
||||
for (i = 0; i < 256 && !node_idle(); i++) node_step(); /* no message waits: it sleeps */
|
||||
}
|
||||
static void boot(void) { boot_with(0); }
|
||||
/* The same with nothing at all on the data stack. */
|
||||
@@ -335,7 +391,7 @@ static const char *say(const char *input)
|
||||
if (!nl) break;
|
||||
len = (unsigned)(nl - typed);
|
||||
if (!v4_message_text(&to_node, 0, 1, V4_MSG_TEXT, typed, len)) return "(line too long)";
|
||||
to_sent = 0;
|
||||
if (!console_put(&to_node)) return "(no room on the wire)";
|
||||
memmove(typed, nl + 1, typed_len - len - 1);
|
||||
typed_len -= len + 1;
|
||||
line_open = 1;
|
||||
@@ -376,8 +432,7 @@ static int post_line(void *self, const char *text, unsigned text_len, char *o, u
|
||||
(void)self;
|
||||
*len = 0;
|
||||
if (post_state_fails && text_len == 25 && memcmp(text, "DECIMAL FORTH DEFINITIONS", 25) == 0) text = "NOSUCHWORD-FOR-STATE ";
|
||||
if (!v4_message_text(&to_node, 0, 1, V4_MSG_TEXT, text, text_len)) return -1;
|
||||
to_sent = 0; from_node.count = 0;
|
||||
if (!v4_message_text(&to_node, 0, 1, V4_MSG_TEXT, text, text_len) || !console_put(&to_node)) return -1;
|
||||
shown_len = 0; shown_dropped = 0;
|
||||
ended = run_line(post_step_limit);
|
||||
if (ended != 1) return -1;
|
||||
@@ -870,7 +925,6 @@ int main(void)
|
||||
printf(" capsule: %ld words\n", (long)v4_text_here(&tx) - 16);
|
||||
if (failures) { printf(" %d checks, %d failures\n", checks, failures); return 1; }
|
||||
w_idle = v4_text_word(&tx, "(IDLE)");
|
||||
w_idle_end = v4_text_word(&tx, "(PASS-ON)"); /* the word after (IDLE) in quit.v4 */
|
||||
w_key = v4_text_word(&tx, "KEY");
|
||||
w_key_end = v4_text_word(&tx, "CR"); /* the word after KEY in core.v4 */
|
||||
w_fault = v4_text_word(&tx, "(FAULTS)");
|
||||
@@ -1153,7 +1207,7 @@ int main(void)
|
||||
(void)say("WORDS\n");
|
||||
for (at = 0; at < plain; at++) if (n.mem[OUT_W + OUT_CELLS + at] != 0) { over++; worst = k; break; }
|
||||
if (n.mem[OUT_PTR] > OUT_W + OUT_CELLS) { over++; worst = k; }
|
||||
if (n.asking || n.doing) { over++; worst = k; } /* it stored to a port */
|
||||
if (n.asking) { over++; worst = k; } /* it stored to a port */
|
||||
CHECK(strcmp(say("1 2 + .\n"), "3 ok\nok> ") == 0, "WORDS with %u values on the stack: the node goes on", k);
|
||||
}
|
||||
CHECK(over == 0, "nothing was ever put past the end of the output buffer (it was, with %u values on the stack)", worst);
|
||||
|
||||
Reference in New Issue
Block a user