feat(v4.0.0): a line from the console breaks a wait
Text for a node from its console, arriving while the node waits for an answer (AWAIT), is kept as any message is; the wait ends in error 21, Interrupted; and the text is then done as usual. Console text for another node, passing through a node that is waiting, does not end its wait. On the hosted program 5 AWAIT ended the program with "the node stopped". The boot now reports that the line is waiting, the host reads the next line and hands it over, which breaks the wait, and then has the kept line done. The bare-metal host does the same; it is booted in the commit that follows. test_host_mesh.c: 76 checks, both widths and the sanitizers. hosted-check types 5 AWAIT and two more lines: Interrupted, then 3 and 56. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
9efe8aad3a
commit
cbe1cd5e44
@@ -1,5 +1,5 @@
|
||||
# Capsule Block Manifest — Auto-generated
|
||||
<!-- Generated by mkcapsule --manifest 2026-10-07T18:02:47Z -->
|
||||
<!-- Generated by mkcapsule --manifest 2026-10-07T18:09:07Z -->
|
||||
<!-- DO NOT EDIT — re-run mkcapsule --manifest to refresh. -->
|
||||
<!-- Hand-written justifications and immutability notes live -->
|
||||
<!-- in MANIFEST.md alongside this auto-generated index. -->
|
||||
|
||||
Binary file not shown.
@@ -200,6 +200,8 @@ hosted-check: $(foreach i,$(HOSTED_ISAS),$(BINDIR)/boot-$(i).txt) $(BINDIR)/star
|
||||
@grep -q "^ok> UNKNOWN WORD: 'T{'$$" $(BINDIR)/after-post-amd64.txt || { echo "hosted-check: POST's harness is on the node"; exit 1; }
|
||||
@printf '5 7 PORT!\n1 2 + .\n' | $(BINDIR)/starforth4-amd64 > $(BINDIR)/noone-amd64.txt 2>&1; true
|
||||
@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; }
|
||||
@$(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; }
|
||||
|
||||
+10
-1
@@ -242,13 +242,22 @@ header SEND-ON
|
||||
\ or a GONE about it from this node's centre, error 20, Node gone. A NACK
|
||||
\ about another node is counted, and a GONE about another has its way
|
||||
\ forgotten, as they would be if this node were not waiting.
|
||||
\ AND A LINE FROM THE CONSOLE BREAKS IT (7b.6): text for this node from its
|
||||
\ console is kept, as any message is, the wait ends in error 21,
|
||||
\ Interrupted, and the text is then done as usual. It is the one way to
|
||||
\ end a wait on a node that is alive and never answers, short of Hera's
|
||||
\ killing that node.
|
||||
header AWAIT
|
||||
: AWAIT
|
||||
(AWAIT-FROM) a! !
|
||||
L: (PORT)+8 b! @b (PORT)+9 b! @b (PORT) + dup b! SWAP (TAKE-HDR)
|
||||
(MQ-HDR) a! @ (ME) a! @ xor if A1 drop jump KEEP
|
||||
A1: drop (MQ-HDR)+2 a! @ \ for this node: its type
|
||||
-3 + if ANSWER -1 + if NACK -1 + if GONE drop jump KEEP
|
||||
-1 + if TEXT -2 + if ANSWER -1 + if NACK -1 + if GONE drop jump KEEP
|
||||
TEXT: drop (CONSOLE) a! @ if C0 jump C1 \ text: from the console?
|
||||
C0: drop (MSG)+1 a! @ \ with none told of, whoever sent the text being done
|
||||
C1: (MQ-HDR)+1 a! @ xor if BREAK drop jump KEEP
|
||||
BREAK: drop (TAKE-KEEP) 0 (AWAIT-FROM) a! ! NODE-ERROR b! 21 !b ; \ kept, to be done next; and the wait is over
|
||||
ANSWER: drop (MQ-HDR)+1 a! @ (AWAIT-FROM) a! @ xor if A3 drop jump KEEP
|
||||
A3: drop (MQ-HDR)+6 a! @ -4 + if A4 drop jump KEEP
|
||||
A4: drop 0 (AWAIT-FROM) a! ! @b ; \ its one word
|
||||
|
||||
@@ -77,8 +77,18 @@ int v4_boot_run(const v4_boot *b);
|
||||
#define V4_BOOT_LINE_TOO_LONG (-1) /* more than the node's input buffer holds; nothing was run */
|
||||
#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 */
|
||||
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
|
||||
* the line has not ended: the node is waiting at its ports for an answer,
|
||||
* and here there is no one but the console to end that. The host reads the
|
||||
* next line and hands it over as usual: that breaks the wait, and what is
|
||||
* returned is how the line that was waiting ended -- in an error,
|
||||
* Interrupted. The node has kept the line just handed over; the host then
|
||||
* calls v4_boot_line with no text (0, 0) to have it done, and that returns
|
||||
* how it ended, which may be V4_BOOT_LINE_WAITING again. */
|
||||
|
||||
/* The dictionary hash of a node started from `im`. */
|
||||
uint64_t v4_boot_dict_hash(const v4_node *n, const v4_image *im);
|
||||
|
||||
|
||||
+3
-1
@@ -101,7 +101,8 @@ static int run_line(const v4_boot *b, const char *text, unsigned len, char *keep
|
||||
uint64_t steps = 0;
|
||||
unsigned sent = 0, i;
|
||||
|
||||
if (!v4_message_text(&out, 0, CONSOLE_ID, V4_MSG_TEXT, text, len)) return V4_BOOT_LINE_TOO_LONG;
|
||||
if (!text) out.count = 0; /* no new line: the node has one it kept (boot.h) */
|
||||
else if (!v4_message_text(&out, 0, CONSOLE_ID, V4_MSG_TEXT, text, len)) return V4_BOOT_LINE_TOO_LONG;
|
||||
in.count = 0;
|
||||
for (;;) {
|
||||
(void)v4_exec_step_word(n, b->es, b->h);
|
||||
@@ -150,6 +151,7 @@ static int run_line(const v4_boot *b, const char *text, unsigned len, char *keep
|
||||
continue;
|
||||
}
|
||||
if (n->read_port != CONSOLE_PORT && n->read_port != KERNEL_PORT && v4_node_port_gone(n, V4_ERROR_NO_ONE)) continue; /* reading one port, with no one on it */
|
||||
if (n->read_port == V4_PORT_ANY) return V4_BOOT_LINE_WAITING; /* waiting for an answer: only the console can end that */
|
||||
return V4_BOOT_LINE_STOPPED; /* waiting for what will not come */
|
||||
}
|
||||
if (v4_image_waiting(n, b->im)) { /* the text is reading the keyboard */
|
||||
|
||||
@@ -317,6 +317,17 @@ int main(void)
|
||||
CHECK(is_empty(tell(11, ": P12 S\" 1 DROP\" 12 SEND ; P12")) && pool[1].n.mem[REFUSED] == refused + 1, "having forgotten the way to 12, what node 11 sends there goes toward its centre, which has no way either, and is refused");
|
||||
CHECK(is_empty(tell(10, "12 2 ROUTE")) && is_empty(tell(11, "12 3 ROUTE")) && strcmp(tell(12, "7 8 * ."), "56 ") == 0, "told the way again, they reach 12 as before");
|
||||
}
|
||||
/* a line from the console breaks a wait (MESH.md 7b.6) */
|
||||
CHECK(is_empty(tell(10, "12 AWAIT .")) && ended == 0 && pool[0].n.mem[AWAIT_FROM] == 12 && waiting(hera), "node 10 waits for an answer from 12, which is not going to send one");
|
||||
CHECK(is_empty(tell(11, "20 22 + .")) && ended == 0 && pool[0].n.mem[AWAIT_FROM] == 12, "text from the console for node 11 comes to node 10 on its way: node 10 keeps it, and goes on waiting");
|
||||
{
|
||||
const char *r = tell(10, "65 EMIT");
|
||||
CHECK(strstr(r, "Interrupted\n") != NULL && pool[0].n.mem[AWAIT_FROM] == 0, "text from the console for node 10 itself ends its wait, with an error that says so: \"%s\"", r);
|
||||
CHECK(strstr(r, "A") != NULL && strstr(r, "42 ") != NULL && ended == 3, "the line is then done as usual, and so is what was kept for node 11: \"%s\", %d ended", r, ended);
|
||||
CHECK(strstr(r, "Interrupted\n") < strstr(r, "A"), "the wait ends before the line that ended it is done");
|
||||
}
|
||||
CHECK(strcmp(tell(10, "1 2 + ."), "3 ") == 0 && waiting(hera) && waiting(mid) && waiting(far), "and all three go on as before");
|
||||
|
||||
CHECK(strstr(tell(10, "5 NO-ROUTE"), "ERROR") == NULL && strcmp(tell(11, "20 22 + ."), "42 ") == 0, "forgetting the way to a node there was no way to changes nothing");
|
||||
|
||||
printf(" %d checks, %d failures\n", checks, failures);
|
||||
|
||||
@@ -75,6 +75,21 @@ int main(void)
|
||||
len = strlen(line);
|
||||
if (len && line[len - 1] == '\n') len--;
|
||||
how = v4_boot_line(&boot, line, (unsigned)len);
|
||||
/* The line is waiting for an answer no one here can send. The next
|
||||
* line typed breaks the wait: the line that was waiting ends in an
|
||||
* error, and then the line just typed is done (boot.h). */
|
||||
while (how == V4_BOOT_LINE_WAITING) {
|
||||
fflush(stdout);
|
||||
if (!fgets(line, sizeof line, stdin)) { putchar('\n'); return 0; }
|
||||
len = strlen(line);
|
||||
if (len && line[len - 1] == '\n') len--;
|
||||
how = v4_boot_line(&boot, line, (unsigned)len);
|
||||
if (how == V4_BOOT_LINE_TOO_LONG) { fputs("Line too long\n ERROR\n", stdout); how = V4_BOOT_LINE_WAITING; continue; }
|
||||
if (how == V4_TEXT_ERROR) fputs(" ERROR\nok> ", stdout);
|
||||
else if (how == V4_TEXT_COMPLETED) fputs(" ok\nok> ", stdout);
|
||||
else break;
|
||||
how = v4_boot_line(&boot, 0, 0);
|
||||
}
|
||||
if (how == V4_TEXT_COMPLETED) fputs(" ok\n", stdout);
|
||||
if (leaving) { fflush(stdout); return 0; }
|
||||
else if (how == V4_TEXT_ERROR) fputs(" ERROR\n", stdout);
|
||||
|
||||
Reference in New Issue
Block a user