feat(v4.0.0): every message begins through the wait -- a stuck node holds up no one
(GATE) offers a message's first word and sleeps until it is taken, taking in whatever is written to the node meanwhile; (PAY) offers every refusal owed at once, one to a port; a node that owes one sleeps in the wait. Writing without looking, looking again and again, and the lower-number rule are gone. Text from the console for a node whose own text waits to begin a message ends that text Interrupted. The lone node's hosts serve an offer as a write. MESH.md 7c; plan step 6d task 2. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
075385ab63
commit
482cc75faf
Binary file not shown.
+97
-73
@@ -131,25 +131,22 @@ header CMOVE
|
|||||||
NONE: drop drop (ROUTE-DEFAULT) a! @ ;
|
NONE: drop drop (ROUTE-DEFAULT) a! @ ;
|
||||||
HIT: drop drop @+ pop drop ;
|
HIT: drop drop @+ pop drop ;
|
||||||
|
|
||||||
\ LOOKING BEFORE WRITING (MESH.md section 7a). A write waits until the
|
\ BEGINNING A MESSAGE: THE WAIT (MESH.md section 7c). A write waits until
|
||||||
\ neighbour reads, and a node that is waiting to write reads nothing: two
|
\ the neighbour reads, and a node that is waiting to write reads nothing:
|
||||||
\ neighbours that each began to write to the other would wait for ever. So
|
\ two neighbours that each began to write to the other would wait for ever,
|
||||||
\ a node does not begin a message until it has looked, (GATE):
|
\ and a node that wrote to one that was stuck would be stuck with it. So
|
||||||
\ - while any neighbour is waiting to write to it, it takes that message
|
\ a node does not write the first word of a message. It offers it, (GATE):
|
||||||
\ in and keeps it with the messages waiting, to deal with when it has
|
\ the word is stored at the port's offer address, and the node then reads
|
||||||
\ nothing else to do;
|
\ the wait, where it sleeps until the offer is taken or a word comes for
|
||||||
\ - then, if the neighbour it means to write to is waiting to read, it
|
\ it, whichever is first. If a word comes, that message is taken in and
|
||||||
\ writes;
|
\ kept with the messages waiting, and the node offers again. When the
|
||||||
\ - and if that neighbour is not, it writes all the same when the
|
\ offer is taken the neighbour has the first word and reads to the end:
|
||||||
\ neighbour's number is the higher of the two, and otherwise looks
|
\ the rest is written, each word waiting for it.
|
||||||
\ again. Of two neighbours one only may wait to write to the other, and
|
\ (OFFER) is the offer address of port 0, and (WAIT) the wait. (READERS)
|
||||||
\ it is the lower: so those that wait are waiting on ever higher
|
\ is the node's look at its neighbours, a bit for each port that has one
|
||||||
\ numbers, and the highest of them is not waiting to write; it is
|
\ waiting to read, and above those a bit for each port that has anything
|
||||||
\ looking, and takes in what is being written to it.
|
\ on it at all. (NEAR) is the number of the node on each port, told by
|
||||||
\ (WRITERS) and (READERS) are the node's two looks at its neighbours, a bit
|
\ whoever wires it (NEIGHBOUR, quit.v4): it is how a node knows its centre.
|
||||||
\ for each port. (NEAR) is the number of the node on each port, told by
|
|
||||||
\ whoever wires it (NEIGHBOUR, quit.v4); a port not told of -- a device's
|
|
||||||
\ -- is written to only when what is there is waiting to read.
|
|
||||||
\
|
\
|
||||||
\ THE MESSAGES WAITING are kept in (MQ) .. (MQ-END), one after another,
|
\ THE MESSAGES WAITING are kept in (MQ) .. (MQ-END), one after another,
|
||||||
\ going round: for each its seven words, the port it came on, and its text.
|
\ going round: for each its seven words, the port it came on, and its text.
|
||||||
@@ -162,12 +159,6 @@ header CMOVE
|
|||||||
if Z -1 + FOR 2/ NEXT 1 and ;
|
if Z -1 + FOR 2/ NEXT 1 and ;
|
||||||
Z: drop 1 and ;
|
Z: drop 1 and ;
|
||||||
|
|
||||||
\ ( mask -- index ) the lowest port in it; the mask is not zero
|
|
||||||
: (LOW)
|
|
||||||
0 push
|
|
||||||
L: dup 1 and if UP drop drop pop ;
|
|
||||||
UP: drop 2/ pop 1 + push jump L
|
|
||||||
|
|
||||||
\ ( w -- ) one more cell of the messages waiting. B is kept.
|
\ ( w -- ) one more cell of the messages waiting. B is kept.
|
||||||
: (MQ!)
|
: (MQ!)
|
||||||
(MQ-TAIL) a! @ a! !+
|
(MQ-TAIL) a! @ a! !+
|
||||||
@@ -260,25 +251,47 @@ 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)
|
||||||
|
|
||||||
\ ( port -- ) wait until a message may be begun on the port, by its
|
\ ( -- flag ) THE WAIT, once. Whatever has been offered stands until
|
||||||
\ address, taking in whatever is being written to this node meanwhile; and
|
\ this returns. Zero: a word came for this node instead; that whole
|
||||||
\ leave B at the port. The port is kept in (GATE-PORT), not on the stack:
|
\ message has been taken in and kept, (TAKE), and its seven words are
|
||||||
\ this is run with the stack as full as EMIT may be.
|
\ still in (MQ-HDR). Not zero: an offer was taken; B is at the port it
|
||||||
: (GATE)
|
\ was taken on, and (GATE-PORT) holds that port's address.
|
||||||
|
: (OFFERS)
|
||||||
|
(WAIT) b! @b push (PORT)+9 b! @b \ ( which ) R: the word
|
||||||
|
dup -PORTS + -if TAKEN
|
||||||
|
drop (PORT) + dup b! pop (TAKE) 0 ; \ a word, from that port: the message's first
|
||||||
|
TAKEN: (PORT) + (GATE-PORT) a! ! drop pop drop
|
||||||
|
(GATE-PORT) a! @ b! -1 ;
|
||||||
|
|
||||||
|
\ ( 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
|
||||||
|
\ node meanwhile is taken in. B is left at the port. The two are kept in
|
||||||
|
\ (GATE-WORD) and (GATE-PORT), not on the stack: (GATE1), which is this
|
||||||
|
\ with the word already in (GATE-WORD), is run with the stack as full as
|
||||||
|
\ EMIT may be.
|
||||||
|
\ TEXT FROM THE CONSOLE for this node, coming while text it is doing waits
|
||||||
|
\ here, ends that text in error 21, Interrupted (MESH.md 7c.3); it is with
|
||||||
|
\ 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)
|
||||||
|
\ is set: what waits then is no text's doing.
|
||||||
|
\ With nothing on the port the fabric raises error 18.
|
||||||
|
: (GATE1) ( port -- )
|
||||||
(GATE-PORT) a! !
|
(GATE-PORT) a! !
|
||||||
L: (WRITERS) b! @b if QUIET
|
L: (GATE-WORD) a! @ (GATE-PORT) a! @ -PORTS - 4 + a! ! \ the offer
|
||||||
(LOW) (PORT) + dup b! @b (TAKE) jump L
|
(OFFERS) if CAME drop ;
|
||||||
QUIET: drop
|
CAME: drop
|
||||||
(GATE-PORT) a! @ (PORT) - (NEAR) + a! @ if ASK
|
(QUIET) a! @ if TEXTS drop jump L
|
||||||
(ME) a! @ - -if GO jump ASK \ its number less this node's
|
TEXTS: drop
|
||||||
GO: drop (GATE-PORT) a! @ b! ; \ the neighbour's is the higher
|
(MQ-HDR) a! @ (ME) a! @ xor if MINE drop jump L
|
||||||
ASK: drop
|
MINE: drop
|
||||||
(READERS) b! @b (GATE-PORT) a! @ (PORT) - (BIT) if NOTYET
|
(MQ-HDR)+2 a! @ -1 + if TEXT drop jump L
|
||||||
drop (GATE-PORT) a! @ b! ; \ it is waiting to read
|
TEXT: drop
|
||||||
NOTYET: drop
|
(CONSOLE) a! @ if C0 jump C1
|
||||||
(READERS) b! @b (GATE-PORT) a! @ (PORT) - -PORTS - (BIT) if NOONE \ is there anything on the port at all?
|
C0: drop (MSG)+1 a! @ \ no console told of: whoever sent the text being done
|
||||||
drop jump L
|
C1: (MQ-HDR)+1 a! @ xor if BREAK drop jump L
|
||||||
NOONE: drop NODE-ERROR b! 18 !b ; \ no: what waits here would wait for ever
|
BREAK: drop NODE-ERROR b! 21 !b ;
|
||||||
|
|
||||||
|
: (GATE) ( w port -- ) push (GATE-WORD) a! ! pop jump (GATE1)
|
||||||
|
|
||||||
\ ( type -- ) words 1 to 5 of a message this node sends: from, type, and
|
\ ( type -- ) words 1 to 5 of a message this node sends: from, type, and
|
||||||
\ the three that are carried and not used yet. Its caller has put B at the
|
\ the three that are carried and not used yet. Its caller has put B at the
|
||||||
@@ -289,14 +302,14 @@ header CMOVE
|
|||||||
!b \ type
|
!b \ type
|
||||||
16 !b 0 !b 0 !b ; \ how many nodes may pass it on (the TTL: MESH.md 7b); ACL tag; sequence
|
16 !b 0 !b 0 !b ; \ how many nodes may pass it on (the TTL: MESH.md 7b); ACL tag; sequence
|
||||||
|
|
||||||
\ PAYING WHAT IS OWED does not hold this node up. A refusal is sent if the
|
\ PAYING WHAT IS OWED (MESH.md 7c.3). Every refusal owed is offered at
|
||||||
\ neighbour it goes by is reading at this moment. If it is not, and its
|
\ once, each on the port it goes by, one to a port, and the node sleeps in
|
||||||
\ number is the higher, the refusal is written all the same, as any message
|
\ the wait: so one owed to a node that is stuck does not keep back one
|
||||||
\ would be (MESH.md 7a): that neighbour takes it in when it next looks. If
|
\ owed to a node that waits for it, and the node takes in whatever is
|
||||||
\ its number is the lower, or it has not been told of, the refusal stays
|
\ written to it meanwhile. One there is no way for, or whose way is a
|
||||||
\ owed, and this node goes on with what it has to do and tries again; it
|
\ port with nothing on it, is let go and counted first. (PAY-K) has a
|
||||||
\ does not sleep while it owes one (quit.v4, (IDLE)). One there is no way
|
\ cell for each port: which refusal is offered there, counted from 1; 0,
|
||||||
\ for, or whose way is a port with nothing on it, is let go and counted.
|
\ none.
|
||||||
|
|
||||||
\ ( k -- ) the k-th refusal owed is done with: the last takes its place
|
\ ( k -- ) the k-th refusal owed is done with: the last takes its place
|
||||||
: (PAID)
|
: (PAID)
|
||||||
@@ -304,36 +317,47 @@ header CMOVE
|
|||||||
2* (OWED) + a! @+ @
|
2* (OWED) + a! @+ @
|
||||||
push push 2* (OWED) + a! pop !+ pop ! ;
|
push push 2* (OWED) + a! pop !+ pop ! ;
|
||||||
|
|
||||||
\ ( k -- ) send the k-th refusal owed, if it can go now
|
\ ( k -- ) the k-th refusal owed is let go if it cannot be sent at all
|
||||||
: (PAY1)
|
: (PAY-DROP)
|
||||||
dup 2* (OWED) + a! @+ @ \ k to about
|
dup 2* (OWED) + a! @ \ k to
|
||||||
over (PORT-FOR) if NOWAY \ k to about port
|
(PORT-FOR) if NOWAY \ k port
|
||||||
dup (THERE) if GONE
|
(THERE) if GONE drop drop ;
|
||||||
drop (READERS) b! @b over (PORT) - (BIT) if BUSY
|
GONE:
|
||||||
drop
|
NOWAY: drop (LOST) a! @ 1 + ! jump (PAID)
|
||||||
WRITE: b! \ k to about B: the port
|
|
||||||
SWAP !b 4 (HDR) 4 !b !b \ k to; from, type 4; four characters; about
|
|
||||||
jump (PAID)
|
|
||||||
BUSY: drop \ it is not reading:
|
|
||||||
dup (PORT) - (NEAR) + a! @ if LEAVE \ k to about port near
|
|
||||||
(ME) a! @ - -if BLIND jump LEAVE \ its number less this node's
|
|
||||||
BLIND: drop jump WRITE \ the higher: it will take it in
|
|
||||||
LEAVE: drop drop drop drop drop ;
|
|
||||||
GONE: drop
|
|
||||||
NOWAY: drop drop drop (LOST) a! @ 1 + ! jump (PAID)
|
|
||||||
|
|
||||||
\ ( -- ) send every refusal this node owes that can go now
|
\ ( k -- ) offer the k-th refusal owed on its port, if nothing is there yet
|
||||||
|
: (PAY-OFFER)
|
||||||
|
dup 2* (OWED) + a! @ \ k to
|
||||||
|
dup (PORT-FOR) (PORT) - \ k to n the port, by its number
|
||||||
|
dup (PAY-K) + a! @ if FREE
|
||||||
|
drop drop drop drop ;
|
||||||
|
FREE: drop
|
||||||
|
dup push (OFFER) + a! ! \ k the message's first word, whom it is to
|
||||||
|
1 + pop (PAY-K) + a! ! ;
|
||||||
|
|
||||||
|
\ ( -- flag ) offer every refusal this node owes and wait once: if one
|
||||||
|
\ is taken its other words are written and it is done with. Zero if
|
||||||
|
\ nothing is owed, and then nothing was waited for.
|
||||||
: (PAY)
|
: (PAY)
|
||||||
(OWED#) a! @ if NONE -1 +
|
(OWED#) a! @ if NONE
|
||||||
FOR pop dup push (PAY1) NEXT ;
|
-1 + FOR pop dup push (PAY-DROP) NEXT
|
||||||
NONE: drop ;
|
(OWED#) a! @ if NONE
|
||||||
|
(PAY-K) a! PORTS-1 FOR 0 !+ NEXT
|
||||||
|
-1 + FOR pop dup push (PAY-OFFER) NEXT
|
||||||
|
(OFFERS) if CAME
|
||||||
|
drop (GATE-PORT) a! @ (PORT) - (PAY-K) + a! @ -1 + \ k: the one that was taken
|
||||||
|
dup 2* (OWED) + 1 + a! @ \ k about
|
||||||
|
4 (HDR) 4 !b !b \ from, type 4; four characters; about
|
||||||
|
(PAID) -1 ;
|
||||||
|
CAME: drop -1 ;
|
||||||
|
NONE: drop 0 ;
|
||||||
|
|
||||||
\ ( -- ) send what has been printed, to where (PRINT-TO) says, by the port
|
\ ( -- ) send what has been printed, to where (PRINT-TO) says, by the port
|
||||||
\ (PRINT-PORT): its length, then its characters, four to a word, the first
|
\ (PRINT-PORT): its length, then its characters, four to a word, the first
|
||||||
\ lowest. quit.v4 sets those two when text for this node arrives.
|
\ lowest. quit.v4 sets those two when text for this node arrives.
|
||||||
: (FLUSH-OUT)
|
: (FLUSH-OUT)
|
||||||
(OUT^) a! @ (OUT) xor if NONE drop
|
(OUT^) a! @ (OUT) xor if NONE drop
|
||||||
(PRINT-PORT) a! @ (GATE) (PRINT-TO) a! @ !b
|
(PRINT-TO) a! @ (GATE-WORD) a! ! (PRINT-PORT) a! @ (GATE1)
|
||||||
2 (HDR)
|
2 (HDR)
|
||||||
(OUT^) a! @ (OUT) - dup !b
|
(OUT^) a! @ (OUT) - dup !b
|
||||||
3 + 2/ 2/ -1 + (OUT) a!
|
3 + 2/ 2/ -1 + (OUT) a!
|
||||||
|
|||||||
+9
-13
@@ -58,7 +58,7 @@ macro R-CLEAR RSTACK-DEPTH b! a !b endmacro
|
|||||||
1 (QUIET) a! ! \ from here an error is not this text's: see (RAISED)
|
1 (QUIET) a! ! \ from here an error is not this text's: see (RAISED)
|
||||||
(LINE-STATUS) b! dup !b
|
(LINE-STATUS) b! dup !b
|
||||||
(FLUSH-OUT)
|
(FLUSH-OUT)
|
||||||
(DONE-PORT) a! @ (GATE) (MSG)+1 a! @ !b \ to whoever sent the text
|
(MSG)+1 a! @ (DONE-PORT) a! @ (GATE) \ to whoever sent the text
|
||||||
3 (HDR) 4 !b !b \ a message of type 3, four characters long: how it ended
|
3 (HDR) 4 !b !b \ a message of type 3, four characters long: how it ended
|
||||||
jump (IDLE)
|
jump (IDLE)
|
||||||
|
|
||||||
@@ -80,12 +80,8 @@ macro R-CLEAR RSTACK-DEPTH b! a !b endmacro
|
|||||||
\ is passed on, (PASS-ON).
|
\ is passed on, (PASS-ON).
|
||||||
: (IDLE)
|
: (IDLE)
|
||||||
L: 1 (QUIET) a! ! \ nothing here is any text's doing: see (RAISED)
|
L: 1 (QUIET) a! ! \ nothing here is any text's doing: see (RAISED)
|
||||||
(PAY) \ what refusals can be sent now, are (core.v4)
|
|
||||||
(MQ#) a! @ if WAIT drop jump HAVE
|
(MQ#) a! @ if WAIT drop jump HAVE
|
||||||
WAIT: drop (OWED#) a! @ if BLOCK \ with one still owed it does not sleep:
|
WAIT: drop (PAY) if BLOCK drop jump L \ what it owes is offered, and it sleeps (core.v4)
|
||||||
drop (WRITERS) b! @b if L0 \ it takes in whatever is being written to it,
|
|
||||||
(LOW) (PORT) + dup b! @b (TAKE) jump L
|
|
||||||
L0: drop jump L \ and tries again
|
|
||||||
BLOCK: drop (PORT)+8 b! @b (PORT)+9 b! @b (PORT) + dup b! \ ( to port ) B: the port it came on
|
BLOCK: drop (PORT)+8 b! @b (PORT)+9 b! @b (PORT) + dup b! \ ( to port ) B: the port it came on
|
||||||
SWAP (TAKE) jump L
|
SWAP (TAKE) jump L
|
||||||
HAVE:
|
HAVE:
|
||||||
@@ -131,7 +127,7 @@ macro R-CLEAR RSTACK-DEPTH b! a !b endmacro
|
|||||||
COUNT: -1 + if SPENT (MSG)+3 a! !
|
COUNT: -1 + if SPENT (MSG)+3 a! !
|
||||||
(MSG) a! @ (PORT-FOR) if NOWAY
|
(MSG) a! @ (PORT-FOR) if NOWAY
|
||||||
dup (THERE) if NOONE drop
|
dup (THERE) if NOONE drop
|
||||||
(GATE) (MSG) a! 6 FOR @+ !b UNEXT \ its seven words
|
(MSG) a! @ SWAP (GATE) (MSG)+1 a! 5 FOR @+ !b UNEXT \ its seven words
|
||||||
(MSG)+6 a! @ 3 + 2/ 2/ if SENT
|
(MSG)+6 a! @ 3 + 2/ 2/ if SENT
|
||||||
-1 + TIB 2/ 2/ a! FOR @+ !b UNEXT \ and its text
|
-1 + TIB 2/ 2/ a! FOR @+ !b UNEXT \ and its text
|
||||||
jump (IDLE)
|
jump (IDLE)
|
||||||
@@ -169,8 +165,8 @@ header ROUTE
|
|||||||
|
|
||||||
\ ( node port -- ) the node on the other end of the port, by its number;
|
\ ( node port -- ) the node on the other end of the port, by its number;
|
||||||
\ 0: none, or a device. Whoever wires the node tells it, and tells it again
|
\ 0: none, or a device. Whoever wires the node tells it, and tells it again
|
||||||
\ when the wiring changes. It is what lets one of two neighbours, and one
|
\ when the wiring changes. It is how a node knows who its centre is
|
||||||
\ only, wait to write to the other (core.v4, (GATE)).
|
\ ((CENTRE)), from whom alone it believes a GONE.
|
||||||
header NEIGHBOUR
|
header NEIGHBOUR
|
||||||
: NEIGHBOUR
|
: NEIGHBOUR
|
||||||
dup -if POS jump BAD
|
dup -if POS jump BAD
|
||||||
@@ -201,7 +197,7 @@ header NO-ROUTE
|
|||||||
\ it is error 12.
|
\ it is error 12.
|
||||||
: (SEND1)
|
: (SEND1)
|
||||||
push dup (PORT-FOR) if NOWAY
|
push dup (PORT-FOR) if NOWAY
|
||||||
(GATE) !b pop (HDR) 4 !b !b ;
|
(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
|
||||||
@@ -216,10 +212,10 @@ header NO-ROUTES
|
|||||||
|
|
||||||
\ ( baddr u node port -- ) send the node u characters of text to
|
\ ( baddr u node port -- ) send the node u characters of text to
|
||||||
\ interpret, by the port given, by its address. It is on its way when this
|
\ interpret, by the port given, by its address. It is on its way when this
|
||||||
\ returns. The write waits for the neighbour to read, and is not begun
|
\ returns. Its first word is offered until the neighbour takes it
|
||||||
\ until it may be (core.v4, (GATE)).
|
\ (core.v4, (GATE)); the rest is written, each word waiting for it.
|
||||||
: (SEND-ON)
|
: (SEND-ON)
|
||||||
(GATE) !b \ to
|
(GATE) \ to
|
||||||
1 (HDR)
|
1 (HDR)
|
||||||
dup !b \ length
|
dup !b \ length
|
||||||
3 + 2/ 2/ if NONE -1 +
|
3 + 2/ 2/ if NONE -1 +
|
||||||
|
|||||||
@@ -107,6 +107,7 @@ static int run_line(const v4_boot *b, const char *text, unsigned len, char *keep
|
|||||||
for (;;) {
|
for (;;) {
|
||||||
(void)v4_exec_step_word(n, b->es, b->h);
|
(void)v4_exec_step_word(n, b->es, b->h);
|
||||||
if (n->stopped) return V4_BOOT_LINE_STOPPED;
|
if (n->stopped) return V4_BOOT_LINE_STOPPED;
|
||||||
|
(void)v4_node_offer_as_write(n); /* in the wait (MESH.md 7c): what it offers is a write, here where whatever is on a port takes at once */
|
||||||
|
|
||||||
if (n->asking && n->ask_port == KERNEL_PORT) { /* a request: the kernel's turn */
|
if (n->asking && n->ask_port == KERNEL_PORT) { /* a request: the kernel's turn */
|
||||||
if (v4_blocks_serve(&boot_blocks, n, n->request)) { /* a block, as for any node */
|
if (v4_blocks_serve(&boot_blocks, n, n->request)) { /* a block, as for any node */
|
||||||
|
|||||||
+8
-1
@@ -87,6 +87,8 @@
|
|||||||
#define MQ_TAIL (BUF0_W - 36) /* where the next will go, */
|
#define MQ_TAIL (BUF0_W - 36) /* where the next will go, */
|
||||||
#define MQ_COUNT (BUF0_W - 37) /* and how many cells they take */
|
#define MQ_COUNT (BUF0_W - 37) /* and how many cells they take */
|
||||||
#define AWAIT_FROM (BUF0_W - 39) /* the node whose word of how text ended is waited for */
|
#define AWAIT_FROM (BUF0_W - 39) /* the node whose word of how text ended is waited for */
|
||||||
|
#define GATE_WORD (BUF0_W - 32) /* the first word of a message that is being offered */
|
||||||
|
#define PAY_K (BUF0_W - 123) /* for each port, which refusal owed is offered there, counted from 1; 0: none. V4_PORTS cells */
|
||||||
#define GATE_PORT (BUF0_W - 38) /* the port address a message is about to be begun on */
|
#define GATE_PORT (BUF0_W - 38) /* the port address a message is about to be begun on */
|
||||||
#define NEAR (BUF0_W - 64) /* for each port, the number of the node on the other end; 0: not told */
|
#define NEAR (BUF0_W - 64) /* for each port, the number of the node on the other end; 0: not told */
|
||||||
#define MQ_HDR (BUF0_W - 56) /* a message being taken in: its seven words and the port it came on */
|
#define MQ_HDR (BUF0_W - 56) /* a message being taken in: its seven words and the port it came on */
|
||||||
@@ -125,7 +127,7 @@ typedef char host_map_ports_fit[(V4_PORTS <= 8u) ? 1 : -1];
|
|||||||
/* the messages waiting are above the dictionary */
|
/* the messages waiting are above the dictionary */
|
||||||
typedef char host_map_queue_fits[(MQ_W >= DICT_END_W) ? 1 : -1];
|
typedef char host_map_queue_fits[(MQ_W >= DICT_END_W) ? 1 : -1];
|
||||||
/* the ports, with the offers and the wait after them, end below the cells that follow */
|
/* the ports, with the offers and the wait after them, end below the cells that follow */
|
||||||
typedef char host_map_port_block_fits[(PORT + 2 * (v4_cell)V4_PORTS + 5 <= BUF0_W - 115) ? 1 : -1];
|
typedef char host_map_port_block_fits[(PORT + 2 * (v4_cell)V4_PORTS + 5 <= PAY_K && PAY_K + (v4_cell)V4_PORTS <= BUF0_W - 115) ? 1 : -1];
|
||||||
|
|
||||||
/* How many slots, from slot 0, a branch may sit in on a node this size: those
|
/* How many slots, from slot 0, a branch may sit in on a node this size: those
|
||||||
* whose address field reaches every word of it. */
|
* whose address field reaches every word of it. */
|
||||||
@@ -198,6 +200,11 @@ static int host_load(v4_text *tx, v4_node *n, const char *const *files, unsigned
|
|||||||
v4_text_constant(tx, "-PORTS", -(v4_cell)V4_PORTS);
|
v4_text_constant(tx, "-PORTS", -(v4_cell)V4_PORTS);
|
||||||
v4_text_constant(tx, "(NEAR)", NEAR);
|
v4_text_constant(tx, "(NEAR)", NEAR);
|
||||||
v4_text_constant(tx, "(GATE-PORT)", GATE_PORT);
|
v4_text_constant(tx, "(GATE-PORT)", GATE_PORT);
|
||||||
|
v4_text_constant(tx, "(GATE-WORD)", GATE_WORD);
|
||||||
|
v4_text_constant(tx, "(PAY-K)", PAY_K);
|
||||||
|
v4_text_constant(tx, "PORTS-1", (v4_cell)V4_PORTS - 1);
|
||||||
|
v4_text_constant(tx, "(OFFER)", PORT + (v4_cell)V4_PORT_OFFER);
|
||||||
|
v4_text_constant(tx, "(WAIT)", PORT + (v4_cell)V4_PORT_WAIT);
|
||||||
v4_text_constant(tx, "(AWAIT-FROM)", AWAIT_FROM);
|
v4_text_constant(tx, "(AWAIT-FROM)", AWAIT_FROM);
|
||||||
v4_text_constant(tx, "(MQ)", MQ_W);
|
v4_text_constant(tx, "(MQ)", MQ_W);
|
||||||
v4_text_constant(tx, "(MQ-END)", MQ_W + MQ_CELLS);
|
v4_text_constant(tx, "(MQ-END)", MQ_W + MQ_CELLS);
|
||||||
|
|||||||
+68
-22
@@ -112,6 +112,10 @@ static unsigned starforth_node(unsigned k)
|
|||||||
return (unsigned)id;
|
return (unsigned)id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* The same, answering with what has been printed: for when a node is
|
||||||
|
* spinning and the fabric never comes to rest. */
|
||||||
|
static const char *said(v4_cell node, const char *text) { (void)tell(node, text); return printed; }
|
||||||
|
|
||||||
static int waiting(unsigned id)
|
static int waiting(unsigned id)
|
||||||
{
|
{
|
||||||
const v4_node *n = &v4_fabric_node_at(&f, id)->n;
|
const v4_node *n = &v4_fabric_node_at(&f, id)->n;
|
||||||
@@ -364,9 +368,10 @@ int main(void)
|
|||||||
{
|
{
|
||||||
CHECK(is_empty(tell(11, "0 GOT !")) && is_empty(tell(11, "12 AWAIT .")) && pool[1].n.mem[AWAIT_FROM] == 12, "node 11 waits on 12 again");
|
CHECK(is_empty(tell(11, "0 GOT !")) && is_empty(tell(11, "12 AWAIT .")) && pool[1].n.mem[AWAIT_FROM] == 12, "node 11 waits on 12 again");
|
||||||
CHECK(is_empty(tell(10, ": F40 40 0 DO S\" HIT\" 11 SEND LOOP ; F40")) && pool[1].n.mem[OWED_COUNT] == 0, "node 10 sends it as many messages as it has room for");
|
CHECK(is_empty(tell(10, ": F40 40 0 DO S\" HIT\" 11 SEND LOOP ; F40")) && pool[1].n.mem[OWED_COUNT] == 0, "node 10 sends it as many messages as it has room for");
|
||||||
(void)tell(10, ": ONE S\" HIT\" 11 SEND 12 NO-ROUTE 12 11 GONE 11 AWAIT . ; ONE");
|
/* node 11 does what it has kept before it pays what it owes (7b.3): forty answers come first */
|
||||||
|
(void)tell(10, ": ONE S\" HIT\" 11 SEND 12 NO-ROUTE 12 11 GONE 41 0 DO 11 AWAIT DROP LOOP ; ONE");
|
||||||
CHECK(strstr(printed, "Message refused") != NULL && pool[0].n.mem[AWAIT_FROM] == 0,
|
CHECK(strstr(printed, "Message refused") != NULL && pool[0].n.mem[AWAIT_FROM] == 0,
|
||||||
"it sends one more, for which there is no room, ends node 11's wait, and waits for node 11's answer: its own wait ends, the message refused: \"%s\"", printed);
|
"it sends one more, for which there is no room, ends node 11's wait, and waits for node 11's answers: after the forty, its wait ends, the message refused: \"%s\"", printed);
|
||||||
CHECK(strtol(tell(11, "GOT @ ."), NULL, 10) == 40 && is_empty(tell(10, "12 3 ROUTE")) && is_empty(tell(11, "12 3 ROUTE")), "node 11 did the forty it had kept; the ways are told again");
|
CHECK(strtol(tell(11, "GOT @ ."), NULL, 10) == 40 && is_empty(tell(10, "12 3 ROUTE")) && is_empty(tell(11, "12 3 ROUTE")), "node 11 did the forty it had kept; the ways are told again");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -393,34 +398,75 @@ int main(void)
|
|||||||
CHECK(is_empty(tell(10, "66 NO-ROUTE")) && waiting(hera) && waiting(mid) && waiting(far), "the way is forgotten, and all three are at rest");
|
CHECK(is_empty(tell(10, "66 NO-ROUTE")) && waiting(hera) && waiting(mid) && waiting(far), "the way is forgotten, and all three are at rest");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* a node that owes a refusal to a node that is stuck is not stuck itself */
|
/* ---- a node that is stuck holds up no one (MESH.md 7c) ------------------- */
|
||||||
CHECK(is_empty(tell(12, "NO-ROUTES 1 3 ROUTE 10 3 ROUTE 11 2 ROUTE")) && is_empty(tell(11, "55 3 ROUTE")), "node 12 has no way for 55; node 11 is told the way to 55 is by node 12");
|
v4_fabric_gone_error(&f, V4_ERROR_NO_ONE);
|
||||||
|
|
||||||
|
/* From here a node is spinning, so the fabric never comes to rest: said()
|
||||||
|
* is tell(), answering with what was printed by the time it gave up. */
|
||||||
|
|
||||||
|
/* a refusal owed to a stuck node whose number is the lower */
|
||||||
|
CHECK(is_empty(said(12, "NO-ROUTES 1 3 ROUTE 10 3 ROUTE 11 2 ROUTE")) && is_empty(said(11, "55 3 ROUTE")), "node 12 has no way for 55; node 11 is told the way to 55 is by node 12");
|
||||||
CHECK(strcmp(tell(11, ": C1 S\" 1 DROP\" 55 SEND BEGIN 0 UNTIL ; C1"), "(still running)") == 0, "node 11 sends 55 a message and is then stuck in a loop");
|
CHECK(strcmp(tell(11, ": C1 S\" 1 DROP\" 55 SEND BEGIN 0 UNTIL ; C1"), "(still running)") == 0, "node 11 sends 55 a message and is then stuck in a loop");
|
||||||
CHECK(pool[2].n.mem[OWED_COUNT] == 1, "node 12 owes node 11 a refusal, and cannot give it: node 11 is not reading");
|
CHECK(pool[2].n.mem[OWED_COUNT] == 1, "node 12 owes node 11 a refusal, and cannot give it: node 11 is not reading");
|
||||||
(void)tell(12, "7 8 * .");
|
CHECK(strcmp(said(12, "7 8 * ."), "56 ") == 0 && printed_from == 12, "node 12 goes on doing what it is sent: \"%s\"", printed);
|
||||||
CHECK(strcmp(printed, "56 ") == 0 && printed_from == 12, "node 12 goes on doing what it is sent: \"%s\"", printed);
|
{
|
||||||
|
v4_uheat_t clock = pool[2].es.anticlock;
|
||||||
|
unsigned k;
|
||||||
|
for (k = 0; k < 1000; k++) (void)v4_fabric_step(&f);
|
||||||
|
CHECK(v4_node_in_wait(&pool[2].n) && pool[2].es.anticlock == clock && pool[2].n.mem[OWED_COUNT] == 1, "between times it sleeps in the wait, still owing, and executes nothing");
|
||||||
|
}
|
||||||
|
CHECK(strcmp(said(10, ": X12 S\" 66 EMIT\" 12 SEND ; X12"), "B") == 0 && printed_from == 12, "another node writes to it and is answered: \"%s\"", printed);
|
||||||
|
|
||||||
/* a node that is waiting to write to a node finds out when that node is removed */
|
/* two refusals owed: one to the stuck node, one to a node that waits for it */
|
||||||
(void)tell(12, ": TO11 S\" 1 DROP\" 11 SEND 65 EMIT ; TO11");
|
CHECK(is_empty(said(10, "77 3 ROUTE")), "node 10 is told the way to 77 is by node 12, which has none");
|
||||||
CHECK(ended == 0, "node 12 sends node 11 a message: node 11 is not reading, and node 12 waits to write to it");
|
CHECK(strstr(said(10, ": W77B S\" 1 DROP\" 77 SEND 77 AWAIT . ; W77B"), "Message refused") != NULL && pool[0].n.mem[AWAIT_FROM] == 0 && pool[2].n.mem[OWED_COUNT] == 1,
|
||||||
|
"node 10 sends 77 a message and waits: node 12, which still owes the stuck node, tells node 10 it was refused: \"%s\"", printed);
|
||||||
|
CHECK(is_empty(said(10, "77 NO-ROUTE")), "the way is forgotten");
|
||||||
|
|
||||||
|
/* a line that must begin a message to the stuck node waits, and the node takes in what comes */
|
||||||
|
CHECK(is_empty(said(10, ": TO11 S\" 1 DROP\" 11 SEND 65 EMIT ; TO11")) && ended == 0 && v4_node_in_wait(&pool[0].n) && !pool[0].n.asking,
|
||||||
|
"node 10 sends the stuck node a message: its line waits in the wait, not blocked writing");
|
||||||
|
{
|
||||||
|
v4_cell kept = pool[0].n.mem[MQ_COUNT];
|
||||||
|
CHECK(is_empty(said(12, "7 8 * .")) && ended == 0 && pool[0].n.mem[MQ_COUNT] > kept && v4_node_in_wait(&pool[0].n),
|
||||||
|
"text from the console for node 12 comes to node 10: it keeps it, and its line goes on waiting");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const char *r = said(10, "66 EMIT");
|
||||||
|
CHECK(strstr(r, "Interrupted\n") != NULL && strstr(r, "A") == NULL && strstr(r, "B") != NULL && strstr(r, "Interrupted\n") < strstr(r, "B"),
|
||||||
|
"text from the console for node 10 itself ends the line that waited, with an error that says so, and is then done: \"%s\"", r);
|
||||||
|
CHECK(strstr(r, "56 ") != NULL && ended == 3, "and what it kept for node 12 is passed on and done: \"%s\", %d ended", r, ended);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* the stuck node is removed while a line waits to begin a message to it */
|
||||||
|
CHECK(is_empty(said(10, "TO11")) && ended == 0 && v4_node_in_wait(&pool[0].n), "node 10's line waits on the stuck node again");
|
||||||
CHECK(v4_fabric_remove(&f, mid) == &pool[1], "node 11 is removed");
|
CHECK(v4_fabric_remove(&f, mid) == &pool[1], "node 11 is removed");
|
||||||
(void)tell(10, "1 DROP");
|
{
|
||||||
CHECK(strstr(printed, "No one on that port") != NULL && strstr(printed, "A") == NULL, "node 12's line ends in an error that says no one is there: \"%s\"", printed);
|
unsigned k;
|
||||||
|
for (k = 0; k < 200000 && ended == 0; k++) (void)v4_fabric_step(&f);
|
||||||
|
CHECK(strstr(printed, "No one on that port") != NULL && strstr(printed, "A") == NULL && ended == 1, "the line ends in an error that says no one is there: \"%s\"", printed);
|
||||||
|
}
|
||||||
step_cap = 20000000;
|
step_cap = 20000000;
|
||||||
CHECK(strcmp(tell(12, "7 8 * ."), "56 ") == 0 && pool[2].n.mem[OWED_COUNT] == 0 && waiting(far) && waiting(hera),
|
CHECK(strcmp(said(12, "7 8 * ."), "56 ") == 0 && pool[2].n.mem[OWED_COUNT] == 0 && waiting(far) && !v4_node_in_wait(&pool[2].n) && waiting(hera),
|
||||||
"it goes on, the refusal it owed node 11 let go: it owes nothing and is at rest");
|
"node 12 goes on, the refusal it owed node 11 let go: it owes nothing and is at rest");
|
||||||
|
|
||||||
/* THE LIMIT (MESH.md 7b.7): a refusal owed to a neighbour whose number is the higher is written
|
/* a refusal owed to a stuck node whose number is the higher */
|
||||||
* without looking, as any message to it is, so a node that is stuck holds up a neighbour that owes
|
|
||||||
* it one -- until it is removed */
|
|
||||||
step_cap = 3000000;
|
step_cap = 3000000;
|
||||||
CHECK(is_empty(tell(12, "55 3 ROUTE")), "node 12 is told the way to 55 is by node 10, which has none");
|
CHECK(is_empty(said(12, "55 3 ROUTE")), "node 12 is told the way to 55 is by node 10, which has none");
|
||||||
CHECK(strcmp(tell(12, ": C2 S\" 1 DROP\" 55 SEND BEGIN 0 UNTIL ; C2"), "(still running)") == 0 && pool[0].n.asking && pool[0].n.mem[OWED_COUNT] == 1,
|
CHECK(strcmp(tell(12, ": C2 S\" 1 DROP\" 55 SEND BEGIN 0 UNTIL ; C2"), "(still running)") == 0 && pool[0].n.mem[OWED_COUNT] == 1 && !pool[0].n.asking,
|
||||||
"node 12 sends 55 a message and is then stuck in a loop: node 10 owes it a refusal, writes it, and is blocked");
|
"node 12 sends 55 a message and is then stuck in a loop: node 10 owes it a refusal, and is not blocked writing it");
|
||||||
v4_fabric_gone_error(&f, V4_ERROR_NO_ONE);
|
|
||||||
CHECK(v4_fabric_remove(&f, far) == &pool[2], "node 12 is removed");
|
|
||||||
step_cap = 20000000;
|
step_cap = 20000000;
|
||||||
CHECK(strcmp(tell(10, "1 2 + ."), "3 ") == 0 && pool[0].n.mem[OWED_COUNT] == 0 && waiting(hera), "node 10 is let go, lets the refusal go, and goes on: \"%s\"", printed);
|
CHECK(strcmp(said(10, "1 2 + ."), "3 ") == 0 && pool[0].n.mem[OWED_COUNT] == 1, "node 10 goes on doing what it is sent, still owing: \"%s\"", printed);
|
||||||
|
{
|
||||||
|
v4_uheat_t clock;
|
||||||
|
unsigned k;
|
||||||
|
for (k = 0; k < 1000; k++) (void)v4_fabric_step(&f);
|
||||||
|
clock = pool[0].es.anticlock;
|
||||||
|
for (k = 0; k < 1000; k++) (void)v4_fabric_step(&f);
|
||||||
|
CHECK(v4_node_in_wait(&pool[0].n) && pool[0].es.anticlock == clock, "and sleeps in the wait between times");
|
||||||
|
}
|
||||||
|
CHECK(v4_fabric_remove(&f, far) == &pool[2], "node 12 is removed");
|
||||||
|
CHECK(strcmp(said(10, "1 2 + ."), "3 ") == 0 && pool[0].n.mem[OWED_COUNT] == 0 && waiting(hera) && !v4_node_in_wait(&pool[0].n), "node 10 lets the refusal go and is at rest: \"%s\"", printed);
|
||||||
|
|
||||||
printf(" %d checks, %d failures\n", checks, failures);
|
printf(" %d checks, %d failures\n", checks, failures);
|
||||||
return failures != 0;
|
return failures != 0;
|
||||||
|
|||||||
@@ -213,6 +213,7 @@ static int run_line(long max)
|
|||||||
while (steps < max && idle < 64) {
|
while (steps < max && idle < 64) {
|
||||||
(void)v4_exec_step_word(&n, &es, &h);
|
(void)v4_exec_step_word(&n, &es, &h);
|
||||||
steps++;
|
steps++;
|
||||||
|
(void)v4_node_offer_as_write(&n); /* in the wait: what it offers is a write, the kernel and the console taking at once */
|
||||||
if (n.asking && n.ask_port == 0) { kernel_serve(); v4_node_port_served(&n); continue; } /* a request */
|
if (n.asking && n.ask_port == 0) { kernel_serve(); v4_node_port_served(&n); continue; } /* a request */
|
||||||
if (n.asking && n.ask_port == CONSOLE_PORT) { /* a word of a message from the node */
|
if (n.asking && n.ask_port == CONSOLE_PORT) { /* a word of a message from the node */
|
||||||
v4_cell word = n.request;
|
v4_cell word = n.request;
|
||||||
|
|||||||
@@ -506,8 +506,8 @@ int main(void)
|
|||||||
/* a node is killed while its neighbour is writing to it */
|
/* a node is killed while its neighbour is writing to it */
|
||||||
step_limit = 3000000ul;
|
step_limit = 3000000ul;
|
||||||
CHECK(strcmp(tell(14, ": SPIN BEGIN 0 UNTIL ; SPIN"), "(still running)") == 0, "node 14 is stuck in a loop that never ends");
|
CHECK(strcmp(tell(14, ": SPIN BEGIN 0 UNTIL ; SPIN"), "(still running)") == 0, "node 14 is stuck in a loop that never ends");
|
||||||
CHECK(strcmp(tell(12, ": TO14 S\" 65 EMIT\" 14 SEND ; TO14"), "(still running)") == 0 && node_numbered(12)->asking,
|
CHECK(strcmp(tell(12, ": TO14 S\" 65 EMIT\" 14 SEND ; TO14"), "(still running)") == 0 && v4_node_in_wait(node_numbered(12)) && !node_numbered(12)->asking,
|
||||||
"node 12, beside it, sends it text: 14 never reads, and 12 is blocked writing to it");
|
"node 12, beside it, sends it text: 14 never reads, and 12 waits to begin the message, in the wait");
|
||||||
{
|
{
|
||||||
unsigned born = born_count;
|
unsigned born = born_count;
|
||||||
const char *r = tell(10, "14 KILL 65 EMIT");
|
const char *r = tell(10, "14 KILL 65 EMIT");
|
||||||
@@ -551,8 +551,8 @@ int main(void)
|
|||||||
/* Hera blocked writing to a node that is stuck: a line from the console lets her go (MESH.md 7b.7) */
|
/* Hera blocked writing to a node that is stuck: a line from the console lets her go (MESH.md 7b.7) */
|
||||||
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 && 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,
|
||||||
"Hera sends it text: its number is the higher, so she writes without looking, and is blocked");
|
"Hera sends it text: it never reads, and she waits to begin the message, in the wait");
|
||||||
{
|
{
|
||||||
const char *r = tell(10, "11 KILL 68 EMIT");
|
const char *r = tell(10, "11 KILL 68 EMIT");
|
||||||
CHECK(strstr(r, "Interrupted\n") != NULL && strstr(r, "D") != NULL && strstr(r, "C") == NULL && strstr(r, "Interrupted\n") < strstr(r, "D"),
|
CHECK(strstr(r, "Interrupted\n") != NULL && strstr(r, "D") != NULL && strstr(r, "C") == NULL && strstr(r, "Interrupted\n") < strstr(r, "D"),
|
||||||
|
|||||||
Reference in New Issue
Block a user