fix(v4.0.0): 29 values left on the stack hung the node; and what the review of step 6d found

The review of step 6d found it not sound.  Mended here, with a check
that failed first: a text that left 29 values on the stack hung the
node for ever, on the products too -- the passes over the messages
waiting that follow every text need four cells, and a stack fault in the
middle of one left the messages in pieces.  Such a text ends 'Stack
overflow', as before step 6d.  hosted-check types it.

The other findings are open and are written under step 6d in MESH.md
section 10; the README no longer says the aim is met.

make -C v4 test and sanitize at both widths, hosted-check.  The bare
metal boots have not been made again.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
rajames
2026-10-08 01:59:28 -04:00
co-authored by Claude Opus 5.5
parent 2b587206ee
commit 380f1cc6ec
6 changed files with 59 additions and 3 deletions
Binary file not shown.
+31
View File
@@ -1256,6 +1256,37 @@ Each is tested, committed and pushed before the next.
`hosted-check`, lint; three bare-metal boots, `logs/20261008-011558`
(amd64), `-011833` (aarch64), `-012159` (riscv64). On all six: POST
538 of 538, `word_count=317`, `dict_hash=0x6a39c0bb9d183418`.
- **The review (2026-10-08) found the step not sound as built.** One
finding is mended, the rest are open and the claims of 7b and 7c that
"a stuck node holds up no other" are not yet true in the cases below.
- *Mended.* A line that left 29 values on the stack hung the node for
ever, on the products too: the passes over the messages waiting
that now follow every text need four cells, and a stack fault in
the middle of one left the messages in pieces. Such a text now
ends "Stack overflow", as it did before step 6d
(`test_host_quit.c`; `hosted-check`). The boots above were made
before this and do not show it.
- *Open: a node removed while a message is being passed on to it*
leaves the node passing it on going round for ever: the error
comes in the middle of writing the message out of the messages
waiting, and leaves them in pieces.
- *Open: offers left standing.* When a node lets a message go and
then begins text without having slept, what it had offered is
still offered, and the text's own message can go to the wrong
port. The review saw text meant for one node done by another.
- *Open: what a finished text printed* is still begun as a message
from text is, and if the way to the console is through a stuck
node the node is held and cannot be typed to.
- *Open: with more than eight messages of its own waiting* (answers,
or the GONEs of `KILL`) a node begins no text at all; on Hera that
cannot be undone from the console.
- *Open, smaller:* an error while a refusal is being written leaves
a value on the stack; with no room at all, how text ended is
begun as a message and can be held by a stuck sender; a line that
was itself refused for want of room can still end a waiting text
"Interrupted"; a message a node makes itself may be passed on by
15 nodes, not 16; the flood's figures in 7a are stale (278 arrive
now) and `(LOST)` counts some messages more than once.
7. **A second unit; scaling while running; sleep, wake and kill by
command.**
+2
View File
@@ -202,6 +202,8 @@ hosted-check: $(foreach i,$(HOSTED_ISAS),$(BINDIR)/boot-$(i).txt) $(BINDIR)/star
@grep -q '^ok> No one on that port$$' $(BINDIR)/noone-amd64.txt && grep -q '^ok> 3 ok$$' $(BINDIR)/noone-amd64.txt || { echo "hosted-check: a write to a port with no one on it was not an error the node came back from"; cat $(BINDIR)/noone-amd64.txt | tail -4; exit 1; }
@printf '5 AWAIT\n1 2 + .\n7 8 * .\n' | $(BINDIR)/starforth4-amd64 > $(BINDIR)/await-amd64.txt 2>&1; true
@grep -q '^ok> Interrupted$$' $(BINDIR)/await-amd64.txt && grep -q '^ok> 3 ok$$' $(BINDIR)/await-amd64.txt && grep -q '^ok> 56 ok$$' $(BINDIR)/await-amd64.txt || { echo "hosted-check: a line typed while the node waited did not break the wait and then run"; tail -6 $(BINDIR)/await-amd64.txt; exit 1; }
@printf '1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29\n1 2 + .\n' | $(BINDIR)/starforth4-amd64 > $(BINDIR)/deep-amd64.txt 2>&1; true
@grep -q '^ok> Stack overflow$$' $(BINDIR)/deep-amd64.txt && grep -q '^ok> 3 ok$$' $(BINDIR)/deep-amd64.txt || { echo "hosted-check: a line that left 29 values on the stack was not an error the node came back from"; tail -4 $(BINDIR)/deep-amd64.txt; exit 1; }
@$(BINDIR)/starforth4-postfail < /dev/null > $(BINDIR)/postfail.txt; test $$? -ne 0 || { echo "hosted-check: a boot with a failing case did not fail"; exit 1; }
@grep -q '^POST FAIL: fail.wrong out<5 5 $$' $(BINDIR)/postfail.txt || { echo "hosted-check: the failing case was not named"; cat $(BINDIR)/postfail.txt; exit 1; }
@grep -q '^POST FAIL: fail.many out<QQQQ' $(BINDIR)/postfail.txt || { echo "hosted-check: the case that prints too much was not named"; exit 1; }
+4 -3
View File
@@ -146,9 +146,10 @@ is gone; a node believes that only from its centre.
**The wait** (`MESH.md` 7c, step 6d). A node does not write the first word
of a message: it offers it, and sleeps until it is taken or a word comes
for it. What a node has to pass on, the answers it has to give and the
refusals it owes all wait with it, offered, and go when they can. So a
node that is stuck holds up no other: only text that must itself send to
the stuck node waits, and on Hera a typed line ends that.
refusals it owes all wait with it, offered, and go when they can. The aim
is that a node that is stuck holds up no other. **It is not yet met in
every case:** the review of step 6d found several where a node is still
held or goes wrong, listed under step 6d in `MESH.md` section 10.
**Blocks** (`MESH.md` step 6). The block words are the kernel's, as they
are for a v3 VM: `BLOCK`, `BUFFER`, `UPDATE`, `SAVE-BUFFERS` and
+7
View File
@@ -54,6 +54,11 @@ macro R-CLEAR RSTACK-DEPTH b! a !b endmacro
\ stops compiling first; it is always jumped to, never called, by
\ something that has emptied the return stack (or by a fault, which empties
\ it). (LINE-STATUS) keeps how the last text ended.
\ A TEXT MAY LEAVE 28 VALUES ON THE STACK. What a node does between texts
\ -- (IDLE)'s passes over the messages waiting -- needs four cells of it,
\ and a stack fault in the middle of a pass would leave those messages in
\ pieces. So a text that leaves more ends here in "Stack overflow", with
\ the stack emptied, as a fault would end it.
\ THE MESSAGE SAYING HOW IT ENDED is not written here: whoever sent the
\ text may be stuck, and this node would wait on it. It is put with the
\ messages waiting and passed on from (IDLE), as any message for another
@@ -61,6 +66,7 @@ macro R-CLEAR RSTACK-DEPTH b! a !b endmacro
\ another text from the same sender until it has gone ((MINE?)): so what
\ one sender is told comes in the order the texts were done.
: (FINISH) ( s -- )
DSTACK-DEPTH a! @ -30 + -if OVER drop \ what follows needs four cells of the stack: with
1 (QUIET) a! ! \ from here an error is not this text's: see (RAISED)
(LINE-STATUS) b! dup !b
(FLUSH-OUT)
@@ -73,6 +79,7 @@ macro R-CLEAR RSTACK-DEPTH b! a !b endmacro
(MQ!) \ how it ended
pop if FRONT -1 + FOR (MQ@) (MQ!) NEXT jump (IDLE) \ and those go round behind it: it is the oldest
FRONT: drop jump (IDLE)
OVER: drop 0 DSTACK-DEPTH a! ! jump (D-OVER) \ more left on it than may wait, the text ends in that error
FULL: drop \ no room at all: it is offered here, and the node waits
(MSG)+1 a! @ (DONE-PORT) a! @ (GATE)
3 (HDR) 4 !b !b
+15
View File
@@ -1134,6 +1134,21 @@ int main(void)
}
printf(" %u values may wait on the stack while the next line is typed and interpreted\n", most);
CHECK(most + 4u >= V4_DATA_DEPTH, "the prompt and the interpreter take no more than four cells of the stack");
/* one more than may wait: the line ends in an error that says so, and the node goes on (found by the review of step 6d) */
{
char line[4 * V4_DATA_DEPTH + 16];
size_t at = 0;
unsigned m;
const char *r;
boot_bare();
for (m = 0; m < most + 1; m++) at += (size_t)snprintf(line + at, sizeof line - at, "1 ");
snprintf(line + at, sizeof line - at, "\n");
r = say(line);
CHECK(strstr(r, "Stack overflow") != NULL && strstr(r, " ERROR\nok> ") != NULL, "a line that leaves one value more than may wait ends in an error that says so: \"%s\"", r);
CHECK(strcmp(say("1 2 + .\n"), "3 ok\nok> ") == 0, "and the next line is done as usual");
for (m = 0; m < 2000 && !node_idle(); m++) (void)v4_exec_step_word(&n, &es, &h); /* it finishes what it was doing when its answer went */
CHECK(n.mem[MQ_COUNT] == 0 && node_idle(), "with nothing left among the messages waiting, and the node at its ports");
}
}
/* ---- how much of the data stack a line has ---- */