fix(v4.0.0): EMIT ran past the output buffer; a message's first word was read before there was room for the rest
The two root causes behind what the reviews of the wait found, both in the code as it stands. Each with a check that failed first. EMIT stored its character and only then looked whether the buffer was exactly full. A flush that ended in a fault -- the stack too deep to begin a message -- left the buffer full, and the fault's own message was then put past its end, with (OUT^) never again at the end to be flushed. The 28 cells after the buffer happen to be unused, so nothing showed; the wait had put the ports there, and the node blocked writing to one. EMIT now sends a buffer it finds full before it stores. AWAIT, and (GATE) when a neighbour is writing, read a message's first word and then used stack that was not known to be there. With 27 or 28 values on the stack, or 28 or 29 calls deep, a line typed during AWAIT was taken and never answered. (ROOM) tries six cells and four return entries first: if they are not there the text ends 'Stack overflow' with nothing read. (GATE) tries the three cells its writing needs before a first word goes; it was safe before only by the order things were done. v4/tools/depthsweep.py runs every message path of the hosted product at every depth of both stacks: each line typed must be answered and the node must come back. hosted-check runs it. 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
6015c87157
commit
0d91608f39
+22
-1
@@ -260,14 +260,27 @@ header CMOVE
|
||||
\ ( port to -- ) take in a message and keep it
|
||||
: (TAKE) (TAKE-HDR) jump (TAKE-KEEP)
|
||||
|
||||
\ ( -- ) ROOM TO TAKE A MESSAGE IN. Once a message's first word has been
|
||||
\ read the rest must be: whoever is writing it waits for that, and a fault
|
||||
\ in the middle leaves the two out of step. Taking one in and keeping it
|
||||
\ needs five cells of the data stack and four entries of the return stack.
|
||||
\ So before a first word is read from within text -- where the stacks may
|
||||
\ be as deep as the text has made them -- six and four are tried: if they
|
||||
\ are not there the fault comes here, with nothing read, and the text
|
||||
\ ends "Stack overflow" as it would have (MESH.md 7c.6).
|
||||
: (ROOM)
|
||||
0 0 0 0 0 0 drop drop drop drop drop drop
|
||||
0 push 0 push 0 push 0 push pop drop pop drop pop drop pop drop ;
|
||||
|
||||
\ ( port -- ) wait until a message may be begun on the port, by its
|
||||
\ address, taking in whatever is being written to this node meanwhile; and
|
||||
\ leave B at the port. The port is kept in (GATE-PORT), not on the stack:
|
||||
\ this is run with the stack as full as EMIT may be.
|
||||
: (GATE)
|
||||
(GATE-PORT) a! !
|
||||
0 0 0 drop drop drop \ what follows the message's first word needs three cells: tried here, before it goes
|
||||
L: (WRITERS) b! @b if QUIET
|
||||
(LOW) (PORT) + dup b! @b (TAKE) jump L
|
||||
(ROOM) (LOW) (PORT) + dup b! @b (TAKE) jump L
|
||||
QUIET: drop
|
||||
(GATE-PORT) a! @ (PORT) - (NEAR) + a! @ if ASK
|
||||
(ME) a! @ - -if GO jump ASK \ its number less this node's
|
||||
@@ -345,9 +358,17 @@ header CMOVE
|
||||
\ prints goes through EMIT. A is kept: the words that print have always
|
||||
\ been free to use it across an EMIT. As v3, only the low 8 bits of the
|
||||
\ character are printed.
|
||||
\ A BUFFER FOUND FULL is sent first. It is full when a flush of it ended
|
||||
\ in a fault -- the stack too deep to begin a message -- and what is printed
|
||||
\ next is that fault's own message: put where (OUT^) points, it would go
|
||||
\ past the buffer's end, and (OUT^) would never again be at the end to be
|
||||
\ flushed (MESH.md 7c.6).
|
||||
header EMIT
|
||||
: EMIT ( c -- )
|
||||
255 and a push push \ A, and then the character, to the return stack
|
||||
(OUT^) b! @b (OUT)+256 xor if STALE drop jump PUT
|
||||
STALE: drop (FLUSH-OUT)
|
||||
PUT:
|
||||
(OUT^) b! @b a! pop !+ a !b \ the character goes where (OUT^) points, which moves on
|
||||
a (OUT)+256 xor if FULL drop pop a! ;
|
||||
FULL: drop (FLUSH-OUT) pop a! ;
|
||||
|
||||
+2
-1
@@ -333,7 +333,8 @@ header AWAIT
|
||||
(A-QUEUE)
|
||||
(A-FOUND) a! @ if WAIT jump (A-END)
|
||||
WAIT: drop
|
||||
L: (PORT)+8 b! @b (PORT)+9 b! @b (PORT) + dup b! SWAP (TAKE-HDR)
|
||||
L: (ROOM) \ before a first word is read: core.v4
|
||||
(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
|
||||
-1 + if TEXT -2 + if ANSWER -1 + if NACK -1 + if GONE drop jump KEEP
|
||||
|
||||
Reference in New Issue
Block a user