fix(v4.0.0): step 6e final review -- texts begun in order; a word no one will read lets its writer go; a cut wire wakes a wait for room; a full console wire is a refusal, not a fault

The final review of step 6e, by a fresh reviewer told to break it, and
what was ruled: fix them all, test first.  Each has a test that failed
before the mend.

- Two texts from one node could be begun in the wrong order when room
  for their answers came between the node's look at the first and at the
  second.  Texts whose answers go by the same port are begun in the order
  they came: (D-MASK), quit.v4.  102 of 700 timings were wrong before.
- A word written with PORT! to a node that speaks whole messages, or to
  a console's port, blocked its writer for ever and cut off every node
  beyond it.  It is error 18 now, whether that node is asleep, running
  or stuck: fabric.c, node.c.
- A node asleep for room on one wire is woken when another of its wires
  is cut.
- A line there was no room for on the lone node's wire was reported as a
  fault and ended the hosted program.  It is Message refused, and the
  prompt comes back: V4_BOOT_LINE_REFUSED.  hosted-check has the case;
  no bare-metal boot has typed it.
- Ruled: text behind held output for the same port is refused though it
  would fit.  MESH.md 7d.4 said otherwise and is corrected.
- The removal test also cuts and re-makes a wire, and has an exchange in
  which output is held at a full wire: 26,814 disturbances in all.
- MESH.md and v4/README.md no longer say that a stuck node holds up no
  other node, that only printing waits, or that no node is left waiting
  on one that is gone; 7d.6 lists the limits and what the products do
  that they did not before.

make -C v4 test and sanitize at both widths, hosted-check, lint.  Three
bare-metal boots: logs/20261013-194827 (amd64), -195131 (aarch64),
-195518 (riscv64).  POST 538 of 538, word_count=317,
dict_hash=0xe5a57f3eb72ff94a on all six; the three sessions are the same
character for character.  The boots were built with the working tree's
uncommitted change to kernel/Makefile that adds the ISA to the kernel's
version line.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
rajames
2026-10-13 20:02:33 -04:00
co-authored by Claude Opus 5.5
parent 06da5700bc
commit 66b26f823c
22 changed files with 1206 additions and 103 deletions
+3
View File
@@ -78,6 +78,9 @@ int v4_boot_run(const v4_boot *b);
#define V4_BOOT_LINE_STOPPED (-2) /* the node stopped on a fault, or never finished */
#define V4_BOOT_LINE_NO_INPUT (-3) /* the line read the keyboard and there is no more input */
#define V4_BOOT_LINE_WAITING (-4) /* the line is waiting for an answer (AWAIT) that no one here can send: see below */
#define V4_BOOT_LINE_REFUSED (-5) /* the node's wire has no room for the line: it was not put, and nothing was run. The node
* is taking nothing -- it waits for an answer and does not know these lines for its
* console's -- and its wire has filled (MESH.md 7d.6). It has not stopped. */
int v4_boot_line(const v4_boot *b, const char *text, unsigned len);
/* A LINE THAT WAITS (docs/v4.0.0/MESH.md 7b.6). V4_BOOT_LINE_WAITING means
+21 -6
View File
@@ -3,9 +3,10 @@
*
* On the fabric every node runs at the same time and a node with nothing to
* do is blocked at a port. Here that is: every node that is awake and not
* blocked executes one instruction word, and then every write that has a
* reader waiting on the other end of its wire is handed over. There is no
* choice of whose turn it is.
* blocked executes one instruction word; then every operation on a whole
* message that a node asked for is done (WHOLE MESSAGES, below); and then
* every word written that has a reader waiting on the other end of its wire
* is handed over. There is no choice of whose turn it is.
*
* THE GEOMETRY IS DATA. Which port connects to what is a table, and it can
* be changed while the nodes run; nodes can be added and removed while they
@@ -50,6 +51,7 @@ typedef struct {
typedef struct {
v4_fabric_node *node; /* 0: no node here */
int asleep;
int cut; /* not 0: a wire of this node's has been cut since it was last woken by that (operation 9) */
v4_wire wire[V4_PORTS];
} v4_place;
@@ -97,6 +99,19 @@ int v4_fabric_device_take(v4_fabric *f, unsigned node, unsigned port, v4_cell *h
* is how a fabric begins, nothing is raised. */
void v4_fabric_gone_error(v4_fabric *f, v4_cell code);
/* A WORD NO ONE WILL READ. The same error is raised on a node that can take
* one and is blocked writing a word to a port whose other end will not read
* it: a device that takes no words (its `take` is 0: it speaks whole
* messages only), or a node that speaks whole messages -- one that has
* asked for a whole-message operation (below) since it was born, whatever
* it is doing now: asleep at its wires, running, stuck in a loop, or put to
* sleep by the fabric. Such a node does not read words, and before this a
* word written to one blocked its writer for ever (found by the final
* review of step 6e). A node that has asked for none yet may still read:
* the writer waits for it. That is a newborn node being sent its nucleus
* a word at a time, which executes from its ports and has asked nothing of
* a wire. */
/* More room: `places`, of `capacity` no less than before, takes over; every
* node keeps its number and its wiring. */
void v4_fabric_grow(v4_fabric *f, v4_place *places, unsigned capacity);
@@ -149,9 +164,9 @@ void v4_fabric_wake(v4_fabric *f, unsigned id);
* and 1 at once if it can never have it. Otherwise the node sleeps until a
* message has come, as for 8, or a message has come off ANY of the wires
* that lead from it -- taken, moved on or dropped by whoever reads that wire
* -- since it was last woken that way; then it is answered 0 if the wire it
* named now has the room and 1 if not, and it asks which wire has what
* (10). A node may be waiting to put on several wires at once and can name
* -- since it was last woken that way, or one of its wires has been cut;
* then it is answered 0 if the wire it named now has the room and 1 if not,
* and it asks which wire has what (10). A node may be waiting to put on several wires at once and can name
* only one. A wake-up that finds nothing to do is answered by asking
* again.
*
+5 -2
View File
@@ -125,6 +125,8 @@ typedef struct {
unsigned wire; /* the port that A, B and Do are about; V4_PORTS if what was stored is not a port */
v4_cell wire_a, wire_b; /* A and B */
int doing; /* non-zero: blocked, having stored `do_op` to Do */
int spoke; /* non-zero: it has asked for a whole-message operation since it was born: it is a node that
* speaks whole messages, and not one that words are written to (fabric.h) */
v4_cell do_op; /* the operation */
v4_cell how; /* how the last one went */
unsigned have; /* which wires had a message waiting, as the fabric last said: bit k for port k */
@@ -217,8 +219,9 @@ void v4_node_console_attach(v4_node *n, v4_cell addr);
* Have fetch: which wires have a message
* waiting, bit k for port k, as the fabric
* last said (v4_node_have)
* The two before them are how a node looks before it writes (MESH.md 7a), as the
* F18's io register lets it. They are what whoever connects the ports last
* The two before them let a node that writes a word at a time see whether a
* neighbour is waiting (MESH.md 7a), as the F18's io register does; a node that
* speaks whole messages has no use for them. They are what whoever connects the ports last
* said, v4_node_port_status; fetching them waits for nothing and changes
* nothing. Of the whole-message addresses How and Have are read only: a
* store to either is an address fault, as to "any port".