fix(v4.0.0): step 6d after its review -- no error in the middle of the messages waiting, no offer left standing
Ruled 2026-10-08: A, B, C, D and the guard for a half message. Each with a scene in test_host_mesh.c that failed first. A. The words of a message after its first are written from the passes over the messages waiting by (!W): an offer and a wait for that offer only (a new engine address). A reader removed in mid-message makes no error; the rest is let go and counted. Before, the error left the messages waiting in pieces and the node going round for ever. B. A store to the wait withdraws every offer; (GATE1) and (PAY-SET) do it first. Before, text begun after a node went round without sleeping could go to the port of an older offer: text for one node was done by another. C. What a finished text printed is put with the messages waiting, as how it ended is. With no room either is let go and counted; neither is begun as a message from (FINISH) any more, where a stuck node on the way held the whole node. D. Only what a sender is owed from an earlier text -- its output, how it ended -- holds that sender's next text back; a GONE does not; and what cannot be noted holds nothing. A node's own messages keep the order they were made in. - A message half taken in when its writer is removed is let go ((MQ-MEND)), not kept as if whole. - Smaller: a refused line no longer ends a waiting text 'Interrupted'; a node's own queued messages may be passed on by 16 nodes, not 15; no value is left on the stack when a port goes in mid-refusal. make -C v4 test and sanitize at both widths, hosted-check. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
380f1cc6ec
commit
2cf37aab4f
+7
-3
@@ -147,11 +147,15 @@ static unsigned hand_over_write(v4_fabric *f, unsigned a)
|
||||
static unsigned hand_over_read(v4_fabric *f, unsigned a)
|
||||
{
|
||||
v4_node *n = &f->place[a].node->n;
|
||||
unsigned first = n->read_port == V4_PORT_ANY ? 0 : n->read_port;
|
||||
unsigned last = n->read_port == V4_PORT_ANY ? V4_PORTS - 1 : n->read_port;
|
||||
unsigned first;
|
||||
unsigned last;
|
||||
unsigned k;
|
||||
v4_cell value;
|
||||
|
||||
if (n->read_port > V4_PORT_ANY) return 0; /* the wait for an offer only: it reads no port */
|
||||
first = n->read_port == V4_PORT_ANY ? 0 : n->read_port;
|
||||
last = n->read_port == V4_PORT_ANY ? V4_PORTS - 1 : n->read_port;
|
||||
|
||||
for (k = first; k <= last; k++) {
|
||||
const v4_wire *w = &f->place[a].wire[k];
|
||||
if (w->kind == V4_WIRE_DEVICE && w->device->give && w->device->give(w->device->self, &value)) {
|
||||
@@ -241,7 +245,7 @@ unsigned v4_fabric_step(v4_fabric *f)
|
||||
if (!awake(f, i)) continue;
|
||||
n = &f->place[i].node->n;
|
||||
if ((n->asking && f->place[i].wire[n->ask_port].kind == V4_WIRE_NONE)
|
||||
|| (n->reading && !n->given && n->read_port != V4_PORT_ANY && f->place[i].wire[n->read_port].kind == V4_WIRE_NONE))
|
||||
|| (n->reading && !n->given && n->read_port < V4_PORTS && f->place[i].wire[n->read_port].kind == V4_WIRE_NONE))
|
||||
done += (unsigned)v4_node_port_gone(n, f->gone_error);
|
||||
else if (v4_node_in_wait(n)) { /* an offer on a port with nothing on it */
|
||||
unsigned k;
|
||||
|
||||
+8
-6
@@ -99,6 +99,7 @@ void v4_node_store(v4_node *n, v4_cell addr, v4_cell value)
|
||||
n->offers |= 1u << k;
|
||||
return;
|
||||
}
|
||||
if (port == (int)V4_PORT_WAIT) { n->offers = 0; return; } /* a store to the wait: every offer is withdrawn */
|
||||
if (port >= 0) { v4_node_fault(n, V4_FAULT_ADDRESS, addr); return; } /* "any", "which port" and the rest are not written to */
|
||||
}
|
||||
/* The stack registers: a store empties the stack. -1 when not attached. */
|
||||
@@ -128,7 +129,7 @@ v4_cell v4_node_fetch(v4_node *n, v4_cell 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_PORT_WAIT) {
|
||||
if (port == (int)V4_PORT_WAIT || port == (int)V4_PORT_WAIT1) {
|
||||
/* the wait: the word that came, or 0 for an offer taken */
|
||||
if (!n->given) return 0;
|
||||
n->given = 0;
|
||||
@@ -206,18 +207,19 @@ void v4_node_port_status(v4_node *n, unsigned writers, unsigned readers)
|
||||
|
||||
int v4_node_port_index(const v4_node *n, v4_cell addr)
|
||||
{
|
||||
if (n->port < 0 || addr < n->port || addr > n->port + (v4_cell)V4_PORT_WAIT) return -1;
|
||||
if (n->port < 0 || addr < n->port || addr > n->port + (v4_cell)V4_PORT_WAIT1) return -1;
|
||||
return (int)(addr - n->port);
|
||||
}
|
||||
|
||||
int v4_node_read_ready(v4_node *n, v4_cell addr)
|
||||
{
|
||||
int port = v4_node_port_index(n, addr);
|
||||
if (port < 0 || (port > (int)V4_PORT_ANY && port != (int)V4_PORT_WAIT)) return 1;
|
||||
if (port < 0 || (port > (int)V4_PORT_ANY && port != (int)V4_PORT_WAIT && port != (int)V4_PORT_WAIT1)) return 1;
|
||||
if (n->given && (port >= (int)V4_PORT_ANY || (unsigned)port == n->given_port)) return 1;
|
||||
n->reading = 1;
|
||||
n->waiting = port == (int)V4_PORT_WAIT; /* the wait is a read of any port, with offers standing */
|
||||
n->read_port = n->waiting ? V4_PORT_ANY : (unsigned)port;
|
||||
n->waiting = port >= (int)V4_PORT_WAIT; /* the wait is a read of any port, with offers standing; */
|
||||
n->read_port = port == (int)V4_PORT_WAIT ? V4_PORT_ANY /* the wait for an offer only reads no port at all */
|
||||
: (unsigned)port;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -288,7 +290,7 @@ int v4_node_port_gone(v4_node *n, v4_cell code)
|
||||
return 1;
|
||||
}
|
||||
if (n->asking) n->asking = 0;
|
||||
else if (n->reading && !n->given && n->read_port != V4_PORT_ANY) n->reading = 0;
|
||||
else if (n->reading && !n->given && n->read_port < V4_PORTS) n->reading = 0;
|
||||
else return 0;
|
||||
v4_node_store(n, n->error_reg, code); /* raises it: P is the node's handler now */
|
||||
return 1;
|
||||
|
||||
Reference in New Issue
Block a user