fix(v4.0.0): step 6c mended after its review -- what a stuck or gone node can and cannot hold up

The review of step 6c found it did not keep its rule.  Ruled: fix them
all; 16 hops.

- Paying a NACK no longer waits on a lower-numbered neighbour; a node
  that owes one does not sleep, and goes on taking in what it is sent.
- AWAIT looks first through the messages already waiting.
- A node waiting to write to a removed neighbour gets error 18: the
  word that says who is reading also says which ports have anything on
  them.
- A console line lets Hera go when she is blocked on the first word of
  a message (v4_fabric_interrupt_error, a device's `pending`).
- A refused answer is told to the node that waits, not the one that
  answered; one owed to the node itself goes to its own queue.
- A message is passed on by at most 16 nodes, then refused.
- A GONE is believed only when it says it is from the node's centre.
- Tests: the check that tested nothing is replaced; no room for a
  waiting sender; the limit that remains is tested as a limit.
- MESH.md 7b's rule reworded to what holds, 7b.7 says where it falls
  short; README likewise.

make -C v4 test, sanitize, hosted-check; three bare-metal boots.  POST
538 of 538, word_count=317, dict_hash=0xc0769523a47b7dc3 on all six.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
rajames
2026-10-07 22:18:06 -04:00
co-authored by Claude Opus 5.5
parent d343001dea
commit 59727f762a
19 changed files with 1058 additions and 66 deletions
+25
View File
@@ -18,6 +18,7 @@ void v4_fabric_init(v4_fabric *f, v4_place *places, unsigned capacity)
f->place = places;
f->capacity = capacity;
f->gone_error = 0;
f->interrupt_error = 0;
for (i = 0; i < capacity; i++) clear_place(&places[i]);
}
@@ -26,6 +27,11 @@ void v4_fabric_gone_error(v4_fabric *f, v4_cell code)
f->gone_error = code;
}
void v4_fabric_interrupt_error(v4_fabric *f, v4_cell code)
{
f->interrupt_error = code;
}
void v4_fabric_grow(v4_fabric *f, v4_place *places, unsigned capacity)
{
unsigned i;
@@ -171,6 +177,7 @@ static void look_about(v4_fabric *f, unsigned a)
for (k = 0; k < V4_PORTS; k++) {
const v4_wire *w = &f->place[a].wire[k];
if (w->kind != V4_WIRE_NONE) readers |= 1u << (V4_PORTS + k); /* there is something on this port */
if (w->kind == V4_WIRE_DEVICE) {
if (w->device->take) readers |= 1u << k;
} else if (w->kind == V4_WIRE_NODE && awake(f, w->node)) {
@@ -202,6 +209,24 @@ unsigned v4_fabric_step(v4_fabric *f)
for (i = 0; i < f->capacity; i++)
if (awake(f, i) && f->place[i].node->n.reading && !f->place[i].node->n.given) done += hand_over_read(f, i);
/* a node blocked on the first word of a message to a node that is not
* reading, while a device of its own has something for it */
if (f->interrupt_error != 0)
for (i = 0; i < f->capacity; i++) {
v4_node *n;
unsigned k;
if (!awake(f, i)) continue;
n = &f->place[i].node->n;
if (!n->asking || f->place[i].wire[n->ask_port].kind != V4_WIRE_NODE || v4_node_words_since_look(n) != 0) continue;
for (k = 0; k < V4_PORTS; k++) {
const v4_wire *w = &f->place[i].wire[k];
if (w->kind == V4_WIRE_DEVICE && w->device->pending && w->device->pending(w->device->self)) {
done += (unsigned)v4_node_port_gone(n, f->interrupt_error);
break;
}
}
}
/* a node blocked at one port that nothing is wired to */
if (f->gone_error != 0)
for (i = 0; i < f->capacity; i++) {
+9 -2
View File
@@ -120,8 +120,8 @@ v4_cell v4_node_fetch(v4_node *n, v4_cell addr)
{
int port = v4_node_port_index(n, addr);
if (port == (int)V4_PORTS + 1) return (v4_cell)n->last_from;
if (port == (int)V4_PORTS + 2) return (v4_cell)n->writers;
if (port == (int)V4_PORTS + 3) return (v4_cell)n->readers;
if (port == (int)V4_PORTS + 2) { n->since_look = 0; return (v4_cell)n->writers; }
if (port == (int)V4_PORTS + 3) { n->since_look = 0; return (v4_cell)n->readers; }
if (port >= 0) {
/* the word its neighbour wrote; v4_node_read_ready has said it is there */
if (!n->given) return 0;
@@ -179,6 +179,7 @@ void v4_node_port_attach(v4_node *n, v4_cell base)
n->last_from = 0;
n->writers = 0;
n->readers = 0;
n->since_look = 0;
}
void v4_node_port_status(v4_node *n, unsigned writers, unsigned readers)
@@ -221,6 +222,12 @@ void v4_node_born(v4_node *n, v4_cell base)
void v4_node_port_served(v4_node *n)
{
n->asking = 0;
n->since_look++;
}
unsigned v4_node_words_since_look(const v4_node *n)
{
return n->since_look;
}
int v4_node_port_gone(v4_node *n, v4_cell code)