feat(v4.0.0): the wait in the engine -- a node offers a word and sleeps
A node stores a word to offer on any of its ports and fetches the wait: it is blocked until a word comes for it or one of its offers is taken, and the fabric does the handing over in one step. The port block moves in the nucleus's map to make room for the new addresses. Nothing uses it yet. MESH.md 7c; plan step 6d task 1. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
dfabfa46f6
commit
075385ab63
Binary file not shown.
@@ -43,6 +43,8 @@
|
||||
#define V4_PORTS 8u
|
||||
#endif
|
||||
#define V4_PORT_ANY V4_PORTS /* not a port: "whichever port has a neighbour writing" */
|
||||
#define V4_PORT_OFFER (V4_PORTS + 4u) /* not a port: the offer on port 0; the offer on port k is k further (the wait, below) */
|
||||
#define V4_PORT_WAIT (2u * V4_PORTS + 4u) /* not a port: the wait */
|
||||
|
||||
#ifndef V4_NODE_WORDS
|
||||
#define V4_NODE_WORDS 1024u
|
||||
@@ -112,6 +114,10 @@ typedef struct {
|
||||
unsigned writers; /* bit k: the neighbour on port k is blocked writing to this node */
|
||||
unsigned readers; /* bit k: the neighbour on port k is blocked reading from this node */
|
||||
unsigned since_look; /* words taken from this node since it last fetched either of those */
|
||||
unsigned offers; /* bit k: a word is offered on port k (the wait: docs/v4.0.0/MESH.md 7c) */
|
||||
v4_cell offer[V4_PORTS]; /* the words offered */
|
||||
int waiting; /* non-zero: the read it is blocked in is the wait */
|
||||
int offer_asked; /* non-zero: `asking` stands for the offer on `ask_port` (v4_node_offer_as_write) */
|
||||
|
||||
/* DSTACK-DEPTH and RSTACK-DEPTH (D-16). See v4_node_stack_regs_attach. */
|
||||
v4_cell dstack_reg; /* its word address, or -1 */
|
||||
@@ -236,6 +242,34 @@ unsigned v4_node_words_since_look(const v4_node *n);
|
||||
* that is not blocked at a port, or is reading "any port". */
|
||||
int v4_node_port_gone(v4_node *n, v4_cell code);
|
||||
|
||||
/* THE WAIT (docs/v4.0.0/MESH.md 7c). After the addresses above come
|
||||
* base + V4_PORT_OFFER + k the offer on port k: a store here is the word
|
||||
* the node offers to write to that port. It is
|
||||
* remembered and waits for nothing.
|
||||
* base + V4_PORT_WAIT the wait: a fetch here blocks the node until a
|
||||
* word comes for it on any port, or one of its
|
||||
* offers is taken, whichever is first.
|
||||
* 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"
|
||||
* gives V4_PORTS + k: the offer on port k. Either way 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
|
||||
* writer to it can tell.
|
||||
*
|
||||
* 1 if the node is blocked in the wait with nothing given yet. */
|
||||
int v4_node_in_wait(const v4_node *n);
|
||||
|
||||
/* The offer on port k has been taken: the node is unblocked and will fetch
|
||||
* 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);
|
||||
|
||||
/* 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
|
||||
* word offered on the lowest such port -- asking, request, ask_port -- and
|
||||
* v4_node_port_served then completes the wait as v4_node_offer_taken would.
|
||||
* 1 if it did. */
|
||||
int v4_node_offer_as_write(v4_node *n);
|
||||
|
||||
/* The neighbour on `port` writes `value`: the node, blocked reading that
|
||||
* port or "any", is unblocked and will fetch it. */
|
||||
void v4_node_port_give(v4_node *n, unsigned port, v4_cell value);
|
||||
|
||||
@@ -209,6 +209,36 @@ unsigned v4_fabric_step(v4_fabric *f)
|
||||
for (i = 0; i < f->capacity; i++)
|
||||
if (awake(f, i) && f->place[i].node->n.reading && !f->place[i].node->n.given) done += hand_over_read(f, i);
|
||||
|
||||
/* THE WAIT (docs/v4.0.0/MESH.md 7c). A node in the wait that was given a
|
||||
* word above is no longer in it: a word for a node comes before its own
|
||||
* offer. Of the rest, each offer is taken by whoever is ready for it: a
|
||||
* device that takes, or a node blocked reading that port or any -- which
|
||||
* a node in the wait is, so of two that offer to each other the one at
|
||||
* the lower place writes and the other's offers are withdrawn. */
|
||||
for (i = 0; i < f->capacity; i++) {
|
||||
v4_node *n;
|
||||
unsigned k;
|
||||
if (!awake(f, i)) continue;
|
||||
n = &f->place[i].node->n;
|
||||
if (!v4_node_in_wait(n)) continue;
|
||||
for (k = 0; k < V4_PORTS; k++) {
|
||||
const v4_wire *w = &f->place[i].wire[k];
|
||||
if (!(n->offers & (1u << k))) continue;
|
||||
if (w->kind == V4_WIRE_DEVICE) {
|
||||
if (w->device->take && w->device->take(w->device->self, n->offer[k])) { (void)v4_node_offer_taken(n, k); done++; break; }
|
||||
} else if (w->kind == V4_WIRE_NODE && awake(f, w->node)) {
|
||||
v4_node *r = &f->place[w->node].node->n;
|
||||
if (r->reading && !r->given && (r->read_port == w->port || r->read_port == V4_PORT_ANY)) {
|
||||
v4_cell word = n->offer[k];
|
||||
(void)v4_node_offer_taken(n, k);
|
||||
v4_node_port_give(r, w->port, word);
|
||||
done++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* a node blocked on the first word of a message to a node that is not
|
||||
* reading, while a device of its own has something for it */
|
||||
if (f->interrupt_error != 0)
|
||||
@@ -236,6 +266,11 @@ unsigned v4_fabric_step(v4_fabric *f)
|
||||
if ((n->asking && f->place[i].wire[n->ask_port].kind == V4_WIRE_NONE)
|
||||
|| (n->reading && !n->given && n->read_port != V4_PORT_ANY && f->place[i].wire[n->read_port].kind == V4_WIRE_NONE))
|
||||
done += (unsigned)v4_node_port_gone(n, f->gone_error);
|
||||
else if (v4_node_in_wait(n)) { /* an offer on a port with nothing on it */
|
||||
unsigned 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; }
|
||||
}
|
||||
}
|
||||
|
||||
return done;
|
||||
|
||||
+54
-6
@@ -93,7 +93,13 @@ void v4_node_store(v4_node *n, v4_cell addr, v4_cell value)
|
||||
n->asking = 1;
|
||||
return;
|
||||
}
|
||||
if (port >= 0) { v4_node_fault(n, V4_FAULT_ADDRESS, addr); return; } /* "any" and "which port" are not written to */
|
||||
if (port >= (int)V4_PORT_OFFER && port < (int)V4_PORT_WAIT) { /* an offer: remembered until the wait */
|
||||
unsigned k = (unsigned)port - V4_PORT_OFFER;
|
||||
n->offer[k] = value;
|
||||
n->offers |= 1u << k;
|
||||
return;
|
||||
}
|
||||
if (port >= 0) { v4_node_fault(n, V4_FAULT_ADDRESS, addr); return; } /* "any", "which port" and the rest are not written to */
|
||||
}
|
||||
/* The stack registers: a store empties the stack. -1 when not attached. */
|
||||
if (addr == n->dstack_reg) { v4_dstack_clear(&n->ds); return; }
|
||||
@@ -122,6 +128,14 @@ v4_cell v4_node_fetch(v4_node *n, v4_cell addr)
|
||||
if (port == (int)V4_PORTS + 1) return (v4_cell)n->last_from;
|
||||
if (port == (int)V4_PORTS + 2) { n->since_look = 0; return (v4_cell)n->writers; }
|
||||
if (port == (int)V4_PORTS + 3) { n->since_look = 0; return (v4_cell)n->readers; }
|
||||
if (port == (int)V4_PORT_WAIT) {
|
||||
/* the wait: the word that came, or 0 for an offer taken */
|
||||
if (!n->given) return 0;
|
||||
n->given = 0;
|
||||
n->last_from = n->given_port;
|
||||
return n->given_value;
|
||||
}
|
||||
if (port >= (int)V4_PORT_OFFER) return 0; /* an offer is not read back */
|
||||
if (port >= 0) {
|
||||
/* the word its neighbour wrote; v4_node_read_ready has said it is there */
|
||||
if (!n->given) return 0;
|
||||
@@ -180,6 +194,9 @@ void v4_node_port_attach(v4_node *n, v4_cell base)
|
||||
n->writers = 0;
|
||||
n->readers = 0;
|
||||
n->since_look = 0;
|
||||
n->offers = 0;
|
||||
n->waiting = 0;
|
||||
n->offer_asked = 0;
|
||||
}
|
||||
|
||||
void v4_node_port_status(v4_node *n, unsigned writers, unsigned readers)
|
||||
@@ -190,17 +207,18 @@ void v4_node_port_status(v4_node *n, unsigned writers, unsigned readers)
|
||||
|
||||
int v4_node_port_index(const v4_node *n, v4_cell addr)
|
||||
{
|
||||
if (n->port < 0 || addr < n->port || addr > n->port + (v4_cell)V4_PORTS + 3) return -1;
|
||||
if (n->port < 0 || addr < n->port || addr > n->port + (v4_cell)V4_PORT_WAIT) return -1;
|
||||
return (int)(addr - n->port);
|
||||
}
|
||||
|
||||
int v4_node_read_ready(v4_node *n, v4_cell addr)
|
||||
{
|
||||
int port = v4_node_port_index(n, addr);
|
||||
if (port < 0 || port > (int)V4_PORT_ANY) return 1;
|
||||
if (n->given && (port == (int)V4_PORT_ANY || (unsigned)port == n->given_port)) return 1;
|
||||
if (port < 0 || (port > (int)V4_PORT_ANY && port != (int)V4_PORT_WAIT)) return 1;
|
||||
if (n->given && (port >= (int)V4_PORT_ANY || (unsigned)port == n->given_port)) return 1;
|
||||
n->reading = 1;
|
||||
n->read_port = (unsigned)port;
|
||||
n->waiting = port == (int)V4_PORT_WAIT; /* the wait is a read of any port, with offers standing */
|
||||
n->read_port = n->waiting ? V4_PORT_ANY : (unsigned)port;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -210,6 +228,31 @@ void v4_node_port_give(v4_node *n, unsigned port, v4_cell value)
|
||||
n->given_value = value;
|
||||
n->given_port = port;
|
||||
n->reading = 0;
|
||||
if (n->waiting) { n->waiting = 0; n->offers = 0; } /* the wait is over: every offer is withdrawn */
|
||||
}
|
||||
|
||||
int v4_node_in_wait(const v4_node *n)
|
||||
{
|
||||
return n->waiting && n->reading && !n->given;
|
||||
}
|
||||
|
||||
int v4_node_offer_taken(v4_node *n, unsigned k)
|
||||
{
|
||||
if (!v4_node_in_wait(n) || k >= V4_PORTS || !(n->offers & (1u << k))) return 0;
|
||||
v4_node_port_give(n, V4_PORTS + k, 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int v4_node_offer_as_write(v4_node *n)
|
||||
{
|
||||
unsigned k;
|
||||
if (!v4_node_in_wait(n) || n->offers == 0 || n->asking) return 0;
|
||||
for (k = 0; !(n->offers & (1u << k)); k++) { }
|
||||
n->request = n->offer[k];
|
||||
n->ask_port = k;
|
||||
n->asking = 1;
|
||||
n->offer_asked = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void v4_node_born(v4_node *n, v4_cell base)
|
||||
@@ -223,6 +266,10 @@ void v4_node_port_served(v4_node *n)
|
||||
{
|
||||
n->asking = 0;
|
||||
n->since_look++;
|
||||
if (n->offer_asked) { /* what was served was an offer: the wait is over */
|
||||
n->offer_asked = 0;
|
||||
v4_node_port_give(n, V4_PORTS + n->ask_port, 0);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned v4_node_words_since_look(const v4_node *n)
|
||||
@@ -233,7 +280,8 @@ unsigned v4_node_words_since_look(const v4_node *n)
|
||||
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 (n->asking) n->asking = 0;
|
||||
if (n->asking) { n->asking = 0; if (n->offer_asked) { n->offer_asked = 0; n->reading = 0; n->waiting = 0; n->offers = 0; } }
|
||||
else if (v4_node_in_wait(n) && n->offers != 0) { n->reading = 0; n->waiting = 0; n->offers = 0; }
|
||||
else if (n->reading && !n->given && n->read_port != V4_PORT_ANY) n->reading = 0;
|
||||
else return 0;
|
||||
v4_node_store(n, n->error_reg, code); /* raises it: P is the node's handler now */
|
||||
|
||||
+3
-1
@@ -73,7 +73,7 @@
|
||||
#define OWED (BUF0_W - 112) /* those: for each, the node it is owed to and the node the refused message was for */
|
||||
#define ACL_HOOK (BUF0_W - 6) /* the xt of the access control recheck word, or 0 */
|
||||
#define LINE_STATUS (BUF0_W - 9) /* how the last line ended: 0 QUIT, 1 completed, 2 an error */
|
||||
#define PORT (BUF0_W - 32) /* the node's ports (node.h): V4_PORTS of them, then "any port" and "which port". Port 0 is where its requests go. */
|
||||
#define PORT (BUF0_W - 144) /* the node's ports (node.h): V4_PORTS of them, then "any port", "which port", who writes, who reads, an offer for each port, and the wait: 2 * V4_PORTS + 5 cells, between the output buffer and A_FOUND. Port 0 is where its requests go. */
|
||||
#define CONSOLE (BUF0_W - 16) /* the node what this one prints is sent to; 0: whoever sent the text being served */
|
||||
#define ROUTE_COUNT (BUF0_W - 17) /* how many entries the table of ways holds */
|
||||
#define ROUTE_DEFAULT (BUF0_W - 18) /* the port address for a node not in the table, or 0: there is none */
|
||||
@@ -124,6 +124,8 @@ typedef char host_map_ports_fit[(V4_PORTS <= 8u) ? 1 : -1];
|
||||
#endif
|
||||
/* the messages waiting are above the dictionary */
|
||||
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 */
|
||||
typedef char host_map_port_block_fits[(PORT + 2 * (v4_cell)V4_PORTS + 5 <= BUF0_W - 115) ? 1 : -1];
|
||||
|
||||
/* 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. */
|
||||
|
||||
+149
-1
@@ -15,7 +15,7 @@ static int failures = 0, checks = 0;
|
||||
#define CHECK(c,...) do{checks++; if(!(c)){failures++; printf("FAIL %s:%d: ",__FILE__,__LINE__); printf(__VA_ARGS__); printf("\n");}}while(0)
|
||||
|
||||
/* The memory map is open (D-4); the test chooses where the ports are. */
|
||||
#define PB ((v4_cell)(V4_NODE_WORDS - 16u)) /* port 0 */
|
||||
#define PB ((v4_cell)(V4_NODE_WORDS - 32u)) /* port 0: the ports and what follows them end below the top of memory */
|
||||
#define ANY (PB + (v4_cell)V4_PORT_ANY)
|
||||
#define ORG ((v4_cell)16) /* where a test's programme starts */
|
||||
#define OUT ((v4_cell)900) /* where it leaves what it found */
|
||||
@@ -45,6 +45,31 @@ static unsigned loaded(unsigned k)
|
||||
/* the programme ends by waiting at a port nothing is wired to */
|
||||
static void wait_for_ever(void) { LIT(PB + 7); O(BANG_B); O(FETCH_B); (void)v4_asm_label(&as); } /* and the last word is written out */
|
||||
|
||||
/* ---- the wait (MESH.md 7c) ---- */
|
||||
static void fresh(void)
|
||||
{
|
||||
unsigned k;
|
||||
v4_fabric_init(&f, places, 4);
|
||||
for (k = 0; k < 4; k++) v4_node_reset(&pool[k].n);
|
||||
}
|
||||
/* offer `word` on port k */
|
||||
static void offer(unsigned k, v4_cell word) { LIT(word); LIT(PB + (v4_cell)V4_PORT_OFFER + (v4_cell)k); O(BANG_A); O(STORE_A); }
|
||||
/* the wait: what it gives goes to `at`, and which port to `at + 1` */
|
||||
static void wait_into(v4_cell at)
|
||||
{
|
||||
LIT(PB + (v4_cell)V4_PORT_WAIT); O(BANG_B); O(FETCH_B); LIT(at); O(BANG_A); O(STORE_A);
|
||||
LIT(PB + (v4_cell)V4_PORTS + 1); O(BANG_B); O(FETCH_B); LIT(at + 1); O(BANG_A); O(STORE_A);
|
||||
}
|
||||
static void done_mark(void) { LIT(1); LIT(OUT + 2); O(BANG_A); O(STORE_A); }
|
||||
static void count_for_ever(void)
|
||||
{
|
||||
v4_cell again;
|
||||
LIT(OUT); O(BANG_A);
|
||||
again = v4_asm_label(&as);
|
||||
O(FETCH_A); LIT(1); O(ADD); O(STORE_A);
|
||||
v4_asm_branch(&as, V4_OP_JUMP, again);
|
||||
}
|
||||
|
||||
/* a device that says whether it has something pending */
|
||||
static int *dev_has;
|
||||
static int device_has(void *self) { (void)self; return *dev_has; }
|
||||
@@ -423,6 +448,129 @@ int main(void)
|
||||
CHECK(ND(a)->n.asking && ND(a)->n.request == 2 && ND(a)->n.faults == 0, "blocked on its second word with the device still pending, the writer waits");
|
||||
}
|
||||
|
||||
/* ---- the wait (MESH.md 7c): a node offers a word and sleeps ---- */
|
||||
{
|
||||
static stream none;
|
||||
static v4_device sink;
|
||||
v4_uheat_t clock;
|
||||
sink.take = stream_take; sink.give = 0; sink.self = &none;
|
||||
|
||||
/* taken by a reader */
|
||||
fresh();
|
||||
a = loaded(0); offer(0, 7); wait_into(OUT); done_mark(); wait_for_ever();
|
||||
b = loaded(1); LIT(PB + 3); O(BANG_B); O(FETCH_B); LIT(OUT); O(BANG_A); O(STORE_A); wait_for_ever();
|
||||
CHECK(v4_asm_ok(&as) && v4_fabric_wire(&f, a, 0, b, 3), "a node that offers, wired to one that reads");
|
||||
(void)settle(300);
|
||||
CHECK(ND(b)->n.mem[OUT] == 7, "the reader has the word offered: %ld", (long)ND(b)->n.mem[OUT]);
|
||||
CHECK(ND(a)->n.mem[OUT] == 0 && ND(a)->n.mem[OUT + 1] == (v4_cell)V4_PORTS + 0 && ND(a)->n.mem[OUT + 2] == 1 && ND(a)->n.offers == 0,
|
||||
"the one that offered is told its offer on port 0 was taken, and has none left: %ld from %ld", (long)ND(a)->n.mem[OUT], (long)ND(a)->n.mem[OUT + 1]);
|
||||
|
||||
/* taken by a device */
|
||||
fresh(); none.took_n = 0;
|
||||
a = loaded(0); offer(0, 7); wait_into(OUT); done_mark(); wait_for_ever();
|
||||
CHECK(v4_fabric_wire_device(&f, a, 0, &sink), "the same, wired to a device that takes what is written");
|
||||
(void)settle(300);
|
||||
CHECK(none.took_n == 1 && none.took[0] == 7 && ND(a)->n.mem[OUT + 1] == (v4_cell)V4_PORTS + 0 && ND(a)->n.mem[OUT + 2] == 1, "the device has the word, and the node is told");
|
||||
|
||||
/* a neighbour that never reads */
|
||||
fresh();
|
||||
a = loaded(0); offer(0, 7); wait_into(OUT); done_mark(); wait_for_ever();
|
||||
b = loaded(1); count_for_ever();
|
||||
CHECK(v4_asm_ok(&as) && v4_fabric_wire(&f, a, 0, b, 3), "a node that offers, wired to one that never reads");
|
||||
for (i = 0; i < 200; i++) (void)v4_fabric_step(&f);
|
||||
clock = ND(a)->es.anticlock;
|
||||
for (i = 0; i < 100; i++) (void)v4_fabric_step(&f);
|
||||
CHECK(v4_node_in_wait(&ND(a)->n) && ND(a)->es.anticlock == clock && ND(a)->n.mem[OUT + 2] == 0, "it is in the wait, and executes nothing there");
|
||||
CHECK((ND(b)->n.readers & (1u << 3)) != 0, "and shows to its neighbour as waiting to read");
|
||||
|
||||
/* withdrawn by a word that comes for it */
|
||||
c = loaded(2); LIT(PB + 0); O(BANG_B); LIT(9); O(STORE_B); wait_for_ever();
|
||||
CHECK(v4_asm_ok(&as) && v4_fabric_wire(&f, c, 0, a, 2), "a third node writes to it");
|
||||
for (i = 0; i < 100; i++) (void)v4_fabric_step(&f);
|
||||
CHECK(ND(a)->n.mem[OUT] == 9 && ND(a)->n.mem[OUT + 1] == 2 && ND(a)->n.mem[OUT + 2] == 1 && ND(a)->n.offers == 0,
|
||||
"it gets that word, from that port, and its offer is withdrawn: %ld from %ld", (long)ND(a)->n.mem[OUT], (long)ND(a)->n.mem[OUT + 1]);
|
||||
CHECK(!ND(b)->n.given && !ND(c)->n.asking, "the node offered to was given nothing, and the writer was served");
|
||||
|
||||
/* an offer that could be taken in the step a word comes: the word comes first */
|
||||
fresh();
|
||||
a = loaded(0); offer(0, 7); wait_into(OUT); offer(0, 7); wait_into(OUT + 3); done_mark(); wait_for_ever();
|
||||
b = loaded(1); LIT(PB + 3); O(BANG_B); O(FETCH_B); LIT(OUT); O(BANG_A); O(STORE_A); wait_for_ever();
|
||||
c = loaded(2); LIT(PB + 0); O(BANG_B); LIT(9); O(STORE_B); wait_for_ever();
|
||||
CHECK(v4_asm_ok(&as) && v4_fabric_wire(&f, a, 0, b, 3) && v4_fabric_wire(&f, c, 0, a, 2), "one offers to a reader while a third writes to it");
|
||||
v4_fabric_sleep(&f, a);
|
||||
for (i = 0; i < 50; i++) (void)v4_fabric_step(&f);
|
||||
CHECK(ND(b)->n.reading && ND(c)->n.asking, "the reader and the writer are both blocked");
|
||||
v4_fabric_sleep(&f, b); v4_fabric_sleep(&f, c); v4_fabric_wake(&f, a);
|
||||
for (i = 0; i < 50; i++) (void)v4_fabric_step(&f);
|
||||
CHECK(v4_node_in_wait(&ND(a)->n), "and then the one that offers is in the wait");
|
||||
v4_fabric_wake(&f, b); v4_fabric_wake(&f, c);
|
||||
(void)v4_fabric_step(&f);
|
||||
CHECK(!v4_node_in_wait(&ND(a)->n) && !ND(c)->n.asking && ND(b)->n.reading && !ND(b)->n.given, "in one step: the word for it comes first, and the reader is given nothing");
|
||||
(void)settle(300);
|
||||
CHECK(ND(a)->n.mem[OUT] == 9 && ND(a)->n.mem[OUT + 1] == 2 && ND(a)->n.mem[OUT + 3] == 0 && ND(a)->n.mem[OUT + 4] == (v4_cell)V4_PORTS + 0 && ND(b)->n.mem[OUT] == 7,
|
||||
"it offers again, and the reader gets the word once");
|
||||
|
||||
/* two nodes each offering to the other */
|
||||
fresh();
|
||||
a = loaded(0); offer(0, 5); wait_into(OUT); done_mark(); wait_for_ever();
|
||||
b = loaded(1); offer(3, 6); wait_into(OUT); done_mark(); wait_for_ever();
|
||||
CHECK(v4_asm_ok(&as) && v4_fabric_wire(&f, a, 0, b, 3), "two nodes each offer to the other");
|
||||
(void)settle(300);
|
||||
CHECK(ND(a)->n.mem[OUT] == 0 && ND(a)->n.mem[OUT + 1] == (v4_cell)V4_PORTS + 0, "the one at the lower place has its offer taken");
|
||||
CHECK(ND(b)->n.mem[OUT] == 5 && ND(b)->n.mem[OUT + 1] == 3, "the other gets the word, from the port it came by");
|
||||
CHECK(ND(a)->n.offers == 0 && ND(b)->n.offers == 0 && ND(a)->n.mem[OUT + 2] == 1 && ND(b)->n.mem[OUT + 2] == 1, "and neither has an offer left");
|
||||
|
||||
/* three in a ring, each offering to the next */
|
||||
fresh();
|
||||
a = loaded(0); offer(0, 11); wait_into(OUT); done_mark(); wait_for_ever();
|
||||
b = loaded(1); offer(0, 12); wait_into(OUT); done_mark(); wait_for_ever();
|
||||
c = loaded(2); offer(0, 13); wait_into(OUT); done_mark(); wait_for_ever();
|
||||
CHECK(v4_asm_ok(&as) && v4_fabric_wire(&f, a, 0, b, 1) && v4_fabric_wire(&f, b, 0, c, 1) && v4_fabric_wire(&f, c, 0, a, 1), "three nodes in a ring, each offering to the next");
|
||||
CHECK(settle(1000) < 1000, "they come to rest");
|
||||
CHECK(ND(a)->n.mem[OUT + 1] == (v4_cell)V4_PORTS + 0 && ND(b)->n.mem[OUT] == 11 && ND(b)->n.mem[OUT + 1] == 1, "the first's offer was taken by the second");
|
||||
CHECK(v4_node_in_wait(&ND(c)->n) && ND(c)->n.offers == 1u && ND(c)->n.mem[OUT + 2] == 0 && !ND(a)->n.given, "the third still offers to the first, which is not reading: nothing was handed over twice");
|
||||
|
||||
/* a neighbour that is asleep */
|
||||
fresh();
|
||||
a = loaded(0); offer(0, 7); wait_into(OUT); done_mark(); wait_for_ever();
|
||||
b = loaded(1); LIT(PB + 3); O(BANG_B); O(FETCH_B); LIT(OUT); O(BANG_A); O(STORE_A); wait_for_ever();
|
||||
CHECK(v4_asm_ok(&as) && v4_fabric_wire(&f, a, 0, b, 3), "a node that offers, wired to a reader");
|
||||
v4_fabric_sleep(&f, a);
|
||||
for (i = 0; i < 50; i++) (void)v4_fabric_step(&f);
|
||||
v4_fabric_sleep(&f, b); v4_fabric_wake(&f, a);
|
||||
for (i = 0; i < 100; i++) (void)v4_fabric_step(&f);
|
||||
CHECK(v4_node_in_wait(&ND(a)->n) && ND(b)->n.reading && !ND(b)->n.given, "while the reader is asleep the offer stands");
|
||||
v4_fabric_wake(&f, b);
|
||||
(void)settle(300);
|
||||
CHECK(ND(b)->n.mem[OUT] == 7 && ND(a)->n.mem[OUT + 2] == 1, "woken, it takes it");
|
||||
|
||||
/* a port with nothing on it */
|
||||
fresh();
|
||||
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");
|
||||
for (i = 0; i < 50; i++) (void)v4_fabric_step(&f);
|
||||
v4_fabric_gone_error(&f, 18);
|
||||
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");
|
||||
v4_node_error_attach(&ND(a)->n, OUT + 20);
|
||||
v4_node_fault_attach(&ND(a)->n, 900);
|
||||
(void)v4_fabric_step(&f);
|
||||
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,
|
||||
"one that can take an error has it raised, and its offer is withdrawn");
|
||||
|
||||
/* a host with one node, whose devices always take */
|
||||
fresh();
|
||||
a = loaded(0); offer(1, 21); offer(3, 23); wait_into(OUT); done_mark(); wait_for_ever();
|
||||
for (i = 0; i < 50; i++) (void)v4_fabric_step(&f);
|
||||
CHECK(v4_node_in_wait(&ND(a)->n) && ND(a)->n.offers == ((1u << 1) | (1u << 3)), "a node offers on two ports and is in the wait");
|
||||
CHECK(v4_node_offer_as_write(&ND(a)->n) && ND(a)->n.asking && ND(a)->n.ask_port == 1 && ND(a)->n.request == 21, "its host turns that into a write of the lower port's word");
|
||||
v4_node_port_served(&ND(a)->n);
|
||||
for (i = 0; i < 50; i++) (void)v4_fabric_step(&f);
|
||||
CHECK(!ND(a)->n.asking && ND(a)->n.mem[OUT] == 0 && ND(a)->n.mem[OUT + 1] == (v4_cell)V4_PORTS + 1 && ND(a)->n.mem[OUT + 2] == 1 && ND(a)->n.offers == 0,
|
||||
"and when it is served the node is told that offer was taken");
|
||||
CHECK(!v4_node_offer_as_write(&ND(a)->n), "a node that is not in the wait is left alone");
|
||||
}
|
||||
|
||||
printf(" %d checks, %d failures\n", checks, failures);
|
||||
return failures != 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user