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
+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 ---- */