fix(v4.0.0): a waiting node goes on passing on and paying; KILL is not held by a stuck node
Found in the five-node test, each with a check that failed first: - A node with answers it could not give to two stuck senders began no text at all. It now holds back only text from a sender whose own answer is still with it. - KILL waited to tell a stuck node, and a typed line that broke it left the nodes after it untold. GONE is put with the messages waiting, as how text ended is, and goes when it can. - So that 'tell, then wait' still works, AWAIT does what (IDLE) does but for beginning text: it looks through the messages waiting, offers what is to be passed on and what is owed, and sleeps in the wait. A waiting node no longer keeps back what passes through it. - A node in the wait with an offer on a port that turns out to have nothing on it is woken and told so (which port: 2 * V4_PORTS + k); no error is raised on it. (GATE) makes it error 18; (IDLE) and AWAIT let the message go. Before, removing a node raised 'No one on that port' on the unrelated text of a neighbour that had something queued for 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
353783726d
commit
ec817a8264
Binary file not shown.
+12
-8
@@ -251,17 +251,20 @@ header CMOVE
|
|||||||
\ ( port to -- ) take in a message and keep it
|
\ ( port to -- ) take in a message and keep it
|
||||||
: (TAKE) (TAKE-HDR) jump (TAKE-KEEP)
|
: (TAKE) (TAKE-HDR) jump (TAKE-KEEP)
|
||||||
|
|
||||||
\ ( -- flag ) THE WAIT, once. Whatever has been offered stands until
|
\ ( -- how ) THE WAIT, once. Whatever has been offered stands until
|
||||||
\ this returns. Zero: a word came for this node instead; that whole
|
\ this returns. 0: a word came for this node instead; that whole message
|
||||||
\ message has been taken in and kept, (TAKE), and its seven words are
|
\ has been taken in and kept, (TAKE), and its seven words are still in
|
||||||
\ still in (MQ-HDR). Not zero: an offer was taken; B is at the port it
|
\ (MQ-HDR). -1: an offer was taken; B is at the port it was taken on, and
|
||||||
\ was taken on, and (GATE-PORT) holds that port's address.
|
\ (GATE-PORT) holds that port's address. 1: there is nothing on a port
|
||||||
|
\ that had an offer: no one will ever take it.
|
||||||
: (OFFERS)
|
: (OFFERS)
|
||||||
(WAIT) b! @b push (PORT)+9 b! @b \ ( which ) R: the word
|
(WAIT) b! @b push (PORT)+9 b! @b \ ( which ) R: the word
|
||||||
dup -PORTS + -if TAKEN
|
dup -PORTS + -if TAKEN
|
||||||
drop (PORT) + dup b! pop (TAKE) 0 ; \ a word, from that port: the message's first
|
drop (PORT) + dup b! pop (TAKE) 0 ; \ a word, from that port: the message's first
|
||||||
TAKEN: (PORT) + (GATE-PORT) a! ! drop pop drop
|
TAKEN: dup -PORTS + -if NOONE \ ( which k x ) k: the port, by its number
|
||||||
|
drop (PORT) + (GATE-PORT) a! ! drop pop drop
|
||||||
(GATE-PORT) a! @ b! -1 ;
|
(GATE-PORT) a! @ b! -1 ;
|
||||||
|
NOONE: drop drop drop pop drop 1 ;
|
||||||
|
|
||||||
\ ( w port -- ) BEGIN A MESSAGE: the word, its first, is offered on the
|
\ ( w port -- ) BEGIN A MESSAGE: the word, its first, is offered on the
|
||||||
\ port, by its address, until it is taken; whatever is written to this
|
\ port, by its address, until it is taken; whatever is written to this
|
||||||
@@ -274,11 +277,12 @@ header CMOVE
|
|||||||
\ the messages waiting and is done next. It is how a node whose text must
|
\ the messages waiting and is done next. It is how a node whose text must
|
||||||
\ begin a message to a node that is stuck is typed to. Not while (QUIET)
|
\ begin a message to a node that is stuck is typed to. Not while (QUIET)
|
||||||
\ is set: what waits then is no text's doing.
|
\ is set: what waits then is no text's doing.
|
||||||
\ With nothing on the port the fabric raises error 18.
|
\ With nothing on the port it is error 18.
|
||||||
: (GATE1) ( port -- )
|
: (GATE1) ( port -- )
|
||||||
(GATE-PORT) a! !
|
(GATE-PORT) a! !
|
||||||
L: (GATE-WORD) a! @ (GATE-PORT) a! @ -PORTS - 4 + a! ! \ the offer
|
L: (GATE-WORD) a! @ (GATE-PORT) a! @ -PORTS - 4 + a! ! \ the offer
|
||||||
(OFFERS) if CAME drop ;
|
(OFFERS) if CAME -if NOONE drop ;
|
||||||
|
NOONE: drop NODE-ERROR b! 18 !b ; \ no one on that port
|
||||||
CAME: drop
|
CAME: drop
|
||||||
(QUIET) a! @ if TEXTS drop jump L
|
(QUIET) a! @ if TEXTS drop jump L
|
||||||
TEXTS: drop
|
TEXTS: drop
|
||||||
|
|||||||
+50
-52
@@ -118,30 +118,35 @@ macro R-CLEAR RSTACK-DEPTH b! a !b endmacro
|
|||||||
\ text into TIB.
|
\ text into TIB.
|
||||||
\ TEXT IS NOT BEGUN WHILE THE ANSWER TO ITS SENDER'S LAST IS STILL HERE:
|
\ TEXT IS NOT BEGUN WHILE THE ANSWER TO ITS SENDER'S LAST IS STILL HERE:
|
||||||
\ a message this node made itself, (FINISH), is ahead of it, to that
|
\ a message this node made itself, (FINISH), is ahead of it, to that
|
||||||
\ sender. (A-WORD) is whom such a message seen so far is to: -1 none,
|
\ sender. Whom each such message seen so far is to is noted in (PAY-K),
|
||||||
\ -2 more than one node, and then no text is begun. A NACK or a GONE for
|
\ which nothing else is using now, and (A-WORD) is how many: at most as
|
||||||
\ this node is never held back.
|
\ many as there are ports, and with more than that no text is begun. A
|
||||||
|
\ NACK or a GONE for this node is never held back.
|
||||||
: (MINE?)
|
: (MINE?)
|
||||||
0 (A-FOUND) a! ! -1 (A-WORD) a! !
|
0 (A-FOUND) a! ! 0 (A-WORD) a! !
|
||||||
(MQ#) a! @ (A-LEFT) a! !
|
(MQ#) a! @ (A-LEFT) a! !
|
||||||
L: (A-LEFT) a! @ if END drop
|
L: (A-LEFT) a! @ if END drop
|
||||||
(MQ-NEXT)
|
(MQ-NEXT)
|
||||||
(A-FOUND) a! @ if LOOK drop jump KEEP
|
(A-FOUND) a! @ if LOOK drop jump KEEP
|
||||||
LOOK: drop
|
LOOK: drop
|
||||||
(MQ-HDR)+7 a! @ -if THEIRS
|
(MQ-HDR)+7 a! @ -if THEIRS
|
||||||
drop (A-WORD) a! @ 1 + if FIRST \ this node's own:
|
drop (A-WORD) a! @ -PORTS + -if MANY \ this node's own: whom it is to is noted
|
||||||
drop (A-WORD) a! @ (MQ-HDR) a! @ xor if SAME
|
drop (MQ-HDR) a! @ push (A-WORD) a! @ dup 1 + ! (PAY-K) + a! pop !
|
||||||
drop -2 (A-WORD) a! ! jump KEEP
|
jump KEEP
|
||||||
FIRST: drop (MQ-HDR) a! @ (A-WORD) a! ! jump KEEP
|
MANY: drop PORTS-1 2 + (A-WORD) a! ! jump KEEP \ more than can be noted
|
||||||
SAME: drop jump KEEP
|
|
||||||
THEIRS: drop
|
THEIRS: drop
|
||||||
(MQ-HDR) a! @ (ME) a! @ xor if MINE drop jump KEEP
|
(MQ-HDR) a! @ (ME) a! @ xor if MINE drop jump KEEP
|
||||||
MINE: drop
|
MINE: drop
|
||||||
(MQ-HDR)+2 a! @ -1 + if TEXT drop jump TAKE
|
(MQ-HDR)+2 a! @ -1 + if TEXT drop jump TAKE
|
||||||
TEXT: drop
|
TEXT: drop
|
||||||
(A-WORD) a! @ 2 + if HOLD
|
(A-WORD) a! @ if TAKE0 \ no answer is waiting to go
|
||||||
drop (A-WORD) a! @ (MQ-HDR)+1 a! @ xor if HOLD
|
dup -PORTS + -1 + -if HOLD2 drop \ ( n count )
|
||||||
drop
|
(MQ-HDR)+1 a! @ SWAP -1 + (PAY-K) a! \ ( n from count-1 )
|
||||||
|
FOR dup @+ xor if HIT drop NEXT
|
||||||
|
drop jump TAKE \ none of them is to this text's sender
|
||||||
|
HIT: drop drop pop drop jump KEEP
|
||||||
|
HOLD2: drop drop jump KEEP
|
||||||
|
TAKE0: drop
|
||||||
TAKE: 1 (A-FOUND) a! !
|
TAKE: 1 (A-FOUND) a! !
|
||||||
(MQ-HDR) a! @ (MSG) a! ! (MQ-HDR)+1 a! @ (MSG)+1 a! ! (MQ-HDR)+2 a! @ (MSG)+2 a! !
|
(MQ-HDR) a! @ (MSG) a! ! (MQ-HDR)+1 a! @ (MSG)+1 a! ! (MQ-HDR)+2 a! @ (MSG)+2 a! !
|
||||||
(MQ-HDR)+3 a! @ (MSG)+3 a! ! (MQ-HDR)+4 a! @ (MSG)+4 a! ! (MQ-HDR)+5 a! @ (MSG)+5 a! !
|
(MQ-HDR)+3 a! @ (MSG)+3 a! ! (MQ-HDR)+4 a! @ (MSG)+4 a! ! (MQ-HDR)+5 a! @ (MSG)+5 a! !
|
||||||
@@ -150,7 +155,6 @@ macro R-CLEAR RSTACK-DEPTH b! a !b endmacro
|
|||||||
-1 + FOR (MQ@) over a! ! 1 + NEXT
|
-1 + FOR (MQ@) over a! ! 1 + NEXT
|
||||||
drop jump L
|
drop jump L
|
||||||
EMPTY: drop drop jump L
|
EMPTY: drop drop jump L
|
||||||
HOLD: drop
|
|
||||||
KEEP: (A-BACK) (A-TEXT) jump L
|
KEEP: (A-BACK) (A-TEXT) jump L
|
||||||
END: drop (A-FOUND) a! @ ;
|
END: drop (A-FOUND) a! @ ;
|
||||||
|
|
||||||
@@ -239,7 +243,7 @@ macro R-CLEAR RSTACK-DEPTH b! a !b endmacro
|
|||||||
(PAY-SET) (FWD-SET) if SET drop jump L \ one was let go: what is owed has changed
|
(PAY-SET) (FWD-SET) if SET drop jump L \ one was let go: what is owed has changed
|
||||||
SET: drop
|
SET: drop
|
||||||
(MQ#) a! @ (OWED#) a! @ + if BLOCK drop
|
(MQ#) a! @ (OWED#) a! @ + if BLOCK drop
|
||||||
(OFFERS) if CAME drop
|
(OFFERS) if CAME -if CAME drop \ nothing on a port: what was for it is let go next time round
|
||||||
(GATE-PORT) a! @ (PORT) - (PAY-K) + a! @ \ what was offered on the port it was taken on
|
(GATE-PORT) a! @ (PORT) - (PAY-K) + a! @ \ what was offered on the port it was taken on
|
||||||
-if OWED drop (FWD-GO) jump L
|
-if OWED drop (FWD-GO) jump L
|
||||||
OWED: -1 + (PAY-GO) jump L
|
OWED: -1 + (PAY-GO) jump L
|
||||||
@@ -328,10 +332,18 @@ header NO-ROUTE
|
|||||||
|
|
||||||
\ ( about node type -- ) send the node a message of one word, `about`:
|
\ ( about node type -- ) send the node a message of one word, `about`:
|
||||||
\ a NACK is type 4, a GONE type 5 (MESH.md 7b). With no way to the node
|
\ a NACK is type 4, a GONE type 5 (MESH.md 7b). With no way to the node
|
||||||
\ it is error 12.
|
\ it is error 12. It is put with the messages waiting and goes from
|
||||||
|
\ (IDLE), or from AWAIT, as how text ended does ((FINISH)): so telling a
|
||||||
|
\ node that is stuck does not hold up the telling of the next. With no
|
||||||
|
\ room at all it is offered here.
|
||||||
: (SEND1)
|
: (SEND1)
|
||||||
push dup (PORT-FOR) if NOWAY
|
push dup (PORT-FOR) if NOWAY \ ( about node port ) R: type
|
||||||
(GATE) pop (HDR) 4 !b !b ;
|
1 (MQ#) a! @ + -MQ-ROOM + -if FULL drop
|
||||||
|
SWAP (MQ!) (ME) a! @ (MQ!) pop (MQ!) \ to, from, type
|
||||||
|
0 (MQ!) 0 (MQ!) 0 (MQ!) 4 (MQ!) \ four characters long
|
||||||
|
0 SWAP - (MQ!) \ by that port, kept below zero: this node's own
|
||||||
|
(MQ!) ; \ about
|
||||||
|
FULL: drop (GATE) pop (HDR) 4 !b !b ;
|
||||||
NOWAY: drop drop drop pop drop NODE-ERROR b! 12 !b ;
|
NOWAY: drop drop drop pop drop NODE-ERROR b! 12 !b ;
|
||||||
|
|
||||||
\ ( n node -- ) tell the node that node n is gone. It acts on that only
|
\ ( n node -- ) tell the node that node n is gone. It acts on that only
|
||||||
@@ -439,52 +451,38 @@ header SEND-ON
|
|||||||
LEFT: drop NODE-ERROR b! 20 !b ;
|
LEFT: drop NODE-ERROR b! 20 !b ;
|
||||||
|
|
||||||
\ ( node -- how ) WAIT FOR THE NODE'S WORD OF HOW TEXT ENDED: 0 QUIT,
|
\ ( node -- how ) WAIT FOR THE NODE'S WORD OF HOW TEXT ENDED: 0 QUIT,
|
||||||
\ 1 completed, 2 an error. This node is blocked reading its ports until a
|
\ 1 completed, 2 an error: a message of type 3 for this node from that
|
||||||
\ message of type 3 comes for it from that node; every other message that
|
\ node. (AWAIT-FROM) holds the node while it waits, and 0 otherwise.
|
||||||
\ comes meanwhile is kept with the messages waiting. (AWAIT-FROM) holds the
|
|
||||||
\ node while it waits, and 0 otherwise.
|
|
||||||
\ THE WAIT ALSO ENDS, in an error, when the answer is not going to come
|
\ THE WAIT ALSO ENDS, in an error, when the answer is not going to come
|
||||||
\ (MESH.md section 7b): a NACK about that node, error 19, Message refused;
|
\ (MESH.md section 7b): a NACK about that node, error 19, Message refused;
|
||||||
\ or a GONE about it from this node's centre, error 20, Node gone. A NACK
|
\ 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
|
\ about another node is counted, and a GONE about another has its way
|
||||||
\ forgotten, as they would be if this node were not waiting. A GONE is
|
\ forgotten, as they would be if this node were not waiting. A GONE is
|
||||||
\ believed only if this node's centre sent it: (CENTRE).
|
\ believed only if this node's centre sent it: (CENTRE).
|
||||||
\ WHAT HAS COME ALREADY is looked through first, (A-QUEUE): the answer, a
|
|
||||||
\ NACK or a GONE may have been taken in before the wait began, while this
|
|
||||||
\ node was waiting to write, and is then with the messages waiting.
|
|
||||||
\ AND A LINE FROM THE CONSOLE BREAKS IT (7b.6): text for this node from its
|
\ 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,
|
\ console ends the wait in error 21, Interrupted, and is then done as
|
||||||
\ Interrupted, and the text is then done as usual. It is the one way to
|
\ usual. It is the one way to end a wait on a node that is alive and
|
||||||
\ end a wait on a node that is alive and never answers, short of Hera's
|
\ never answers, short of Hera's killing that node.
|
||||||
\ killing that node.
|
\ WHILE IT WAITS THE NODE DOES WHAT (IDLE) DOES, but for beginning text:
|
||||||
|
\ the messages waiting are looked through for what would end the wait,
|
||||||
|
\ (A-QUEUE); what it has for other nodes and the refusals it owes are
|
||||||
|
\ offered, and it sleeps in the wait (MESH.md 7c); what comes is taken in
|
||||||
|
\ and kept, and it looks again. So what passes through a node is not
|
||||||
|
\ kept back by its waiting, and what it sent before it began to wait --
|
||||||
|
\ a GONE, say -- goes.
|
||||||
header AWAIT
|
header AWAIT
|
||||||
: AWAIT
|
: AWAIT
|
||||||
(AWAIT-FROM) a! !
|
(AWAIT-FROM) a! !
|
||||||
(A-QUEUE)
|
L: (A-QUEUE)
|
||||||
(A-FOUND) a! @ if WAIT jump (A-END)
|
(A-FOUND) a! @ if WAIT jump (A-END)
|
||||||
WAIT: drop
|
WAIT: drop
|
||||||
L: (PORT)+8 b! @b (PORT)+9 b! @b (PORT) + dup b! SWAP (TAKE-HDR)
|
(PAY-SET) (FWD-SET) if SET drop jump L \ one was let go: this node may have been told so
|
||||||
(MQ-HDR) a! @ (ME) a! @ xor if A1 drop jump KEEP
|
SET: drop
|
||||||
A1: drop (MQ-HDR)+2 a! @ \ for this node: its type
|
(OFFERS) if CAME -if CAME drop
|
||||||
-1 + if TEXT -2 + if ANSWER -1 + if NACK -1 + if GONE drop jump KEEP
|
(GATE-PORT) a! @ (PORT) - (PAY-K) + a! @
|
||||||
TEXT: drop (A-CONSOLE) (MQ-HDR)+1 a! @ xor if BREAK drop jump KEEP \ text: from the console?
|
-if OWED drop (FWD-GO) jump L
|
||||||
BREAK: drop (TAKE-KEEP) 4 jump (A-END) \ kept, to be done next; and the wait is over
|
OWED: -1 + (PAY-GO) jump L
|
||||||
ANSWER: drop (MQ-HDR)+1 a! @ (AWAIT-FROM) a! @ xor if A3 drop jump KEEP
|
CAME: drop jump L
|
||||||
A3: drop (MQ-HDR)+6 a! @ -4 + if A4 drop jump KEEP
|
|
||||||
A4: drop 0 (AWAIT-FROM) a! ! @b ; \ its one word
|
|
||||||
NACK: drop (MQ-HDR)+6 a! @ -4 + if N4 drop jump KEEP
|
|
||||||
N4: drop @b (AWAIT-FROM) a! @ xor if REFUSED \ about the node waited for?
|
|
||||||
drop (REFUSED) a! @ 1 + ! jump L \ another: counted
|
|
||||||
REFUSED: drop 2 jump (A-END)
|
|
||||||
GONE: drop (MQ-HDR)+6 a! @ -4 + if G4 drop jump KEEP
|
|
||||||
G4: drop @b \ the node that is gone
|
|
||||||
(CENTRE) if NOC (MQ-HDR)+1 a! @ xor if CENTRE \ only its centre is believed
|
|
||||||
drop drop jump L
|
|
||||||
NOC: drop drop jump L
|
|
||||||
CENTRE: drop dup NO-ROUTE
|
|
||||||
(AWAIT-FROM) a! @ xor if LEFT drop jump L
|
|
||||||
LEFT: drop 3 jump (A-END)
|
|
||||||
KEEP: (TAKE-KEEP) jump L
|
|
||||||
|
|
||||||
\ ( w port -- ) write a cell to the port, by its number. It waits until
|
\ ( w port -- ) write a cell to the port, by its number. It waits until
|
||||||
\ the neighbour has taken it. This is how a node is sent a capsule of F18
|
\ the neighbour has taken it. This is how a node is sent a capsule of F18
|
||||||
|
|||||||
+10
-2
@@ -251,8 +251,10 @@ int v4_node_port_gone(v4_node *n, v4_cell code);
|
|||||||
* offers is taken, whichever is first.
|
* offers is taken, whichever is first.
|
||||||
* A word that came is fetched as from "any port", and "which port" says
|
* A word that came is fetched as from "any port", and "which port" says
|
||||||
* where from. When an offer was taken the fetch gives 0 and "which port"
|
* where from. When an offer was taken the fetch gives 0 and "which port"
|
||||||
* gives V4_PORTS + k: the offer on port k. Either way every offer the node
|
* gives V4_PORTS + k: the offer on port k. When there turns out to be
|
||||||
* had is withdrawn. Whoever connects the ports does the handing over
|
* nothing on a port it has an offer on, the fetch gives 0 and "which
|
||||||
|
* port" gives 2 * V4_PORTS + k. Whichever, every offer the node had is
|
||||||
|
* withdrawn. Whoever connects the ports does the handing over
|
||||||
* (fabric.h); a node in the wait is blocked reading "any port" as far as a
|
* (fabric.h); a node in the wait is blocked reading "any port" as far as a
|
||||||
* writer to it can tell.
|
* writer to it can tell.
|
||||||
*
|
*
|
||||||
@@ -263,6 +265,12 @@ int v4_node_in_wait(const v4_node *n);
|
|||||||
* 0, from V4_PORTS + k. 0 if it is not in the wait or has no offer there. */
|
* 0, from V4_PORTS + k. 0 if it is not in the wait or has no offer there. */
|
||||||
int v4_node_offer_taken(v4_node *n, unsigned k);
|
int v4_node_offer_taken(v4_node *n, unsigned k);
|
||||||
|
|
||||||
|
/* There is nothing on port k, where the node has an offer: a node in the
|
||||||
|
* wait that can take an error (v4_node_port_gone says which can) is
|
||||||
|
* unblocked and will fetch 0, from 2 * V4_PORTS + k. No error is raised:
|
||||||
|
* what the node does about it is its own to say. 0 if it was not done. */
|
||||||
|
int v4_node_offer_gone(v4_node *n, unsigned k);
|
||||||
|
|
||||||
/* For a host with one node, whose devices always take what is written: if
|
/* For a host with one node, whose devices always take what is written: if
|
||||||
* the node is in the wait with an offer, it becomes a blocked write of the
|
* the node is in the wait with an offer, it becomes a blocked write of the
|
||||||
* word offered on the lowest such port -- asking, request, ask_port -- and
|
* word offered on the lowest such port -- asking, request, ask_port -- and
|
||||||
|
|||||||
+1
-1
@@ -269,7 +269,7 @@ unsigned v4_fabric_step(v4_fabric *f)
|
|||||||
else if (v4_node_in_wait(n)) { /* an offer on a port with nothing on it */
|
else if (v4_node_in_wait(n)) { /* an offer on a port with nothing on it */
|
||||||
unsigned k;
|
unsigned k;
|
||||||
for (k = 0; k < V4_PORTS; k++)
|
for (k = 0; k < V4_PORTS; k++)
|
||||||
if ((n->offers & (1u << k)) && f->place[i].wire[k].kind == V4_WIRE_NONE) { done += (unsigned)v4_node_port_gone(n, f->gone_error); break; }
|
if ((n->offers & (1u << k)) && f->place[i].wire[k].kind == V4_WIRE_NONE) { done += (unsigned)v4_node_offer_gone(n, k); break; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+14
-2
@@ -243,6 +243,14 @@ int v4_node_offer_taken(v4_node *n, unsigned k)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int v4_node_offer_gone(v4_node *n, unsigned k)
|
||||||
|
{
|
||||||
|
if (n->error_reg < 0 || n->fault_vector < 0 || n->stopped) return 0;
|
||||||
|
if (!v4_node_in_wait(n) || k >= V4_PORTS || !(n->offers & (1u << k))) return 0;
|
||||||
|
v4_node_port_give(n, 2u * V4_PORTS + k, 0);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
int v4_node_offer_as_write(v4_node *n)
|
int v4_node_offer_as_write(v4_node *n)
|
||||||
{
|
{
|
||||||
unsigned k;
|
unsigned k;
|
||||||
@@ -280,8 +288,12 @@ unsigned v4_node_words_since_look(const v4_node *n)
|
|||||||
int v4_node_port_gone(v4_node *n, v4_cell code)
|
int v4_node_port_gone(v4_node *n, v4_cell code)
|
||||||
{
|
{
|
||||||
if (code == 0 || n->error_reg < 0 || n->fault_vector < 0 || n->stopped) return 0;
|
if (code == 0 || n->error_reg < 0 || n->fault_vector < 0 || n->stopped) return 0;
|
||||||
if (n->asking) { n->asking = 0; if (n->offer_asked) { n->offer_asked = 0; n->reading = 0; n->waiting = 0; n->offers = 0; } }
|
if (n->asking && n->offer_asked) { /* what it wrote was an offer: it is told, not faulted */
|
||||||
else if (v4_node_in_wait(n) && n->offers != 0) { n->reading = 0; n->waiting = 0; n->offers = 0; }
|
n->asking = 0; n->offer_asked = 0;
|
||||||
|
v4_node_port_give(n, 2u * V4_PORTS + n->ask_port, 0);
|
||||||
|
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_PORT_ANY) n->reading = 0;
|
||||||
else return 0;
|
else return 0;
|
||||||
v4_node_store(n, n->error_reg, code); /* raises it: P is the node's handler now */
|
v4_node_store(n, n->error_reg, code); /* raises it: P is the node's handler now */
|
||||||
|
|||||||
@@ -547,16 +547,16 @@ int main(void)
|
|||||||
/* a port with nothing on it */
|
/* a port with nothing on it */
|
||||||
fresh();
|
fresh();
|
||||||
a = loaded(0); offer(5, 7); wait_into(OUT); done_mark(); wait_for_ever();
|
a = loaded(0); offer(5, 7); wait_into(OUT); done_mark(); wait_for_ever();
|
||||||
CHECK(v4_asm_ok(&as), "a node that offers on a port nothing is wired to");
|
CHECK(v4_asm_ok(&as) && v4_fabric_wire_device(&f, a, 7, &sink), "a node that offers on a port nothing is wired to (the port it ends by reading has a device that gives nothing)");
|
||||||
for (i = 0; i < 50; i++) (void)v4_fabric_step(&f);
|
for (i = 0; i < 50; i++) (void)v4_fabric_step(&f);
|
||||||
v4_fabric_gone_error(&f, 18);
|
v4_fabric_gone_error(&f, 18);
|
||||||
for (i = 0; i < 50; i++) (void)v4_fabric_step(&f);
|
for (i = 0; i < 50; i++) (void)v4_fabric_step(&f);
|
||||||
CHECK(v4_node_in_wait(&ND(a)->n) && ND(a)->n.faults == 0, "a bare node waits, though the fabric has an error for it");
|
CHECK(v4_node_in_wait(&ND(a)->n) && ND(a)->n.faults == 0, "a bare node waits, though the fabric has an error for it");
|
||||||
v4_node_error_attach(&ND(a)->n, OUT + 20);
|
v4_node_error_attach(&ND(a)->n, OUT + 20);
|
||||||
v4_node_fault_attach(&ND(a)->n, 900);
|
v4_node_fault_attach(&ND(a)->n, 900);
|
||||||
(void)v4_fabric_step(&f);
|
for (i = 0; i < 100 && ND(a)->n.mem[OUT + 2] == 0; i++) (void)v4_fabric_step(&f); /* until it has noted what it was told */
|
||||||
CHECK(!v4_node_in_wait(&ND(a)->n) && ND(a)->n.fault_kind == V4_FAULT_RAISED && ND(a)->n.fault_addr == 18 && ND(a)->n.offers == 0,
|
CHECK(!v4_node_in_wait(&ND(a)->n) && ND(a)->n.faults == 0 && ND(a)->n.offers == 0 && ND(a)->n.mem[OUT] == 0 && ND(a)->n.mem[OUT + 1] == 2 * (v4_cell)V4_PORTS + 5 && ND(a)->n.mem[OUT + 2] == 1,
|
||||||
"one that can take an error has it raised, and its offer is withdrawn");
|
"one that can take an error is woken and told there is nothing on port 5: no error is raised, and its offer is withdrawn: %ld", (long)ND(a)->n.mem[OUT + 1]);
|
||||||
|
|
||||||
/* a host with one node, whose devices always take */
|
/* a host with one node, whose devices always take */
|
||||||
fresh();
|
fresh();
|
||||||
|
|||||||
@@ -306,12 +306,12 @@ int main(void)
|
|||||||
|
|
||||||
/* no room: a node that is waiting keeps what it is sent, and has room for only so much */
|
/* no room: a node that is waiting keeps what it is sent, and has room for only so much */
|
||||||
{
|
{
|
||||||
v4_cell lost = pool[1].n.mem[LOST], refused = pool[0].n.mem[REFUSED], got, not_taken;
|
v4_cell lost = pool[1].n.mem[LOST], refused = pool[0].n.mem[REFUSED], got, not_taken, told_of;
|
||||||
CHECK(is_empty(tell(11, "0 GOT !")) && is_empty(tell(11, "12 AWAIT .")) && ended == 0 && pool[1].n.mem[AWAIT_FROM] == 12, "node 11 waits for an answer from 12, which is not going to send one");
|
CHECK(is_empty(tell(11, "0 GOT !")) && is_empty(tell(11, "12 AWAIT .")) && ended == 0 && pool[1].n.mem[AWAIT_FROM] == 12, "node 11 waits for an answer from 12, which is not going to send one");
|
||||||
CHECK(is_empty(tell(10, ": F 49 0 DO S\" HIT\" 11 SEND LOOP ; F")) && ended == 1 && ended_from == 10, "node 10 sends it 49 messages meanwhile");
|
CHECK(is_empty(tell(10, ": F 49 0 DO S\" HIT\" 11 SEND LOOP ; F")) && ended == 1 && ended_from == 10, "node 10 sends it 49 messages meanwhile");
|
||||||
CHECK(pool[1].n.mem[MQ_COUNT] > 300 && pool[1].n.mem[MQ_COUNT] <= MQ_CELLS - 36, "node 11 has kept what it has room for, and left the room that is kept for refusals: %ld cells", (long)pool[1].n.mem[MQ_COUNT]);
|
CHECK(pool[1].n.mem[MQ_COUNT] > 300 && pool[1].n.mem[MQ_COUNT] <= MQ_CELLS - 36, "node 11 has kept what it has room for, and left the room that is kept for refusals: %ld cells", (long)pool[1].n.mem[MQ_COUNT]);
|
||||||
CHECK(pool[1].n.mem[OWED_COUNT] == 8, "it owes a refusal for each of the rest, as many as it can owe: %ld", (long)pool[1].n.mem[OWED_COUNT]);
|
CHECK(pool[1].n.mem[OWED_COUNT] == 0 && pool[0].n.mem[REFUSED] >= refused + 8, "it owed a refusal for each of the rest, as many as it could owe at once, and paid them while it waited: node 10 was told of %ld",
|
||||||
CHECK(pool[0].n.mem[REFUSED] == refused, "and has sent none yet: it is still waiting");
|
(long)(pool[0].n.mem[REFUSED] - refused));
|
||||||
|
|
||||||
/* one from its centre ends the wait */
|
/* one from its centre ends the wait */
|
||||||
CHECK(is_empty(tell(10, "12 NO-ROUTE")), "node 10 forgets the way to 12");
|
CHECK(is_empty(tell(10, "12 NO-ROUTE")), "node 10 forgets the way to 12");
|
||||||
@@ -320,9 +320,10 @@ int main(void)
|
|||||||
got = strtol(tell(11, "GOT @ ."), NULL, 10);
|
got = strtol(tell(11, "GOT @ ."), NULL, 10);
|
||||||
not_taken = 49 - got;
|
not_taken = 49 - got;
|
||||||
CHECK(got >= 30 && not_taken > 8, "it then did every message it had kept: %ld of the 49", (long)got);
|
CHECK(got >= 30 && not_taken > 8, "it then did every message it had kept: %ld of the 49", (long)got);
|
||||||
CHECK(pool[0].n.mem[REFUSED] == refused + 8, "and sent the eight refusals it owed: node 10 was told of %ld", (long)(pool[0].n.mem[REFUSED] - refused));
|
told_of = pool[0].n.mem[REFUSED] - refused;
|
||||||
CHECK(pool[1].n.mem[LOST] == lost + not_taken + (not_taken - 8), "it counted each message it did not take, and each refusal it could not owe: %ld for %ld not taken",
|
CHECK(told_of >= 8 && told_of <= not_taken, "node 10 was told of %ld of them", (long)told_of);
|
||||||
(long)(pool[1].n.mem[LOST] - lost), (long)not_taken);
|
CHECK(pool[1].n.mem[LOST] == lost + not_taken + (not_taken - told_of), "it counted each message it did not take, and each refusal it could not owe: %ld for %ld not taken, %ld told",
|
||||||
|
(long)(pool[1].n.mem[LOST] - lost), (long)not_taken, (long)told_of);
|
||||||
/* the way to 12 is forgotten: what is sent there now is refused */
|
/* the way to 12 is forgotten: what is sent there now is refused */
|
||||||
refused = pool[1].n.mem[REFUSED];
|
refused = pool[1].n.mem[REFUSED];
|
||||||
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(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");
|
||||||
@@ -330,11 +331,11 @@ int main(void)
|
|||||||
}
|
}
|
||||||
/* a line from the console breaks a wait (MESH.md 7b.6) */
|
/* 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(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");
|
CHECK(strcmp(tell(11, "20 22 + ."), "42 ") == 0 && ended == 1 && pool[0].n.mem[AWAIT_FROM] == 12, "text from the console for node 11 comes to node 10 on its way: node 10 passes it on, and goes on waiting: \"%s\"", printed);
|
||||||
{
|
{
|
||||||
const char *r = tell(10, "65 EMIT");
|
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, "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, "A") != NULL && ended == 2, "the line is then done as usual: \"%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(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(strcmp(tell(10, "1 2 + ."), "3 ") == 0 && waiting(hera) && waiting(mid) && waiting(far), "and all three go on as before");
|
||||||
|
|||||||
+30
-22
@@ -276,6 +276,8 @@ static const char *tell(v4_cell node, const char *text)
|
|||||||
if (steps >= step_limit) { stuck(); return "(still running)"; }
|
if (steps >= step_limit) { stuck(); return "(still running)"; }
|
||||||
return printed;
|
return printed;
|
||||||
}
|
}
|
||||||
|
/* The same, answering with what has been printed: for when a node is spinning and the fabric never rests. */
|
||||||
|
static const char *said(v4_cell node, const char *text) { (void)tell(node, text); return printed; }
|
||||||
static int told(v4_cell node, const char *text) { return tell(node, text)[0] == 0 && ended == 1 && ended_how == V4_TEXT_COMPLETED; }
|
static int told(v4_cell node, const char *text) { return tell(node, text)[0] == 0 && ended == 1 && ended_how == V4_TEXT_COMPLETED; }
|
||||||
|
|
||||||
/* POST's host, for the node numbered 10 (v4/include/v4/post.h): a line is
|
/* POST's host, for the node numbered 10 (v4/include/v4/post.h): a line is
|
||||||
@@ -525,30 +527,34 @@ int main(void)
|
|||||||
CHECK(strstr(tell(10, ": H14 S\" 1\" 14 SEND ; H14"), "Argument out of range") != NULL, "Hera herself has no way to it");
|
CHECK(strstr(tell(10, ": H14 S\" 1\" 14 SEND ; H14"), "Argument out of range") != NULL, "Hera herself has no way to it");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* a node waits on one that is stuck and is not its neighbour; Hera kills the stuck one */
|
/* two nodes each send a third text and are then stuck: the third has an answer for each that it
|
||||||
|
* cannot give, and goes on doing what others send it (MESH.md 7c). While a node spins the fabric
|
||||||
|
* never rests: said() answers with what was printed. */
|
||||||
step_limit = 3000000ul;
|
step_limit = 3000000ul;
|
||||||
CHECK(strcmp(tell(13, ": SPIN BEGIN 0 UNTIL ; SPIN"), "(still running)") == 0, "node 13 is stuck in a loop");
|
CHECK(strcmp(tell(12, ": S11 S\" 1 DROP\" 11 SEND BEGIN 0 UNTIL ; S11"), "(still running)") == 0 &&
|
||||||
CHECK(strcmp(tell(12, ": W13 13 AWAIT . ; W13"), "(still running)") == 0 && node_numbered(12)->mem[AWAIT_FROM] == 13 && waiting(12),
|
strcmp(tell(13, ": S11 S\" 1 DROP\" 11 SEND BEGIN 0 UNTIL ; S11"), "(still running)") == 0, "nodes 12 and 13, either side of node 11, each send it text and are then stuck in a loop");
|
||||||
"node 12, which is not wired to it, waits for its answer");
|
CHECK(node_numbered(11)->mem[MQ_COUNT] == 18 && v4_node_in_wait(node_numbered(11)), "node 11 keeps its answer to each, offered, and sleeps: %ld cells", (long)node_numbered(11)->mem[MQ_COUNT]);
|
||||||
/* while it is stuck it holds up no one (MESH.md 7c): text for it waits with Hera, who goes on */
|
CHECK(strcmp(said(11, "11 100 * ."), "1100 ") == 0 && ended == 1 && node_numbered(11)->mem[MQ_COUNT] == 18, "text typed for node 11 is done all the same: \"%s\"", printed);
|
||||||
(void)tell(13, "1 DROP");
|
|
||||||
CHECK(ended == 0 && v4_node_in_wait(node_numbered(10)) && !node_numbered(10)->asking && node_numbered(10)->mem[MQ_COUNT] > 0,
|
/* a node waits on one that is stuck; text for the stuck one waits with Hera, who goes on; Hera kills it */
|
||||||
"text typed for the stuck node waits with Hera, offered: she sleeps in the wait, not blocked writing");
|
CHECK(said(11, ": W13 13 AWAIT . ; W13")[0] == 0 && ended == 0 && node_numbered(11)->mem[AWAIT_FROM] == 13 && waiting(11), "node 11 waits for node 13's answer");
|
||||||
(void)tell(11, "11 100 * .");
|
{
|
||||||
CHECK(strcmp(printed, "1100 ") == 0 && ended == 1, "text typed for another node is passed on and done meanwhile: \"%s\"", printed);
|
v4_cell kept = node_numbered(10)->mem[MQ_COUNT];
|
||||||
(void)tell(10, "1 2 + .");
|
(void)tell(13, "1 DROP");
|
||||||
CHECK(strcmp(printed, "3 ") == 0 && ended == 1, "and so is text for Hera herself: \"%s\"", printed);
|
CHECK(ended == 0 && v4_node_in_wait(node_numbered(10)) && !node_numbered(10)->asking && node_numbered(10)->mem[MQ_COUNT] > kept,
|
||||||
(void)tell(10, "13 KILL");
|
"text typed for the stuck node waits with Hera, offered: she sleeps in the wait, not blocked writing");
|
||||||
CHECK(node_numbered(10)->mem[MQ_COUNT] == 0, "killing it, she lets go what she kept for it");
|
}
|
||||||
CHECK(node_numbered(13) == NULL && strstr(printed, "Node gone") != NULL && node_numbered(12)->mem[AWAIT_FROM] == 0 && waiting(12),
|
CHECK(strcmp(said(10, "1 2 + ."), "3 ") == 0 && ended == 1, "and text for Hera herself is done meanwhile: \"%s\"", printed);
|
||||||
"Hera kills node 13: node 12's wait ends, with an error that says the node is gone: \"%s\"", printed);
|
(void)tell(10, "13 KILL 65 EMIT");
|
||||||
step_limit = 30000000ul;
|
CHECK(node_numbered(13) == NULL && strstr(printed, "Node gone") != NULL && node_numbered(11)->mem[AWAIT_FROM] == 0,
|
||||||
CHECK(strcmp(tell(12, "12 100 * ."), "1200 ") == 0 && strcmp(tell(11, "11 100 * ."), "1100 ") == 0, "and the others go on");
|
"Hera kills node 13: node 11's wait ends, with an error that says the node is gone: \"%s\"", printed);
|
||||||
|
CHECK(strstr(printed, "A") != NULL && ended == 2 && node_numbered(10)->mem[MQ_COUNT] == 9 && v4_node_in_wait(node_numbered(10)),
|
||||||
|
"her line is not held by node 12, which is stuck and has to be told too: that message waits with her, offered, and what she kept for node 13 is let go: %ld cells",
|
||||||
|
(long)node_numbered(10)->mem[MQ_COUNT]);
|
||||||
|
CHECK(strcmp(said(11, "11 100 * ."), "1100 ") == 0 && strcmp(said(10, "1 2 + ."), "3 ") == 0, "and the others go on");
|
||||||
|
|
||||||
/* Hera's own wait, on a node that is stuck: a line from the console ends it */
|
/* Hera's own wait, on a node that is stuck: a line from the console ends it */
|
||||||
step_limit = 3000000ul;
|
CHECK(said(10, "12 AWAIT .")[0] == 0 && ended == 0 && node_numbered(10)->mem[AWAIT_FROM] == 12 && waiting(10), "Hera waits for node 12's answer, which will not come");
|
||||||
CHECK(strcmp(tell(12, ": SPIN BEGIN 0 UNTIL ; SPIN"), "(still running)") == 0, "node 12 is stuck in a loop");
|
|
||||||
CHECK(strcmp(tell(10, "12 AWAIT ."), "(still running)") == 0 && node_numbered(10)->mem[AWAIT_FROM] == 12 && waiting(10), "Hera waits for its answer, which will not come");
|
|
||||||
{
|
{
|
||||||
const char *r = tell(10, "12 KILL 66 EMIT");
|
const char *r = tell(10, "12 KILL 66 EMIT");
|
||||||
CHECK(strstr(r, "Interrupted\n") != NULL && strstr(r, "B") != NULL && strstr(r, "Interrupted\n") < strstr(r, "B") && ended == 2,
|
CHECK(strstr(r, "Interrupted\n") != NULL && strstr(r, "B") != NULL && strstr(r, "Interrupted\n") < strstr(r, "B") && ended == 2,
|
||||||
@@ -556,8 +562,10 @@ int main(void)
|
|||||||
CHECK(node_numbered(12) == NULL && node_numbered(10)->mem[AWAIT_FROM] == 0, "the line killed the stuck node");
|
CHECK(node_numbered(12) == NULL && node_numbered(10)->mem[AWAIT_FROM] == 0, "the line killed the stuck node");
|
||||||
}
|
}
|
||||||
step_limit = 30000000ul;
|
step_limit = 30000000ul;
|
||||||
|
CHECK(strcmp(tell(11, "11 100 * ."), "1100 ") == 0 && strcmp(tell(10, "1 2 + ."), "3 ") == 0 && node_numbered(11)->mem[MQ_COUNT] == 0 && node_numbered(10)->mem[MQ_COUNT] == 0 && waiting(10) && waiting(11),
|
||||||
|
"the two that are left go on, and keep nothing for the nodes that are gone");
|
||||||
|
|
||||||
/* Hera blocked writing to a node that is stuck: a line from the console lets her go (MESH.md 7b.7) */
|
/* Hera's line must begin a message to a node that is stuck: it waits, and a line from the console ends it (MESH.md 7c.3) */
|
||||||
step_limit = 3000000ul;
|
step_limit = 3000000ul;
|
||||||
CHECK(strcmp(tell(11, ": SPIN BEGIN 0 UNTIL ; SPIN"), "(still running)") == 0, "node 11 is stuck in a loop");
|
CHECK(strcmp(tell(11, ": SPIN BEGIN 0 UNTIL ; SPIN"), "(still running)") == 0, "node 11 is stuck in a loop");
|
||||||
CHECK(strcmp(tell(10, ": T11 S\" 1 DROP\" 11 SEND 67 EMIT ; T11"), "(still running)") == 0 && v4_node_in_wait(node_numbered(10)) && !node_numbered(10)->asking,
|
CHECK(strcmp(tell(10, ": T11 S\" 1 DROP\" 11 SEND 67 EMIT ; T11"), "(still running)") == 0 && v4_node_in_wait(node_numbered(10)) && !node_numbered(10)->asking,
|
||||||
|
|||||||
Reference in New Issue
Block a user