Files
LithosAnanake/v4/tests/test_fabric.c
T
rajamesandClaude Opus 5.5 66b26f823c fix(v4.0.0): step 6e final review -- texts begun in order; a word no one will read lets its writer go; a cut wire wakes a wait for room; a full console wire is a refusal, not a fault
The final review of step 6e, by a fresh reviewer told to break it, and
what was ruled: fix them all, test first.  Each has a test that failed
before the mend.

- Two texts from one node could be begun in the wrong order when room
  for their answers came between the node's look at the first and at the
  second.  Texts whose answers go by the same port are begun in the order
  they came: (D-MASK), quit.v4.  102 of 700 timings were wrong before.
- A word written with PORT! to a node that speaks whole messages, or to
  a console's port, blocked its writer for ever and cut off every node
  beyond it.  It is error 18 now, whether that node is asleep, running
  or stuck: fabric.c, node.c.
- A node asleep for room on one wire is woken when another of its wires
  is cut.
- A line there was no room for on the lone node's wire was reported as a
  fault and ended the hosted program.  It is Message refused, and the
  prompt comes back: V4_BOOT_LINE_REFUSED.  hosted-check has the case;
  no bare-metal boot has typed it.
- Ruled: text behind held output for the same port is refused though it
  would fit.  MESH.md 7d.4 said otherwise and is corrected.
- The removal test also cuts and re-makes a wire, and has an exchange in
  which output is held at a full wire: 26,814 disturbances in all.
- MESH.md and v4/README.md no longer say that a stuck node holds up no
  other node, that only printing waits, or that no node is left waiting
  on one that is gone; 7d.6 lists the limits and what the products do
  that they did not before.

make -C v4 test and sanitize at both widths, hosted-check, lint.  Three
bare-metal boots: logs/20261013-194827 (amd64), -195131 (aarch64),
-195518 (riscv64).  POST 538 of 538, word_count=317,
dict_hash=0xe5a57f3eb72ff94a on all six; the three sessions are the same
character for character.  The boots were built with the working tree's
uncommitted change to kernel/Makefile that adds the ISA to the kernel's
version line.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 20:02:33 -04:00

1040 lines
64 KiB
C

/* test_fabric.c -- nodes that talk: ports that block on a read as well as a
* write, a node born empty that executes what arrives at its port, and the
* fabric of nodes and wiring that changes while they run.
* docs/v4.0.0/MESH.md section 4 and step 1.
*
* At the level of opcodes: no word of any vocabulary is defined here.
*/
#include "v4/asm.h"
#include "v4/capsule.h"
#include "v4/fabric.h"
#include <stdio.h>
#include <string.h>
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 - 24u)) /* port 0: the ports, and the addresses after them (node.h), end inside memory */
#define P_WIRE (PB + (v4_cell)V4_PORT_WIRE)
#define P_A (PB + (v4_cell)V4_PORT_A)
#define P_B (PB + (v4_cell)V4_PORT_B)
#define P_DO (PB + (v4_cell)V4_PORT_DO)
#define P_HOW (PB + (v4_cell)V4_PORT_HOW)
#define P_HAVE (PB + (v4_cell)V4_PORT_HAVE)
#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 */
#define NODES 8
static v4_fabric_node pool[NODES];
static v4_place places[4], more_places[NODES];
static v4_fabric f;
static v4_asm as;
/* the node with that number in the fabric */
#define ND(id) (v4_fabric_node_at(&f, (id)))
#define O(name) v4_asm_op(&as, V4_OP_##name)
#define LIT(v) v4_asm_lit(&as, (v4_cell)(v))
/* A node with a programme already in it, started at ORG: for the tests that
* are about talking and not about being born. */
static unsigned loaded(unsigned k)
{
int id = v4_fabric_add(&f, &pool[k], PB);
pool[k].n.p = ORG;
v4_asm_begin(&as, &pool[k].n, ORG);
return (unsigned)id;
}
/* 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 */
/* Let time pass until nothing more can happen, or `max` steps. */
static unsigned settle(unsigned max)
{
unsigned steps = 0;
while (steps < max && v4_fabric_step(&f) != 0) steps++;
return steps;
}
/* ---- a device: a neighbour that is not a node ---- */
typedef struct { const v4_cell *words; unsigned count, at; v4_cell took[16]; unsigned took_n; } stream;
static int stream_give(void *self, v4_cell *value)
{
stream *s = (stream *)self;
if (s->at >= s->count) return 0;
*value = s->words[s->at++];
return 1;
}
static int stream_take(void *self, v4_cell value)
{
stream *s = (stream *)self;
if (s->took_n >= 16) return 0;
s->took[s->took_n++] = value;
return 1;
}
/* for v4_capsule_write: each word of the capsule, into the stream's own list */
static void collect(void *self, v4_cell word)
{
stream *s = (stream *)self;
((v4_cell *)s->words)[s->count++] = word;
}
/* What a neighbour sends an empty node to put `count` words at `addr` and
* start there (MESH.md section 5): for the F18, and for this node, code a
* node executes straight from its port. */
static unsigned capsule(v4_cell *out, const v4_cell *words, unsigned count, v4_cell addr)
{
static const unsigned set_up[V4_SLOT_COUNT] = { V4_OP_FETCH_P, V4_OP_BANG_A, V4_OP_FETCH_P, V4_OP_PUSH, V4_OP_NOP, V4_OP_NOP };
static const unsigned pump[V4_SLOT_COUNT] = { V4_OP_FETCH_P, V4_OP_STORE_INC, V4_OP_UNEXT, V4_OP_NOP, V4_OP_NOP, V4_OP_NOP };
static v4_node scratch;
v4_asm j;
unsigned at = 0, i;
out[at++] = (v4_cell)v4_iword_assemble(set_up, 0); /* @p a! @p push */
out[at++] = addr;
out[at++] = (v4_cell)(count - 1);
out[at++] = (v4_cell)v4_iword_assemble(pump, 0); /* @p !+ unext */
for (i = 0; i < count; i++) out[at++] = words[i];
v4_node_reset(&scratch);
v4_asm_begin(&j, &scratch, 0);
v4_asm_branch(&j, V4_OP_JUMP, addr); /* jump addr */
(void)v4_asm_label(&j);
out[at++] = scratch.mem[0];
return at;
}
/* ---- messages on a wire: what the tests below drive a node with ---- */
/* A message in a node's memory: seven words at `hdr` and its text at `text`. */
static void put_msg(v4_node *n, v4_cell hdr, v4_cell text, v4_cell len, v4_cell seed)
{
unsigned i;
for (i = 0; i < 7; i++) n->mem[hdr + (v4_cell)i] = seed * 100 + (v4_cell)i;
n->mem[hdr + 6] = len;
for (i = 0; len > 0 && len <= 1024 && i < ((unsigned)len + 3u) / 4u; i++) n->mem[text + (v4_cell)i] = seed * 1000 + (v4_cell)i;
}
/* 1 if the message in the node's memory is the one put_msg makes, but for the word `except` of the header */
static int msg_is_but(const v4_node *n, v4_cell hdr, v4_cell text, v4_cell len, v4_cell seed, unsigned except)
{
unsigned i;
for (i = 0; i < 7; i++) {
v4_cell want = i == 6 ? len : seed * 100 + (v4_cell)i;
if (i != except && n->mem[hdr + (v4_cell)i] != want) return 0;
}
for (i = 0; i < ((unsigned)len + 3u) / 4u; i++) if (n->mem[text + (v4_cell)i] != seed * 1000 + (v4_cell)i) return 0;
return 1;
}
static int msg_is(const v4_node *n, v4_cell hdr, v4_cell text, v4_cell len, v4_cell seed)
{
return msg_is_but(n, hdr, text, len, seed, 7);
}
/* A queue filled until `leave` cells are free. */
static void fill(v4_wire_queue *q, unsigned leave)
{
v4_cell h[7] = { 1, 2, 3, 4, 5, 6, 1024 };
static v4_cell t[V4_WIRE_CELLS];
while (V4_WIRE_CELLS - q->used >= 263u + leave) (void)v4_wire_put(q, h, t);
if (V4_WIRE_CELLS - q->used > leave) {
h[6] = 4 * (v4_cell)(V4_WIRE_CELLS - q->used - leave - 7u);
(void)v4_wire_put(q, h, t);
}
}
/* Ask node `id` for an operation from outside, as its own programme would by
* storing to its wire, A, B and Do; let one step of the engine pass; and give
* how it went, or -99 if the node is still blocked at it. */
static v4_cell ask(unsigned id, v4_cell wire, v4_cell a, v4_cell b, v4_cell op)
{
v4_node *n = &ND(id)->n;
v4_node_store(n, P_WIRE, wire);
v4_node_store(n, P_A, a);
v4_node_store(n, P_B, b);
v4_node_store(n, P_DO, op);
(void)v4_fabric_step(&f);
return n->doing ? -99 : v4_node_fetch(n, P_HOW);
}
/* The same for a take, a move or a drop, which are done only on the message
* last looked at: the node looks first, as a node does. */
static v4_cell seen(unsigned id, v4_cell wire, v4_cell a, v4_cell b, v4_cell op)
{
(void)ask(id, wire, 500, 0, 1);
return ask(id, wire, a, b, op);
}
int main(void)
{
unsigned a, b, c, i, steps;
printf("v4 fabric tests: V4_CELL_BITS=%d, %u ports to a node\n", V4_CELL_BITS, (unsigned)V4_PORTS);
/* ---- a node newly born ---- */
v4_fabric_init(&f, places, 4);
CHECK(v4_fabric_node_at(&f, 0) == 0 && v4_fabric_step(&f) == 0, "an empty fabric: no nodes, and nothing happens");
a = (unsigned)v4_fabric_add(&f, &pool[0], PB);
CHECK(a == 0 && v4_fabric_node_at(&f, 0) == &pool[0], "a node is born and has a number");
CHECK(pool[0].n.p == ANY && pool[0].n.ds.depth == 0 && pool[0].n.rs.depth == 0, "its P is at \"any port\" and its stacks are empty");
for (i = 0; i < V4_NODE_WORDS && pool[0].n.mem[i] == 0; i++) { }
CHECK(i == V4_NODE_WORDS, "and there is nothing in its memory");
CHECK(v4_fabric_step(&f) == 0 && pool[0].n.reading && pool[0].n.read_port == V4_PORT_ANY, "it is blocked reading its ports");
CHECK(v4_fabric_step(&f) == 0 && pool[0].es.anticlock == 0, "and executes nothing while it waits: its anti-clock has not moved");
CHECK(v4_fabric_remove(&f, a) == &pool[0] && v4_fabric_node_at(&f, 0) == 0, "it is removed, and its storage comes back");
/* ---- two nodes exchange words ---- */
v4_fabric_init(&f, places, 4);
a = loaded(0); /* writes three words, then reads one */
LIT(PB + 2); O(BANG_B); LIT(10); O(STORE_B); LIT(20); O(STORE_B); LIT(12); O(STORE_B);
O(FETCH_B); LIT(OUT); O(BANG_A); O(STORE_A);
wait_for_ever();
CHECK(v4_asm_ok(&as), "the writer assembles");
b = loaded(1); /* reads three, writes their sum back */
LIT(PB + 5); O(BANG_B); O(FETCH_B); O(FETCH_B); O(FETCH_B); O(ADD); O(ADD); O(STORE_B);
wait_for_ever();
CHECK(v4_asm_ok(&as), "the reader assembles");
steps = settle(1000);
CHECK(ND(a)->n.asking && ND(a)->n.ask_port == 2 && ND(a)->n.request == 10, "with no wire, the writer is blocked at its first write");
CHECK(ND(b)->n.reading && ND(b)->n.read_port == 5, "and the reader at its first read");
CHECK(v4_fabric_wire(&f, a, 2, b, 5), "port 2 of one is wired to port 5 of the other, while they wait");
steps = settle(1000);
CHECK(steps < 1000, "then everything that can happen happens, and stops: %u steps", steps);
CHECK(ND(a)->n.mem[OUT] == 42, "three words went one way and their sum came back: %ld", (long)ND(a)->n.mem[OUT]);
CHECK(ND(a)->n.reading && ND(b)->n.reading, "and both are waiting again");
{
v4_uheat_t ca = ND(a)->es.anticlock, cb = ND(b)->es.anticlock;
for (i = 0; i < 20; i++) (void)v4_fabric_step(&f);
CHECK(ND(a)->es.anticlock == ca && ND(b)->es.anticlock == cb, "a node that is waiting executes nothing, however long");
}
/* ---- an empty node is filled through its port, and runs what it was sent ---- */
{
static v4_node src;
static v4_cell prog[64], sent[96];
static stream s;
static v4_device dev;
unsigned len, count;
v4_node_reset(&src); /* the programme, assembled somewhere to be sent */
v4_asm_begin(&as, &src, ORG);
LIT(77); LIT(OUT); O(BANG_A); O(STORE_A);
LIT(PB + 1); O(BANG_B); LIT(99); O(STORE_B);
wait_for_ever();
count = (unsigned)(v4_asm_label(&as) - ORG);
CHECK(v4_asm_ok(&as) && count < 64, "a programme to send: %u words", count);
for (i = 0; i < count; i++) prog[i] = src.mem[ORG + i];
len = capsule(sent, prog, count, ORG);
/* sent by a device */
v4_fabric_init(&f, places, 4);
memset(&s, 0, sizeof s);
s.words = sent; s.count = len;
dev.give = stream_give; dev.take = stream_take; dev.self = &s;
b = (unsigned)v4_fabric_add(&f, &pool[2], PB);
CHECK(v4_fabric_wire_device(&f, b, 3, &dev), "a device is wired to port 3 of a node newly born");
steps = settle(5000);
CHECK(s.at == len, "the node took every word it was sent: %u of %u", s.at, len);
for (i = 0; i < count && ND(b)->n.mem[ORG + i] == prog[i]; i++) { }
CHECK(i == count, "the programme is in its memory, where it was sent to");
CHECK(ND(b)->n.mem[OUT] == 77, "and it ran: it left its mark");
CHECK(ND(b)->n.asking && ND(b)->n.ask_port == 1 && ND(b)->n.request == 99, "and is now writing to its port 1, which nothing is wired to");
CHECK(v4_fabric_wire_device(&f, b, 1, &dev) && settle(100) < 100 && s.took_n == 1 && s.took[0] == 99,
"a device wired there takes what it wrote");
/* sent by another node, which holds it in its own memory */
v4_fabric_init(&f, places, 4);
a = loaded(0);
for (i = 0; i < len; i++) ND(a)->n.mem[200 + i] = sent[i];
LIT(200); O(BANG_A); LIT(len - 1); O(PUSH); LIT(PB + 4); O(BANG_B);
(void)v4_asm_label(&as);
O(FETCH_INC); O(STORE_B); O(UNEXT); /* @+ !b unext: each write blocks in the middle of the word */
wait_for_ever();
CHECK(v4_asm_ok(&as), "a node that sends what is in its memory");
b = (unsigned)v4_fabric_add(&f, &pool[2], PB);
CHECK(v4_fabric_wire(&f, a, 4, b, 0), "is wired to a node newly born");
steps = settle(5000);
CHECK(ND(b)->n.mem[OUT] == 77 && ND(b)->n.asking && ND(b)->n.request == 99, "which takes it in and runs it, as from the device");
CHECK(ND(b)->n.last_from == 0, "it was listening to any port, and knows which it heard from: %u", ND(b)->n.last_from);
}
/* ---- a capsule of F18 code: any node's memory, sent to an empty node (capsule.h) ---- */
{
static v4_node src;
static v4_cell sent[400];
static stream s;
static v4_device dev;
unsigned k;
v4_node_reset(&src);
v4_asm_begin(&as, &src, 40); /* a programme, not at the start of memory */
LIT(123); LIT(OUT); O(BANG_A); O(STORE_A);
wait_for_ever();
CHECK(v4_asm_ok(&as), "a programme for the capsule");
src.mem[3] = 5; /* and words here and there: the first of memory, */
src.mem[200] = -1; src.mem[202] = 7; /* two with one zero between them, */
src.mem[300] = 9; src.mem[310] = 11; /* two with many, */
src.mem[PB - 1] = 13; /* and the last before the ports */
memset(&s, 0, sizeof s);
s.words = sent;
dev.give = stream_give; dev.take = stream_take; dev.self = &s;
s.count = 0;
s.count = v4_capsule_write(&src, 40, collect, &s);
CHECK(s.count > 0 && s.count < 400, "the capsule is %u words for a memory of %u", s.count, (unsigned)V4_NODE_WORDS);
s.at = 0;
v4_fabric_init(&f, places, 4);
b = (unsigned)v4_fabric_add(&f, &pool[0], PB);
CHECK(v4_fabric_wire_device(&f, b, 5, &dev), "it is sent to a node newly born");
steps = settle(5000);
CHECK(s.at == s.count, "which takes all of it: %u of %u", s.at, s.count);
for (k = 0; k < V4_NODE_WORDS && ND(b)->n.mem[k] == (k == (unsigned)OUT ? 123 : src.mem[k]); k++) { }
CHECK(k == V4_NODE_WORDS, "its memory is then the same, word for word (first difference at %u)", k);
CHECK(ND(b)->n.mem[OUT] == 123 && ND(b)->n.reading, "and it was started where the capsule says, and ran");
}
/* ---- a word is passed on by a node in between ---- */
v4_fabric_init(&f, places, 4);
a = loaded(0);
LIT(PB + 0); O(BANG_B); LIT(7); O(STORE_B); LIT(8); O(STORE_B); LIT(9); O(STORE_B);
wait_for_ever();
b = loaded(1); /* whatever comes on any port goes out on port 6 */
LIT(ANY); O(BANG_A); LIT(PB + 6); O(BANG_B);
{
v4_cell again = v4_asm_label(&as);
O(FETCH_A); O(STORE_B);
v4_asm_branch(&as, V4_OP_JUMP, again);
}
c = loaded(2);
LIT(PB + 1); O(BANG_B); LIT(OUT); O(BANG_A); O(FETCH_B); O(STORE_INC); O(FETCH_B); O(STORE_INC); O(FETCH_B); O(STORE_INC);
wait_for_ever();
CHECK(v4_asm_ok(&as), "three nodes in a row assemble");
CHECK(v4_fabric_wire(&f, a, 0, b, 2) && v4_fabric_wire(&f, b, 6, c, 1), "the first is wired to the second, the second to the third");
steps = settle(2000);
CHECK(ND(c)->n.mem[OUT] == 7 && ND(c)->n.mem[OUT + 1] == 8 && ND(c)->n.mem[OUT + 2] == 9,
"what the first wrote reached the third, in order, through the one in between");
CHECK(ND(b)->n.last_from == 2, "the one in between heard it on its port 2");
/* ---- the wiring is changed while they run ---- */
v4_fabric_init(&f, places, 4);
a = loaded(0); /* writes 1 2 3 4 ... to its port 0 for ever */
LIT(PB + 0); O(BANG_B); LIT(0);
{
v4_cell again = v4_asm_label(&as);
LIT(1); O(ADD); O(DUP); O(STORE_B);
v4_asm_branch(&as, V4_OP_JUMP, again);
}
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 + 3); O(BANG_B); O(FETCH_B); LIT(OUT); O(BANG_A); O(STORE_A); wait_for_ever();
CHECK(v4_asm_ok(&as), "a writer and two readers assemble");
CHECK(v4_fabric_wire(&f, a, 0, b, 3), "the writer is wired to the first reader");
(void)settle(500);
CHECK(ND(b)->n.mem[OUT] == 1 && ND(c)->n.mem[OUT] == 0, "who gets its first word");
CHECK(v4_fabric_wire(&f, a, 0, c, 3), "the same port is wired to the second reader instead");
(void)settle(500);
CHECK(ND(c)->n.mem[OUT] == 2, "who gets its next: %ld", (long)ND(c)->n.mem[OUT]);
CHECK(ND(a)->n.asking && ND(a)->n.request == 3, "and the writer waits with its third");
CHECK(v4_fabric_unwire(&f, a, 0) && settle(100) == 0 && ND(a)->n.asking, "with the wire cut it goes on waiting");
v4_fabric_gone_error(&f, 18);
CHECK(settle(100) == 0 && ND(a)->n.asking && ND(a)->n.faults == 0, "and still does when the fabric has an error for it: a bare node cannot take one");
v4_node_error_attach(&ND(a)->n, OUT + 20);
v4_node_fault_attach(&ND(a)->n, 900);
CHECK(v4_fabric_step(&f) == 1 && !ND(a)->n.asking && ND(a)->n.fault_kind == V4_FAULT_RAISED && ND(a)->n.fault_addr == 18
&& ND(a)->n.p == 900 + (v4_cell)V4_FAULT_RAISED && ND(a)->n.mem[OUT + 20] == 18,
"a node that can take an error is let go, and the error is raised on it: its P is its handler");
CHECK(!v4_node_port_gone(&ND(c)->n, 18), "a node that is not blocked at a port is not touched");
/* ---- A WORD WRITTEN TO WHAT WILL NOT READ ONE (found by the final review of step 6e) ----
* A node that writes a word to a port is blocked until it is taken. Two things will never take it: a
* device that takes no words -- one that speaks whole messages only -- and a node that is asleep at its
* wires, which executes nothing until a message comes. The writer is let go, as from a port with no one
* on it. A neighbour that is doing something else, or that the fabric has put to sleep, may yet read:
* the writer waits. */
{
static v4_wire_queue wq[4];
static const v4_device mute = { 0, 0, 0 }; /* it takes and gives no words */
static const v4_cell one[1] = { 5 };
static stream s;
static v4_device giver;
v4_fabric_init(&f, places, 4);
v4_fabric_queues(&f, wq, 4);
a = loaded(0); /* writes a word to its port 0, and then counts in OUT */
LIT(PB + 0); O(BANG_B); LIT(7); O(STORE_B);
LIT(OUT); O(BANG_A);
{
v4_cell again = v4_asm_label(&as);
O(FETCH_A); LIT(1); O(ADD); O(STORE_A);
v4_asm_branch(&as, V4_OP_JUMP, again);
}
CHECK(v4_asm_ok(&as) && v4_fabric_wire_device(&f, a, 0, &mute), "a writer, wired to a device that takes no words");
(void)settle(100);
CHECK(ND(a)->n.asking && ND(a)->n.mem[OUT] == 0, "it is blocked writing");
v4_fabric_gone_error(&f, 18);
(void)settle(100);
CHECK(ND(a)->n.asking && ND(a)->n.faults == 0, "a bare node, which cannot take an error, waits there");
v4_node_error_attach(&ND(a)->n, OUT + 20);
v4_node_fault_attach(&ND(a)->n, 900);
(void)v4_fabric_step(&f);
CHECK(!ND(a)->n.asking && ND(a)->n.fault_kind == V4_FAULT_RAISED && ND(a)->n.fault_addr == 18 && ND(a)->n.mem[OUT + 20] == 18,
"a node that can take one is let go: no one on that port");
v4_fabric_init(&f, places, 4);
v4_fabric_queues(&f, wq, 4);
v4_fabric_gone_error(&f, 18);
a = loaded(0);
LIT(PB + 0); O(BANG_B); LIT(7); O(STORE_B);
wait_for_ever();
b = loaded(1); /* reads a word from its port 5, and then sleeps at its wires */
LIT(PB + 5); O(BANG_B); O(FETCH_B); O(DROP);
LIT(P_DO); O(BANG_B); LIT(8); O(STORE_B);
wait_for_ever();
CHECK(v4_asm_ok(&as) && v4_fabric_wire(&f, a, 0, b, 3), "a writer wired to a node that is waiting to read another port");
v4_node_error_attach(&ND(a)->n, OUT + 20);
v4_node_fault_attach(&ND(a)->n, 900);
(void)settle(200);
CHECK(ND(a)->n.asking && ND(a)->n.faults == 0 && ND(b)->n.reading, "the neighbour is not asleep at its wires and may yet read: the writer waits");
v4_fabric_sleep(&f, b);
memset(&s, 0, sizeof s); s.words = one; s.count = 1;
giver.give = stream_give; giver.take = 0; giver.self = &s;
CHECK(v4_fabric_wire_device(&f, b, 5, &giver), "the neighbour is put to sleep by the fabric, and a device with a word for it is wired to its port 5");
(void)settle(200);
CHECK(ND(a)->n.asking && ND(a)->n.faults == 0 && !v4_node_doing(&ND(b)->n), "a neighbour the fabric has put to sleep takes nothing and does nothing: the writer waits");
v4_fabric_wake(&f, b);
for (i = 0; i < 200 && ND(a)->n.asking; i++) (void)v4_fabric_step(&f);
CHECK(v4_node_doing(&ND(b)->n) && ND(b)->n.do_op == 8, "woken, the neighbour takes its word and sleeps at its wires");
CHECK(!ND(a)->n.asking && ND(a)->n.fault_kind == V4_FAULT_RAISED && ND(a)->n.fault_addr == 18, "and the writer is let go: no one on that port will read a word");
/* nor does it matter that such a node is awake and running: once a node has asked for a whole message
* it is not one that words are written to. A stuck node does not hold whoever writes it a word. */
v4_fabric_init(&f, places, 4);
v4_fabric_queues(&f, wq, 4);
v4_fabric_gone_error(&f, 18);
a = loaded(0);
LIT(PB + 0); O(BANG_B); LIT(7); O(STORE_B);
wait_for_ever();
b = loaded(1); /* reads a word from its port 5, asks whether its wire 3 has room, and then counts for ever */
LIT(PB + 5); O(BANG_B); O(FETCH_B); O(DROP);
LIT(P_WIRE); O(BANG_B); LIT(3); O(STORE_B);
LIT(P_A); O(BANG_B); LIT(8); O(STORE_B);
LIT(P_DO); O(BANG_B); LIT(10); O(STORE_B);
LIT(OUT); O(BANG_A);
{
v4_cell again = v4_asm_label(&as);
O(FETCH_A); LIT(1); O(ADD); O(STORE_A);
v4_asm_branch(&as, V4_OP_JUMP, again);
}
CHECK(v4_asm_ok(&as) && v4_fabric_wire(&f, a, 0, b, 3), "a writer wired to a node that has not yet asked anything of a wire");
v4_node_error_attach(&ND(a)->n, OUT + 20);
v4_node_fault_attach(&ND(a)->n, 900);
(void)settle(200);
CHECK(ND(a)->n.asking && ND(a)->n.faults == 0, "the writer waits");
memset(&s, 0, sizeof s); s.words = one; s.count = 1;
CHECK(v4_fabric_wire_device(&f, b, 5, &giver), "the neighbour is given its word");
for (i = 0; i < 200 && ND(a)->n.asking; i++) (void)v4_fabric_step(&f);
CHECK(!ND(a)->n.asking && ND(a)->n.fault_kind == V4_FAULT_RAISED && ND(a)->n.fault_addr == 18, "it asks for room, and the writer is let go");
for (i = 0; i < 20; i++) (void)v4_fabric_step(&f);
CHECK(!v4_node_doing(&ND(b)->n) && ND(b)->n.mem[OUT] > 0, "though that node is not asleep: it goes on counting");
}
/* ---- asleep, awake, and removed, while looping; and a node born while others run ---- */
v4_fabric_init(&f, places, 4);
a = loaded(0); /* counts in OUT for ever */
LIT(OUT); O(BANG_A);
{
v4_cell again = v4_asm_label(&as);
O(FETCH_A); LIT(1); O(ADD); O(STORE_A);
v4_asm_branch(&as, V4_OP_JUMP, again);
}
b = loaded(1); /* and so does this one */
LIT(OUT); O(BANG_A);
{
v4_cell again = v4_asm_label(&as);
O(FETCH_A); LIT(1); O(ADD); O(STORE_A);
v4_asm_branch(&as, V4_OP_JUMP, again);
}
CHECK(v4_asm_ok(&as), "two nodes that never stop assemble");
for (i = 0; i < 100; i++) (void)v4_fabric_step(&f);
{
v4_cell ca = ND(a)->n.mem[OUT], cb = ND(b)->n.mem[OUT];
v4_uheat_t clock = ND(a)->es.anticlock;
CHECK(ca > 0 && ca == cb, "each has had as much time as the other: %ld and %ld", (long)ca, (long)cb);
v4_fabric_sleep(&f, a);
for (i = 0; i < 100; i++) (void)v4_fabric_step(&f);
CHECK(ND(a)->n.mem[OUT] == ca && ND(a)->es.anticlock == clock, "put to sleep in the middle of its loop, one executes nothing");
CHECK(ND(b)->n.mem[OUT] == 2 * cb, "and the other goes on as before: %ld", (long)ND(b)->n.mem[OUT]);
v4_fabric_wake(&f, a);
for (i = 0; i < 100; i++) (void)v4_fabric_step(&f);
CHECK(ND(a)->n.mem[OUT] == 2 * ca, "woken, it goes on from where it was: %ld", (long)ND(a)->n.mem[OUT]);
CHECK(v4_fabric_remove(&f, a) != 0 && v4_fabric_node_at(&f, a) == 0, "it is removed while it loops");
cb = ND(b)->n.mem[OUT];
for (i = 0; i < 100; i++) (void)v4_fabric_step(&f);
CHECK(ND(b)->n.mem[OUT] > cb, "and the other goes on");
c = (unsigned)v4_fabric_add(&f, &pool[3], PB);
CHECK(c == a && pool[3].n.p == ANY, "a node is born while the other runs, into the place that was freed");
for (i = 0; i < 10; i++) (void)v4_fabric_step(&f);
CHECK(pool[3].n.reading && pool[3].es.anticlock == 0, "and waits, empty, while the other runs");
}
/* ---- more room, while they run ---- */
v4_fabric_init(&f, places, 4);
a = loaded(0); LIT(PB + 0); O(BANG_B); LIT(5); O(STORE_B); wait_for_ever();
b = loaded(1); LIT(PB + 0); O(BANG_B); O(FETCH_B); LIT(OUT); O(BANG_A); O(STORE_A); wait_for_ever();
(void)loaded(2); wait_for_ever();
(void)loaded(3); wait_for_ever();
CHECK(v4_fabric_add(&f, &pool[4], PB) == -1, "a fabric with no room left takes no more nodes");
CHECK(v4_fabric_wire(&f, a, 0, b, 0), "two of its nodes are wired");
v4_fabric_grow(&f, more_places, NODES);
CHECK(v4_fabric_add(&f, &pool[4], PB) == 4, "given more room, it takes another");
(void)settle(500);
CHECK(ND(b)->n.mem[OUT] == 5, "and the nodes that were there are wired as they were");
/* ---- what cannot be wired ---- */
CHECK(!v4_fabric_wire(&f, a, V4_PORTS, b, 0) && !v4_fabric_wire(&f, a, 0, 7, 0) && !v4_fabric_wire(&f, a, 1, a, 1),
"a port that is not there, a node that is not there, a port to itself: refused");
CHECK(v4_fabric_wire(&f, a, 1, a, 2), "but one port of a node may be wired to another of the same node");
/* ---- a node looks at its neighbours before it writes (MESH.md 7a) ---- */
v4_fabric_init(&f, places, 4);
a = loaded(0); LIT(PB + 0); O(BANG_B); LIT(5); O(STORE_B); wait_for_ever(); /* writes to its port 0 */
c = loaded(1); LIT(PB + 1); O(BANG_B); O(FETCH_B); LIT(OUT); O(BANG_A); O(STORE_A); wait_for_ever(); /* reads its port 1 */
for (i = 0; i < 10; i++) (void)v4_fabric_step(&f);
CHECK(ND(a)->n.asking && ND(c)->n.reading, "one node is waiting to write and another to read");
b = loaded(2);
LIT(PB + (v4_cell)V4_PORTS + 2); O(BANG_B); O(FETCH_B); LIT(OUT); O(BANG_A); O(STORE_A);
LIT(PB + (v4_cell)V4_PORTS + 3); O(BANG_B); O(FETCH_B); LIT(OUT + 1); O(BANG_A); O(STORE_A);
wait_for_ever();
CHECK(v4_fabric_wire(&f, b, 2, a, 0) && v4_fabric_wire(&f, b, 4, c, 1), "a third is wired to both");
{
static stream none;
static v4_device sink;
sink.take = stream_take; sink.give = 0; sink.self = &none;
CHECK(v4_fabric_wire_device(&f, b, 6, &sink), "and to a device that takes what is written to it");
}
for (i = 0; i < 20; i++) (void)v4_fabric_step(&f);
CHECK(ND(b)->n.mem[OUT] == (1 << 2), "it sees which of its ports has a neighbour waiting to write to it: %ld", (long)ND(b)->n.mem[OUT]);
CHECK((ND(b)->n.mem[OUT + 1] & ((1 << V4_PORTS) - 1)) == ((1 << 4) | (1 << 6)), "and which have one waiting to read from it, or a device: %ld", (long)(ND(b)->n.mem[OUT + 1] & ((1 << V4_PORTS) - 1)));
CHECK((ND(b)->n.mem[OUT + 1] >> V4_PORTS) == ((1 << 2) | (1 << 4) | (1 << 6)), "and, in the bits above those, which of its ports have anything on them at all: %ld", (long)(ND(b)->n.mem[OUT + 1] >> V4_PORTS));
CHECK(ND(a)->n.asking && ND(a)->n.request == 5 && ND(c)->n.reading && ND(c)->n.mem[OUT] == 0, "looking takes nothing and gives nothing: both wait as they did");
CHECK(!ND(b)->n.stopped && ND(b)->n.reading, "and the one that looked did not wait to look");
/* ---- messages on the wire (MESH.md 7d): the engine only ---- */
{
static v4_wire_queue qpool[8];
v4_cell h[7], t[V4_WIRE_CELLS], snap[V4_NODE_WORDS];
v4_wire_queue copy;
v4_device nothing;
unsigned w, k;
memset(&nothing, 0, sizeof nothing);
/* a node puts a message on its wire 0 and a neighbour takes it from its wire 3 */
v4_fabric_init(&f, places, 4);
v4_fabric_queues(&f, qpool, 8);
a = loaded(0);
LIT(P_WIRE); O(BANG_B); LIT(0); O(STORE_B);
LIT(P_A); O(BANG_B); LIT(100); O(STORE_B);
LIT(P_B); O(BANG_B); LIT(110); O(STORE_B);
LIT(P_DO); O(BANG_B); LIT(5); O(STORE_B);
LIT(P_HOW); O(BANG_B); O(FETCH_B); LIT(OUT); O(BANG_A); O(STORE_A);
wait_for_ever();
b = loaded(1);
LIT(P_WIRE); O(BANG_B); LIT(3); O(STORE_B);
LIT(P_DO); O(BANG_B); LIT(8); O(STORE_B);
LIT(P_HAVE); O(BANG_B); O(FETCH_B); LIT(OUT + 1); O(BANG_A); O(STORE_A);
LIT(P_A); O(BANG_B); LIT(200); O(STORE_B);
LIT(P_DO); O(BANG_B); LIT(1); O(STORE_B); /* it looks, and then takes what it looked at */
LIT(P_B); O(BANG_B); LIT(220); O(STORE_B);
LIT(P_DO); O(BANG_B); LIT(2); O(STORE_B);
LIT(P_HOW); O(BANG_B); O(FETCH_B); LIT(OUT); O(BANG_A); O(STORE_A);
wait_for_ever();
CHECK(v4_asm_ok(&as), "a node that puts and a node that sleeps and takes assemble");
CHECK(v4_fabric_wire(&f, a, 0, b, 3), "wire 0 of the first is wired to wire 3 of the second");
ND(a)->n.mem[OUT] = ND(b)->n.mem[OUT] = -7;
put_msg(&ND(a)->n, 100, 110, 13, 5);
for (steps = 0; steps < 300 && ND(a)->n.do_op != 5; steps++) (void)v4_fabric_step(&f);
CHECK(ND(a)->n.do_op == 5 && !ND(a)->n.doing && ND(a)->n.how == 0 && f.place[a].wire[0].tx->used == 7 + 4,
"the put was done in the engine step that asked for it: the message is on the wire and the node is not blocked");
steps = settle(2000);
CHECK(steps < 2000 && ND(b)->n.do_op == 2 && ND(a)->n.mem[OUT] == 0 && ND(b)->n.mem[OUT] == 0, "how it went is 0 on both: %ld and %ld", (long)ND(a)->n.mem[OUT], (long)ND(b)->n.mem[OUT]);
CHECK(msg_is(&ND(b)->n, 200, 220, 13, 5), "the seven words and the text taken are what was put");
CHECK(ND(b)->n.mem[OUT + 1] == (1 << 3), "and while it waited it saw the message on its wire 3: %ld", (long)ND(b)->n.mem[OUT + 1]);
CHECK(f.place[a].wire[0].tx->used == 0 && f.place[b].wire[3].rx->used == 0, "the wire is empty again");
/* a put with nothing on the port is 2, to a wire with no room is 1; neither changes the wire */
v4_fabric_init(&f, places, 4);
v4_fabric_queues(&f, qpool, 8);
a = (unsigned)v4_fabric_add(&f, &pool[0], PB);
b = (unsigned)v4_fabric_add(&f, &pool[1], PB);
CHECK(v4_fabric_wire(&f, a, 0, b, 3), "a wire for the refusals");
put_msg(&ND(a)->n, 100, 110, 20, 3);
CHECK(ask(a, 5, 100, 110, 5) == 2, "a put to a port with nothing on it answers 2");
CHECK(ask(a, (v4_cell)V4_PORTS, 100, 110, 5) == 2 && ask(a, -1, 100, 110, 5) == 2, "and so does one to a port that is not there");
fill(f.place[a].wire[0].tx, 0);
memcpy(&copy, f.place[a].wire[0].tx, sizeof copy);
CHECK(ask(a, 0, 100, 110, 5) == 1, "a put to a wire with no room answers 1");
CHECK(memcmp(&copy, f.place[a].wire[0].tx, sizeof copy) == 0, "and the wire is exactly as it was");
v4_wire_reset(f.place[a].wire[0].tx);
fill(f.place[a].wire[0].tx, 200);
put_msg(&ND(a)->n, 100, 110, 4 * (201 - 7), 3);
memcpy(&copy, f.place[a].wire[0].tx, sizeof copy);
CHECK(ask(a, 0, 100, 110, 5) == 1 && memcmp(&copy, f.place[a].wire[0].tx, sizeof copy) == 0, "a message of 201 cells to a wire with 200 free: 1, and nothing put");
put_msg(&ND(a)->n, 100, 110, 4 * (200 - 7), 3);
CHECK(ask(a, 0, 100, 110, 5) == 0 && f.place[a].wire[0].tx->used == V4_WIRE_CELLS, "one of 200 cells goes");
put_msg(&ND(a)->n, 100, 110, 5000, 3);
v4_wire_reset(f.place[a].wire[0].tx);
CHECK(ask(a, 0, 100, 110, 5) == 4 && f.place[a].wire[0].tx->used == 0, "a message whose length is not 0 to 1,024 answers 4");
put_msg(&ND(a)->n, 100, 110, -1, 3);
CHECK(ask(a, 0, 100, 110, 5) == 4 && f.place[a].wire[0].tx->used == 0, "so does a length of -1");
CHECK(ask(a, 0, 100, 110, 99) == 4 && ask(a, 0, 100, 110, 0) == 4 && ask(a, 0, 100, 110, 11) == 4, "and an operation that is not one of the ten");
/* addresses off the end of memory, or inside the ports: 4, and nothing copied */
{
v4_cell bad[7];
v4_wire_queue before_rx, before_tx;
unsigned op, i;
const unsigned ops[3] = { 1, 2, 5 };
bad[0] = (v4_cell)V4_NODE_WORDS - 3; /* the seven words run off the end */
bad[1] = (v4_cell)V4_NODE_WORDS;
bad[2] = -1;
bad[3] = PB + 2; /* inside the ports */
bad[4] = PB - 3; /* runs into them */
bad[5] = PB + (v4_cell)V4_PORT_HAVE; /* the last of them */
bad[6] = PB + (v4_cell)V4_PORT_HAVE - 6; /* the seven end on it */
for (op = 0; op < 3; op++)
for (k = 0; k < 7; k++) {
unsigned faults = ND(a)->n.faults;
v4_wire_reset(f.place[a].wire[0].tx);
v4_wire_reset(f.place[a].wire[0].rx);
put_msg(&ND(a)->n, 100, 110, 30, 6); /* a message waiting for look and take to find */
for (i = 0; i < 7; i++) t[i] = ND(a)->n.mem[100 + i];
for (i = 0; i < 8; i++) t[8 + i] = ND(a)->n.mem[110 + i];
CHECK(v4_wire_put(f.place[a].wire[0].rx, t, t + 8) == V4_WIRE_DONE, "(a message waits)");
f.place[a].wire[0].rx->looked = 1; /* and has been looked at */
memcpy(snap, ND(a)->n.mem, sizeof snap);
memcpy(&before_rx, f.place[a].wire[0].rx, sizeof before_rx);
memcpy(&before_tx, f.place[a].wire[0].tx, sizeof before_tx);
CHECK(ask(a, 0, bad[k], 110, ops[op]) == 4, "op %u with its seven words at %ld: 4", ops[op], (long)bad[k]);
CHECK(memcmp(snap, ND(a)->n.mem, sizeof snap) == 0 && ND(a)->n.faults == faults, "op %u at %ld: nothing was copied, and the node did not fault", ops[op], (long)bad[k]);
CHECK(memcmp(&before_rx, f.place[a].wire[0].rx, sizeof before_rx) == 0 && memcmp(&before_tx, f.place[a].wire[0].tx, sizeof before_tx) == 0,
"op %u at %ld: neither wire has changed", ops[op], (long)bad[k]);
}
/* a good header and a text address off the end or inside the ports: 4, and the header is not copied either */
bad[0] = (v4_cell)V4_NODE_WORDS - 10;
bad[1] = PB + 3;
bad[2] = -5;
for (k = 0; k < 3; k++) {
v4_wire_reset(f.place[a].wire[0].tx);
v4_wire_reset(f.place[a].wire[0].rx);
put_msg(&ND(a)->n, 100, 110, 30, 6);
for (i = 0; i < 7; i++) t[i] = ND(a)->n.mem[100 + i];
for (i = 0; i < 8; i++) t[8 + i] = ND(a)->n.mem[110 + i];
(void)v4_wire_put(f.place[a].wire[0].rx, t, t + 8);
f.place[a].wire[0].rx->looked = 1;
memcpy(snap, ND(a)->n.mem, sizeof snap);
memcpy(&before_rx, f.place[a].wire[0].rx, sizeof before_rx);
CHECK(ask(a, 0, 300, bad[k], 2) == 4 && memcmp(snap, ND(a)->n.mem, sizeof snap) == 0 && memcmp(&before_rx, f.place[a].wire[0].rx, sizeof before_rx) == 0,
"take with its text at %ld: 4, nothing copied (not the seven words either), the message still there", (long)bad[k]);
CHECK(ask(a, 0, 100, bad[k], 5) == 4 && f.place[a].wire[0].tx->used == 0, "put with its text at %ld: 4, nothing put", (long)bad[k]);
}
/* a message with no text needs no address for it */
put_msg(&ND(a)->n, 100, 110, 0, 6);
v4_wire_reset(f.place[a].wire[0].tx);
CHECK(ask(a, 0, 100, -1, 5) == 0 && f.place[a].wire[0].tx->used == 7, "a message of no text is put with any address for its text");
v4_wire_reset(f.place[a].wire[0].tx);
}
/* the wire, look, take, first, next, drop and the mark */
v4_fabric_init(&f, places, 4);
v4_fabric_queues(&f, qpool, 8);
a = (unsigned)v4_fabric_add(&f, &pool[0], PB);
b = (unsigned)v4_fabric_add(&f, &pool[1], PB);
CHECK(v4_fabric_wire(&f, a, 0, b, 3), "a wire for looking");
CHECK(ask(a, 0, 200, 220, 1) == 3 && ask(a, 0, 200, 220, 2) == 3 && ask(a, 0, 200, 220, 4) == 3 && ask(a, 0, 200, 220, 3) == 3 && ask(a, 5, 200, 220, 1) == 2,
"look, take, drop and move on a wire with no message answer 3, on a port with nothing 2");
put_msg(&ND(b)->n, 100, 110, 9, 1); CHECK(ask(b, 3, 100, 110, 5) == 0, "three messages are put");
put_msg(&ND(b)->n, 100, 110, 21, 2); CHECK(ask(b, 3, 100, 110, 5) == 0, "the second");
put_msg(&ND(b)->n, 100, 110, 4, 3); CHECK(ask(b, 3, 100, 110, 5) == 0, "and the third");
CHECK(ask(a, 0, 200, 220, 6) == 0 && ask(a, 0, 200, 220, 1) == 0 && ND(a)->n.mem[200] == 100 && ND(a)->n.mem[206] == 9, "first, and look: the first message's seven words");
CHECK(f.place[a].wire[0].rx->used == 10 + 13 + 8, "look took nothing");
CHECK(ask(a, 0, 200, 220, 7) == 0 && ask(a, 0, 200, 220, 1) == 0 && ND(a)->n.mem[200] == 200, "next: the second");
CHECK(ask(a, 0, 200, 220, 2) == 0 && msg_is(&ND(a)->n, 200, 220, 21, 2), "take of the second");
CHECK(ask(a, 0, 200, 220, 1) == 0 && ND(a)->n.mem[200] == 300, "the mark is at the third");
CHECK(ask(a, 0, 200, 220, 6) == 0 && seen(a, 0, 200, 220, 4) == 0 && ask(a, 0, 200, 220, 1) == 0 && ND(a)->n.mem[200] == 300, "first and drop: the first is gone, the third is left");
CHECK(ask(a, 0, 200, 220, 7) == 3 && ask(a, 0, 200, 220, 1) == 3, "next at the end answers 3 and the mark is at no message");
CHECK(ask(a, 0, 200, 220, 6) == 0 && seen(a, 0, 200, 220, 2) == 0 && msg_is(&ND(a)->n, 200, 220, 4, 3) && f.place[a].wire[0].rx->used == 0, "and the third is taken");
/* "which wires have a message": a bit to a wire; fetching it clears arrived */
v4_fabric_init(&f, places, 4);
v4_fabric_queues(&f, qpool, 8);
a = (unsigned)v4_fabric_add(&f, &pool[0], PB);
b = (unsigned)v4_fabric_add(&f, &pool[1], PB);
c = (unsigned)v4_fabric_add(&f, &pool[2], PB);
CHECK(v4_fabric_wire(&f, a, 2, b, 0) && v4_fabric_wire(&f, a, 6, c, 1), "a node with two neighbours");
(void)v4_fabric_step(&f);
CHECK(v4_node_fetch(&ND(a)->n, P_HAVE) == 0, "with nothing on any wire, no bit");
put_msg(&ND(b)->n, 100, 110, 3, 1); CHECK(ask(b, 0, 100, 110, 5) == 0, "one neighbour puts");
put_msg(&ND(c)->n, 100, 110, 3, 1); CHECK(ask(c, 1, 100, 110, 5) == 0, "the other puts");
CHECK(f.place[a].wire[2].rx->arrived && f.place[a].wire[6].rx->arrived, "a message has arrived on each");
(void)v4_fabric_step(&f);
CHECK(v4_node_fetch(&ND(a)->n, P_HAVE) == ((1 << 2) | (1 << 6)), "the wires with a message have each their bit");
(void)v4_fabric_step(&f);
CHECK(!f.place[a].wire[2].rx->arrived && !f.place[a].wire[6].rx->arrived, "and fetching it clears what has arrived");
CHECK(v4_node_fetch(&ND(a)->n, P_HAVE) == ((1 << 2) | (1 << 6)), "though the messages are still there");
/* sleep: blocked, and the engine's clock does not move; a put to one of its wires wakes it */
v4_fabric_init(&f, places, 4);
v4_fabric_queues(&f, qpool, 8);
a = loaded(0);
LIT(P_DO); O(BANG_B); LIT(8); O(STORE_B);
LIT(1); LIT(OUT); O(BANG_A); O(STORE_A);
wait_for_ever();
b = (unsigned)v4_fabric_add(&f, &pool[1], PB);
CHECK(v4_asm_ok(&as) && v4_fabric_wire(&f, a, 4, b, 2), "a node that sleeps and then leaves its mark");
(void)settle(100);
CHECK(ND(a)->n.doing && ND(a)->n.do_op == 8 && ND(a)->n.mem[OUT] == 0, "it is blocked at its sleep");
{
v4_uheat_t clock = ND(a)->es.anticlock;
unsigned moved = 0;
for (i = 0; i < 500; i++) moved += v4_fabric_step(&f);
CHECK(moved == 0 && ND(a)->es.anticlock == clock && ND(a)->n.doing && ND(a)->n.mem[OUT] == 0, "for 500 steps nothing happens and its clock does not move");
put_msg(&ND(b)->n, 100, 110, 3, 1);
CHECK(ask(b, 2, 100, 110, 5) == 0, "a put to one of its wires");
(void)settle(100);
CHECK(!ND(a)->n.doing && ND(a)->n.how == 0 && ND(a)->n.mem[OUT] == 1, "wakes it, and it goes on");
}
/* a node that looks, leaves the message there, fetches "which wires" and sleeps again stays asleep until a second comes */
v4_fabric_init(&f, places, 4);
v4_fabric_queues(&f, qpool, 8);
a = (unsigned)v4_fabric_add(&f, &pool[0], PB);
b = (unsigned)v4_fabric_add(&f, &pool[1], PB);
CHECK(v4_fabric_wire(&f, a, 1, b, 4), "two nodes");
put_msg(&ND(b)->n, 100, 110, 3, 1);
CHECK(ask(b, 4, 100, 110, 5) == 0, "a message comes");
CHECK(ask(a, 0, 200, 220, 8) == 0, "a sleep with a message come is not a sleep");
CHECK(ask(a, 1, 200, 220, 6) == 0 && ask(a, 1, 200, 220, 1) == 0 && ND(a)->n.mem[200] == 100, "it looks, and leaves it there");
(void)v4_node_fetch(&ND(a)->n, P_HAVE);
v4_node_store(&ND(a)->n, P_DO, 8);
for (i = 0; i < 500; i++) (void)v4_fabric_step(&f);
CHECK(ND(a)->n.doing && f.place[a].wire[1].rx->used == 8, "it sleeps again, with the message still on the wire, and stays asleep for 500 steps");
put_msg(&ND(b)->n, 100, 110, 3, 2);
CHECK(ask(b, 4, 100, 110, 5) == 0, "a second message comes");
(void)v4_fabric_step(&f);
CHECK(!ND(a)->n.doing && ND(a)->n.how == 0, "and wakes it");
/* a message put in the same engine step as the node's fetch of "which wires" is not lost: the next sleep does not block */
v4_fabric_init(&f, places, 4);
v4_fabric_queues(&f, qpool, 8);
a = (unsigned)v4_fabric_add(&f, &pool[0], PB);
b = (unsigned)v4_fabric_add(&f, &pool[1], PB);
CHECK(v4_fabric_wire(&f, a, 0, b, 3), "two nodes for the same-step put");
(void)v4_node_fetch(&ND(a)->n, P_HAVE); /* the fetch is seen in the step's execute phase ... */
put_msg(&ND(b)->n, 100, 110, 3, 1);
v4_node_store(&ND(b)->n, P_WIRE, 3);
v4_node_store(&ND(b)->n, P_A, 100);
v4_node_store(&ND(b)->n, P_B, 110);
v4_node_store(&ND(b)->n, P_DO, 5); /* ... and the put is done in the same step's operations */
(void)v4_fabric_step(&f);
CHECK(!ND(b)->n.doing && ND(b)->n.how == 0 && f.place[a].wire[0].rx->used == 8 && f.place[a].wire[0].rx->arrived != 0,
"the put and the fetch were in one step: the message is on the wire and counts as arrived");
CHECK(ask(a, 0, 0, 0, 8) == 0, "so the sleep that follows does not block");
/* sleep for room */
v4_fabric_init(&f, places, 4);
v4_fabric_queues(&f, qpool, 8);
a = (unsigned)v4_fabric_add(&f, &pool[0], PB);
b = (unsigned)v4_fabric_add(&f, &pool[1], PB);
CHECK(v4_fabric_wire(&f, a, 0, b, 3), "a writer and a reader");
fill(f.place[a].wire[0].tx, 100);
CHECK(ask(a, 0, 50, 0, 10) == 0 && ask(a, 0, 100, 0, 10) == 0 && ask(a, 0, 101, 0, 10) == 1 && ask(a, 0, 5000, 0, 10) == 1,
"room, with 100 cells free: for 50 and for 100, not for 101");
CHECK(ask(a, 5, 10, 0, 10) == 2 && ask(a, 0, -1, 0, 10) == 4, "room on a port with nothing is 2, for -1 cells 4");
CHECK(f.place[a].wire[0].tx->used == V4_WIRE_CELLS - 100 && !ND(a)->n.doing, "and it changes nothing");
CHECK(ask(a, 0, V4_WIRE_CELLS + 1, 0, 9) == 1 && ask(a, 0, 100000, 0, 9) == 1 && ask(a, 0, -1, 0, 9) == 1 && !ND(a)->n.doing,
"sleep for more room than the wire can ever have answers 1 at once, not blocked");
(void)v4_node_fetch(&ND(a)->n, P_HAVE);
(void)v4_fabric_step(&f);
CHECK(ask(a, 0, 200, 0, 9) == -99 && ND(a)->n.doing, "sleep for 200 cells with 100 free: blocked");
for (i = 0; i < 100; i++) (void)v4_fabric_step(&f);
CHECK(ND(a)->n.doing, "and still blocked");
v4_wire_first(f.place[b].wire[3].rx);
CHECK(seen(b, 3, 400, 420, 4) == 0, "the reader drops the front message (which is 263 cells)");
while (ND(a)->n.doing && f.place[b].wire[3].rx->used > 0) {
(void)seen(b, 3, 400, 420, 4);
}
CHECK(!ND(a)->n.doing && ND(a)->n.how == 0 && f.place[a].wire[0].tx->used + 200 <= V4_WIRE_CELLS, "woken, with how it went 0");
/* a small message in front: dropping it frees little, and the sleeper wants more than that leaves */
v4_wire_reset(f.place[a].wire[0].tx);
v4_wire_reset(f.place[b].wire[3].rx);
{
v4_cell hh[7] = { 1, 2, 3, 4, 5, 6, 12 };
static v4_cell tt[V4_WIRE_CELLS];
(void)v4_wire_put(f.place[a].wire[0].tx, hh, tt); /* 10 cells */
hh[6] = 1024;
for (i = 0; i < 3; i++) (void)v4_wire_put(f.place[a].wire[0].tx, hh, tt);
hh[6] = 4 * (35 - 7);
(void)v4_wire_put(f.place[a].wire[0].tx, hh, tt); /* 35 cells: 190 are free */
}
CHECK(V4_WIRE_CELLS - f.place[a].wire[0].tx->used == 190, "a wire with 190 free and a small message in front");
(void)v4_node_fetch(&ND(a)->n, P_HAVE);
(void)v4_fabric_step(&f);
CHECK(ask(a, 0, 250, 0, 9) == -99, "asleep for 250");
v4_wire_first(f.place[b].wire[3].rx);
CHECK(seen(b, 3, 400, 420, 4) == 0 && V4_WIRE_CELLS - f.place[a].wire[0].tx->used == 200, "the reader drops the small one: 200 are free");
CHECK(!ND(a)->n.doing && ND(a)->n.how == 1, "the sleeper, which wants 250, is woken, and told 1: a wire of its own has gained room, and not enough");
CHECK(ask(a, 0, 250, 0, 9) == -99, "it asks again, and sleeps");
for (i = 0; i < 50; i++) (void)v4_fabric_step(&f);
CHECK(ND(a)->n.doing, "and sleeps on while nothing changes");
CHECK(seen(b, 3, 400, 420, 4) == 0 && !ND(a)->n.doing && ND(a)->n.how == 0, "the next take gives it the room: 0");
/* woken by a message coming instead */
v4_wire_reset(f.place[a].wire[0].tx);
fill(f.place[a].wire[0].tx, 100);
(void)v4_node_fetch(&ND(a)->n, P_HAVE);
(void)v4_fabric_step(&f);
CHECK(ask(a, 0, 200, 0, 9) == -99, "asleep for room again");
put_msg(&ND(b)->n, 100, 110, 3, 1);
CHECK(ask(b, 3, 100, 110, 5) == 0, "a message comes on the other wire");
(void)v4_fabric_step(&f);
CHECK(!ND(a)->n.doing && ND(a)->n.how == 1 && f.place[a].wire[0].tx->used + 200 > V4_WIRE_CELLS, "it wakes, and how it went says 1: no room, but a message");
/* A node asleep for room on one wire is woken when another of its wires is cut, or the node on it
* removed: it may be keeping something to put there, and must find out that it cannot (found by the
* final review of step 6e: before, it slept on until something else happened). */
v4_fabric_init(&f, more_places, 8);
v4_fabric_queues(&f, qpool, 8);
a = (unsigned)v4_fabric_add(&f, &pool[0], PB);
b = (unsigned)v4_fabric_add(&f, &pool[1], PB);
c = (unsigned)v4_fabric_add(&f, &pool[2], PB);
CHECK(v4_fabric_wire(&f, a, 0, b, 3) && v4_fabric_wire(&f, a, 1, c, 2), "a node with two wires");
fill(f.place[a].wire[0].tx, 0);
fill(f.place[a].wire[1].tx, 0);
(void)v4_node_fetch(&ND(a)->n, P_HAVE);
(void)v4_fabric_step(&f);
CHECK(ask(a, 0, 100, 0, 9) == -99, "both full: asleep for room on wire 0");
CHECK(v4_fabric_unwire(&f, a, 1), "its wire 1 is cut");
(void)v4_fabric_step(&f);
CHECK(!ND(a)->n.doing && ND(a)->n.how == 1, "it wakes, told 1: the wire it named has no room, but something has changed");
CHECK(ask(a, 1, 100, 0, 10) == 2, "it asks, and finds no one on port 1");
CHECK(ask(a, 0, 100, 0, 9) == -99, "asleep for room on wire 0 again");
for (i = 0; i < 100; i++) (void)v4_fabric_step(&f);
CHECK(ND(a)->n.doing, "and it is not woken twice for the one cut");
CHECK(v4_fabric_wire(&f, a, 1, c, 2), "the wire is made again");
fill(f.place[a].wire[1].tx, 0);
for (i = 0; i < 100; i++) (void)v4_fabric_step(&f);
CHECK(ND(a)->n.doing, "a wire being made does not wake it");
CHECK(v4_fabric_remove(&f, c) == &pool[2], "the node on wire 1 is removed");
(void)v4_fabric_step(&f);
CHECK(!ND(a)->n.doing && ND(a)->n.how == 1, "it wakes, told 1");
ND(a)->n.doing = 0;
v4_node_store(&ND(a)->n, P_DO, 8);
CHECK(v4_fabric_wire(&f, a, 1, b, 4) && v4_fabric_unwire(&f, a, 1), "asleep for a message (8), with a wire made and cut");
for (i = 0; i < 100; i++) (void)v4_fabric_step(&f);
CHECK(ND(a)->n.doing, "a node asleep for a message is not woken by a cut: it keeps nothing");
/* SLEEP FOR ROOM IS WOKEN BY ROOM ON ANY OF THE NODE'S WIRES (MESH.md 7d.3): a node may be waiting to
* put on more than one, and names only one */
v4_fabric_init(&f, more_places, 8);
v4_fabric_queues(&f, qpool, 8);
a = (unsigned)v4_fabric_add(&f, &pool[0], PB);
b = (unsigned)v4_fabric_add(&f, &pool[1], PB);
c = (unsigned)v4_fabric_add(&f, &pool[2], PB);
CHECK(v4_fabric_wire(&f, a, 0, b, 3) && v4_fabric_wire(&f, a, 1, c, 2), "a node with two wires");
fill(f.place[a].wire[0].tx, 0);
fill(f.place[a].wire[1].tx, 0);
(void)v4_node_fetch(&ND(a)->n, P_HAVE);
(void)v4_fabric_step(&f);
CHECK(ask(a, 0, 100, 0, 9) == -99, "both full: asleep for room on wire 0");
for (i = 0; i < 100; i++) (void)v4_fabric_step(&f);
CHECK(ND(a)->n.doing, "and it sleeps on while nothing changes");
v4_wire_first(f.place[c].wire[2].rx);
CHECK(seen(c, 2, 400, 420, 4) == 0, "the reader of wire 1 drops a message");
CHECK(!ND(a)->n.doing && ND(a)->n.how == 1 && f.place[a].wire[0].tx->used == V4_WIRE_CELLS, "it wakes, told 1: the wire it named has no room, but something has changed");
CHECK(ask(a, 0, 100, 0, 10) == 1 && ask(a, 1, 100, 0, 10) == 0, "it asks which: wire 1 has the room");
CHECK(ask(a, 0, 100, 0, 9) == -99, "asleep for room on wire 0 again: that room on wire 1 was seen");
for (i = 0; i < 100; i++) (void)v4_fabric_step(&f);
CHECK(ND(a)->n.doing, "and it is not woken twice for it");
/* the room comes in the very step the node goes to sleep: the reader's take is done before the sleep is looked at */
(void)ask(c, 2, 500, 0, 1); v4_node_store(&ND(c)->n, P_WIRE, 2); v4_node_store(&ND(c)->n, P_DO, 4);
ND(a)->n.doing = 0;
v4_node_store(&ND(a)->n, P_WIRE, 0); v4_node_store(&ND(a)->n, P_A, 100); v4_node_store(&ND(a)->n, P_DO, 9);
(void)v4_fabric_step(&f);
CHECK(!ND(c)->n.doing && !ND(a)->n.doing && ND(a)->n.how == 1, "room that comes in the step the node goes to sleep wakes it: no wake-up is lost");
/* a device's take is a take */
{
static const v4_device dev = { 0, 0, 0 };
v4_cell hh[7], tt[V4_WIRE_CELLS / 4];
CHECK(v4_fabric_wire_device(&f, a, 4, &dev), "a device on a third wire");
fill(f.place[a].wire[4].tx, 0);
CHECK(ask(a, 0, 100, 0, 9) == -99, "asleep for room on wire 0");
CHECK(v4_fabric_device_take(&f, a, 4, hh, tt, V4_WIRE_CELLS / 4) == V4_WIRE_DONE, "the device takes a message between steps");
(void)v4_fabric_step(&f);
CHECK(!ND(a)->n.doing && ND(a)->n.how == 1, "and that wakes it");
}
/* a plain sleep is not woken by room */
(void)v4_node_fetch(&ND(a)->n, P_HAVE);
(void)v4_fabric_step(&f);
CHECK(ask(a, 0, 0, 0, 8) == -99, "asleep, for a message");
CHECK(seen(c, 2, 400, 420, 4) == 0 && ND(a)->n.doing, "room on a wire does not wake a node that sleeps for a message only");
/* TAKE, MOVE AND DROP ARE ABOUT THE MESSAGE LAST LOOKED AT (MESH.md 7d.3). A node looks, decides, and
* then takes: if the wire was cut and made again in between, and another message put, what is at the
* mark is not what it looked at. Then each answers 3, no such message, and nothing is written or moved. */
v4_fabric_init(&f, more_places, 8);
v4_fabric_queues(&f, qpool, 8);
a = (unsigned)v4_fabric_add(&f, &pool[0], PB);
b = (unsigned)v4_fabric_add(&f, &pool[1], PB);
c = (unsigned)v4_fabric_add(&f, &pool[2], PB);
CHECK(v4_fabric_wire(&f, a, 0, b, 3) && v4_fabric_wire(&f, a, 1, c, 2), "a node between two");
put_msg(&ND(b)->n, 100, 110, 4, 1);
CHECK(ask(b, 3, 100, 110, 5) == 0 && ask(a, 0, 0, 0, 6) == 0, "a message of four characters comes, and the mark is at it");
memcpy(snap, ND(a)->n.mem, sizeof snap);
CHECK(ask(a, 0, 200, 220, 2) == 3 && ask(a, 0, 1, 0, 3) == 3 && ask(a, 0, 0, 0, 4) == 3 && f.place[a].wire[0].rx->used == 8 && memcmp(snap, ND(a)->n.mem, sizeof snap) == 0,
"not looked at: take, move and drop each answer 3, and nothing is written or taken");
CHECK(ask(a, 0, 200, 220, 1) == 0 && ND(a)->n.mem[206] == 4, "it is looked at: four characters");
/* the wire is cut and made again, and a message of 1,024 characters put where the short one was */
CHECK(v4_fabric_unwire(&f, a, 0) && v4_fabric_wire(&f, a, 0, b, 3), "the wire is cut, and made again");
put_msg(&ND(b)->n, 100, 110, 1024, 2);
CHECK(ask(b, 3, 100, 110, 5) == 0 && f.place[a].wire[0].rx->used == 263, "and a message of 1,024 characters is put on it");
memcpy(snap, ND(a)->n.mem, sizeof snap);
CHECK(ask(a, 0, 200, 220, 2) == 3 && memcmp(snap, ND(a)->n.mem, sizeof snap) == 0 && f.place[a].wire[0].rx->used == 263,
"a take answers 3: it is not the message that was looked at. Nothing is written: not one cell, where 256 would have been");
CHECK(ask(a, 0, 1, 0, 3) == 3 && ask(a, 0, 0, 0, 4) == 3 && f.place[a].wire[0].rx->used == 263 && f.place[a].wire[1].tx->used == 0, "nor is it moved, or dropped");
CHECK(ask(a, 0, 200, 220, 1) == 0 && ND(a)->n.mem[206] == 1024 && ask(a, 0, 200, 220, 2) == 0 && msg_is(&ND(a)->n, 200, 220, 1024, 2), "looked at, it is taken");
/* a look is good for one take, move or drop, and not after the mark has been moved */
put_msg(&ND(b)->n, 100, 110, 4, 3); CHECK(ask(b, 3, 100, 110, 5) == 0, "another comes");
put_msg(&ND(b)->n, 100, 110, 8, 4); CHECK(ask(b, 3, 100, 110, 5) == 0, "and another behind it");
CHECK(ask(a, 0, 200, 220, 1) == 0 && ask(a, 0, 0, 0, 7) == 0 && ask(a, 0, 200, 220, 2) == 3, "looked at, then the mark moved on with next: a take answers 3");
CHECK(ask(a, 0, 0, 0, 6) == 0 && ask(a, 0, 200, 220, 1) == 0 && ask(a, 0, 0, 0, 6) == 0 && ask(a, 0, 0, 0, 4) == 3, "looked at, then first: a drop answers 3");
CHECK(ask(a, 0, 200, 220, 1) == 0 && ask(a, 0, 0, 0, 4) == 0 && ask(a, 0, 0, 0, 4) == 3 && f.place[a].wire[0].rx->used == 9, "looked at and dropped; the one that followed has not been looked at, and is not dropped");
CHECK(ask(a, 0, 200, 220, 1) == 0 && ND(a)->n.mem[206] == 8 && ask(a, 0, 1, 0, 3) == 0 && f.place[a].wire[1].tx->used == 9, "looked at, it is moved");
/* a message put behind the one looked at does not undo the look */
put_msg(&ND(b)->n, 100, 110, 4, 5); CHECK(ask(b, 3, 100, 110, 5) == 0 && ask(a, 0, 200, 220, 1) == 0, "one comes and is looked at");
put_msg(&ND(b)->n, 100, 110, 4, 6); CHECK(ask(b, 3, 100, 110, 5) == 0 && ask(a, 0, 200, 220, 2) == 0 && msg_is(&ND(a)->n, 200, 220, 4, 5), "another comes behind it: the first is still the one looked at, and is taken");
/* THE KERNEL ON A PORT has no queue: it is asked by words, and takes no messages */
{
static stream ks;
static v4_device kern;
unsigned free_before = f.queue_free;
ks.took_n = 0; kern.take = stream_take; kern.give = 0; kern.self = &ks;
CHECK(v4_fabric_wire_kernel(&f, a, 5, &kern) && f.queue_free == free_before && !f.place[a].wire[5].rx && !f.place[a].wire[5].tx, "a kernel is wired to a port, and is given no queues");
put_msg(&ND(a)->n, 100, 110, 4, 7);
CHECK(ask(a, 5, 100, 110, 5) == 2 && ask(a, 5, 8, 0, 10) == 2 && ask(a, 5, 8, 0, 9) == 2 && ask(a, 5, 200, 220, 1) == 2 && ask(a, 5, 0, 0, 6) == 2 && !ND(a)->n.doing,
"a message put to it, room asked of it, a sleep for room on it, a look: each answers 2 at once -- no one takes messages there");
v4_node_store(&ND(a)->n, PB + 5, 77);
(void)v4_fabric_step(&f);
CHECK(!ND(a)->n.asking && ks.took_n == 1 && ks.took[0] == 77, "a word written to it is handed over as before");
CHECK(v4_fabric_unwire(&f, a, 5) && f.queue_free == free_before, "and cutting it gives back no queue that it never had");
}
/* move from one of a node's wires to another */
v4_fabric_init(&f, more_places, 8);
v4_fabric_queues(&f, qpool, 8);
a = (unsigned)v4_fabric_add(&f, &pool[0], PB);
b = (unsigned)v4_fabric_add(&f, &pool[1], PB);
c = (unsigned)v4_fabric_add(&f, &pool[2], PB);
CHECK(v4_fabric_wire(&f, a, 0, b, 3) && v4_fabric_wire(&f, a, 1, c, 2), "a node between two");
put_msg(&ND(b)->n, 100, 110, 21, 4);
ND(b)->n.mem[103] = 9;
CHECK(ask(b, 3, 100, 110, 5) == 0 && ask(a, 0, 0, 0, 6) == 0, "a message comes on wire 0");
CHECK(seen(a, 0, 1, 0, 3) == 0, "and is moved to wire 1");
CHECK(f.place[a].wire[0].rx->used == 0 && f.place[c].wire[2].rx->used == 7 + 6, "it is off the one and on the other");
CHECK(ask(c, 2, 200, 220, 6) == 0 && seen(c, 2, 200, 220, 2) == 0 && ND(c)->n.mem[203] == 8 && msg_is_but(&ND(c)->n, 200, 220, 21, 4, 3),
"and arrives whole, its fourth word one fewer");
/* the other full: 1, and nothing moved */
put_msg(&ND(b)->n, 100, 110, 21, 4);
CHECK(ask(b, 3, 100, 110, 5) == 0 && ask(a, 0, 0, 0, 6) == 0, "another comes");
fill(f.place[a].wire[1].tx, 0);
(void)ask(a, 0, 500, 0, 1);
memcpy(&copy, f.place[a].wire[0].rx, sizeof copy);
{
v4_wire_queue copy2;
memcpy(&copy2, f.place[a].wire[1].tx, sizeof copy2);
CHECK(ask(a, 0, 1, 0, 3) == 1, "looked at, and moved to a wire with no room: 1");
CHECK(memcmp(&copy, f.place[a].wire[0].rx, sizeof copy) == 0 && memcmp(&copy2, f.place[a].wire[1].tx, sizeof copy2) == 0, "and neither wire has changed");
}
CHECK(ask(a, 0, 5, 0, 3) == 2 && ask(a, 0, -1, 0, 3) == 2 && ask(a, 5, 1, 0, 3) == 2, "move to a port with nothing, or from one: 2");
/* a node removed with messages waiting both ways on two wires */
v4_fabric_init(&f, more_places, 8);
v4_fabric_queues(&f, qpool, 4);
a = (unsigned)v4_fabric_add(&f, &pool[0], PB);
b = (unsigned)v4_fabric_add(&f, &pool[1], PB);
c = (unsigned)v4_fabric_add(&f, &pool[2], PB);
CHECK(v4_fabric_wire(&f, a, 0, b, 3) && v4_fabric_wire(&f, c, 1, b, 5), "a node in the middle with two neighbours");
put_msg(&ND(a)->n, 100, 110, 21, 1);
CHECK(ask(a, 0, 100, 110, 5) == 0 && ask(b, 3, 100, 110, 5) == 0, "a message each way on the first wire");
CHECK(ask(c, 1, 100, 110, 5) == 0 && ask(b, 5, 100, 110, 5) == 0, "and on the second");
(void)ask(a, 0, 0, 0, 7);
(void)ask(b, 3, 0, 0, 7);
{
v4_wire_queue *q0 = f.place[a].wire[0].tx, *q1 = f.place[a].wire[0].rx, *q2 = f.place[c].wire[1].tx, *q3 = f.place[c].wire[1].rx;
fill(q0, 0);
fill(q2, 0);
(void)v4_node_fetch(&ND(a)->n, P_HAVE);
(void)v4_node_fetch(&ND(c)->n, P_HAVE);
(void)v4_fabric_step(&f);
CHECK(ask(a, 0, 300, 0, 9) == -99 && ask(c, 1, 300, 0, 9) == -99, "both neighbours are asleep, waiting for room on the wire to it");
CHECK(v4_fabric_remove(&f, b) == &pool[1], "the node in the middle is removed");
(void)v4_fabric_step(&f);
CHECK(!ND(a)->n.doing && ND(a)->n.how == 2 && !ND(c)->n.doing && ND(c)->n.how == 2, "each neighbour asleep for room is woken with 2");
CHECK(q0->used == 0 && q0->mark == q0->used && q1->used == 0 && q1->mark == q1->used && q2->used == 0 && q2->mark == q2->used && q3->used == 0 && q3->mark == q3->used,
"and what waited on the four queues is let go, their marks at no message");
CHECK(f.place[a].wire[0].kind == V4_WIRE_NONE && f.place[a].wire[0].rx == 0 && f.place[a].wire[0].tx == 0, "the neighbour's wire is cut, with no queue");
CHECK(ask(a, 0, 100, 110, 5) == 2 && ask(a, 0, 100, 110, 1) == 2, "a put or a look on it is 2");
CHECK(v4_fabric_wire(&f, a, 4, c, 4) && v4_fabric_wire(&f, a, 5, c, 5) && v4_fabric_wire(&f, a, 6, c, 6) == 0,
"the queues went back to the pool: all four, so two wires and not a third");
}
/* a device's wire */
v4_fabric_init(&f, places, 4);
v4_fabric_queues(&f, qpool, 8);
a = (unsigned)v4_fabric_add(&f, &pool[0], PB);
b = (unsigned)v4_fabric_add(&f, &pool[1], PB);
CHECK(v4_fabric_wire_device(&f, a, 4, &nothing) && v4_fabric_wire(&f, a, 1, b, 1), "a device on wire 4 of a node, and another node on wire 1");
put_msg(&ND(a)->n, 100, 110, 17, 8);
for (w = 0; w < 7; w++) h[w] = ND(a)->n.mem[100 + w];
for (w = 0; w < 5; w++) t[w] = ND(a)->n.mem[110 + w];
CHECK(v4_fabric_device_put(&f, a, 4, h, t) == 0, "the device puts a message");
CHECK(ask(a, 4, 200, 220, 6) == 0 && seen(a, 4, 200, 220, 2) == 0 && msg_is(&ND(a)->n, 200, 220, 17, 8), "and the node takes it, whole");
put_msg(&ND(a)->n, 100, 110, 3, 9);
CHECK(ask(a, 4, 100, 110, 5) == 0, "the node puts one");
memset(h, 0, sizeof h); memset(t, 0, sizeof t);
CHECK(v4_fabric_device_take(&f, a, 4, h, t, 0) == 4, "the device, with no room for its text, is told 4");
CHECK(v4_fabric_device_take(&f, a, 4, h, t, V4_WIRE_CELLS) == 0 && h[6] == 3 && h[0] == 900 && h[2] == 902, "and takes it with room");
CHECK(v4_fabric_device_take(&f, a, 4, h, t, V4_WIRE_CELLS) == 3, "no more: 3");
CHECK(v4_fabric_device_put(&f, a, 1, h, t) == 2 && v4_fabric_device_take(&f, a, 1, h, t, 1) == 2, "a wire to a node is not a device's: 2");
CHECK(v4_fabric_device_put(&f, a, 6, h, t) == 2 && v4_fabric_device_take(&f, a, 6, h, t, 1) == 2 && v4_fabric_device_put(&f, 7, 4, h, t) == 2, "nor is a port with nothing, or a node that is not there");
h[6] = 1025;
CHECK(v4_fabric_device_put(&f, a, 4, h, t) == 4, "a device cannot put what is not a message");
h[6] = 1024;
fill(f.place[a].wire[4].rx, 0);
CHECK(v4_fabric_device_put(&f, a, 4, h, t) == 1, "nor onto a full wire: 1");
/* a wire when the pool has queues for two: a third fails and changes nothing */
v4_fabric_init(&f, places, 4);
v4_fabric_queues(&f, qpool, 4);
a = (unsigned)v4_fabric_add(&f, &pool[0], PB);
b = (unsigned)v4_fabric_add(&f, &pool[1], PB);
c = (unsigned)v4_fabric_add(&f, &pool[2], PB);
CHECK(v4_fabric_wire(&f, a, 0, b, 0) && v4_fabric_wire_device(&f, c, 0, &nothing), "two wires take the four queues");
CHECK(v4_fabric_wire(&f, a, 1, c, 1) == 0 && v4_fabric_wire_device(&f, b, 1, &nothing) == 0, "a third wire, of either kind, fails");
CHECK(f.place[a].wire[1].kind == V4_WIRE_NONE && f.place[c].wire[1].kind == V4_WIRE_NONE && f.place[b].wire[1].kind == V4_WIRE_NONE,
"and it is not there");
CHECK(f.place[a].wire[0].kind == V4_WIRE_NODE && f.place[a].wire[0].node == b && f.place[c].wire[0].kind == V4_WIRE_DEVICE,
"and what was wired still is");
CHECK(v4_fabric_wire(&f, a, 0, c, 2) && f.place[b].wire[0].kind == V4_WIRE_NONE && f.place[c].wire[2].node == a,
"a port that is wired again gives back its queues first, and gets them");
CHECK(v4_fabric_unwire(&f, a, 0) && v4_fabric_wire(&f, a, 1, b, 1), "and one that is cut gives them back");
CHECK(ask(a, 0, 100, 110, 5) == 2, "a put on the cut one: 2");
}
printf(" %d checks, %d failures\n", checks, failures);
return failures != 0;
}