feat(v4.0.0): messages are whole and wait on the wire -- a node keeps none but the one it is doing
Step 6e, tasks 2 and 3 (docs/v4.0.0/MESH.md 7d). The nucleus puts, looks at, takes, moves and drops whole messages through the six addresses after its ports; its own ring of messages waiting, the refusals owed, (GATE) and the looking words' use are gone. SEND, an answer, a NACK and a GONE go or are refused at once; what a text prints waits for room; a text is not begun until the wire its answer goes by has room for it, and that room is kept. The lone node's host (v4/system/boot.c) keeps its two wires and does its operations; the test hosts' consoles put and take whole messages through the fabric. New: tests/test_host_depth.c, every message path of a mesh node at every depth of both stacks. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
83694b18be
commit
b1d09af043
Binary file not shown.
+244
-210
File diff suppressed because it is too large
Load Diff
+172
-196
File diff suppressed because it is too large
Load Diff
@@ -71,8 +71,8 @@ typedef struct {
|
||||
int v4_boot_run(const v4_boot *b);
|
||||
|
||||
/* Hand the node a line and run it to its end, sending what it prints to
|
||||
* `out`. The line is sent to the node as a message and what it prints
|
||||
* comes back as messages (message.h). Returns how it ended -- V4_TEXT_QUIT,
|
||||
* `out`. The line is put on the node's wire as a message, whole, and what
|
||||
* it prints comes back as messages (message.h; docs/v4.0.0/MESH.md 7d.5). Returns how it ended -- V4_TEXT_QUIT,
|
||||
* V4_TEXT_COMPLETED or V4_TEXT_ERROR -- or one of these: */
|
||||
#define V4_BOOT_LINE_TOO_LONG (-1) /* more than the node's input buffer holds; nothing was run */
|
||||
#define V4_BOOT_LINE_STOPPED (-2) /* the node stopped on a fault, or never finished */
|
||||
@@ -81,12 +81,12 @@ int v4_boot_run(const v4_boot *b);
|
||||
int v4_boot_line(const v4_boot *b, const char *text, unsigned len);
|
||||
|
||||
/* A LINE THAT WAITS (docs/v4.0.0/MESH.md 7b.6). V4_BOOT_LINE_WAITING means
|
||||
* the line has not ended: the node is waiting at its ports for an answer,
|
||||
* the line has not ended: the node is asleep, waiting for an answer,
|
||||
* and here there is no one but the console to end that. The host reads the
|
||||
* next line and hands it over as usual: that breaks the wait, and what is
|
||||
* returned is how the line that was waiting ended -- in an error,
|
||||
* Interrupted. The node has kept the line just handed over; the host then
|
||||
* calls v4_boot_line with no text (0, 0) to have it done, and that returns
|
||||
* Interrupted. The line just handed over is still on the node's wire; the host
|
||||
* then calls v4_boot_line with no text (0, 0) to have it done, and that returns
|
||||
* how it ended, which may be V4_BOOT_LINE_WAITING again. */
|
||||
|
||||
/* The dictionary hash of a node started from `im`. */
|
||||
|
||||
@@ -27,8 +27,8 @@ typedef struct {
|
||||
unsigned cell_bits, node_words, data_ring, ret_ring;
|
||||
|
||||
/* Where the nucleus waits for a message (v4/capsule/quit.v4, (IDLE)):
|
||||
* the node's P is there, blocked reading its ports, when it has nothing
|
||||
* to do. */
|
||||
* a node that has taken the nucleus in begins there, and with nothing
|
||||
* to do it is asleep at a wire inside it (operation 8, MESH.md 7d.3). */
|
||||
v4_cell idle;
|
||||
v4_cell fault_table; /* (FAULTS): where a fault goes */
|
||||
v4_cell port; /* word address of the node's port (node.h) */
|
||||
|
||||
+169
-47
@@ -8,6 +8,7 @@
|
||||
#include "v4/blocks.h"
|
||||
#include "v4/boot.h"
|
||||
#include "v4/message.h"
|
||||
#include "v4/wire.h"
|
||||
#include "starkernel/capsule.h"
|
||||
#include "starkernel/capsule_generated.h"
|
||||
#include "starkernel/capsule_sig.h"
|
||||
@@ -80,10 +81,11 @@ static uint32_t word_count(const v4_node *n, const v4_image *im)
|
||||
/* ---- handing the node a line ---------------------------------------------- */
|
||||
|
||||
/* The boot is the node's neighbour on two of its ports. On port 0 it is
|
||||
* the kernel: the node writes the number of a request there (ENGINE.md
|
||||
* 3.3) -- for a block (blocks.h), or for one of the host's words. On port
|
||||
* 1 it is the console: it writes the node a message of text and reads back
|
||||
* messages of what the node printed and of how the text ended (message.h). */
|
||||
* the kernel: the node writes the number of a request there, a word
|
||||
* (ENGINE.md 3.3) -- for a block (blocks.h), or for one of the host's
|
||||
* words. On port 1 it is the console: it puts a message of text on the
|
||||
* node's wire and takes off it the messages of what the node printed and
|
||||
* of how the text ended (message.h). */
|
||||
#define KERNEL_PORT 0u
|
||||
#define CONSOLE_PORT 1u
|
||||
#define CONSOLE_ID 1 /* the console's number as a sender; the node's is 0 until it is given one */
|
||||
@@ -91,23 +93,165 @@ static uint32_t word_count(const v4_node *n, const v4_image *im)
|
||||
/* What the kernel keeps of the node's block window (blocks.h). */
|
||||
static v4_blocks boot_blocks;
|
||||
|
||||
/* THE WIRES (docs/v4.0.0/MESH.md 7d.5). The lone node has no fabric: the
|
||||
* boot keeps the queues of its two wires, a queue each way (wire.h), and
|
||||
* does what the node asks of them, as a fabric does for a node of a mesh
|
||||
* (fabric.c). Every other port has no one on it. The kernel's wire has
|
||||
* its queues as any wire has; the kernel puts nothing on it and takes
|
||||
* nothing off: its requests are words, not messages. */
|
||||
static v4_wire_queue boot_wire[2][2]; /* by port: [0] to the node, [1] from it */
|
||||
#define TO_NODE(port) (&boot_wire[port][0])
|
||||
#define FROM_NODE(port) (&boot_wire[port][1])
|
||||
|
||||
/* 1 if `count` words from `addr` are the node's memory and not its ports
|
||||
* or the addresses after them: they may be copied to and from. */
|
||||
static int span_ok(const v4_node *n, v4_cell addr, unsigned count)
|
||||
{
|
||||
if (count == 0) return 1;
|
||||
if (count > V4_NODE_WORDS || addr < 0 || (v4_ucell)addr > (v4_ucell)(V4_NODE_WORDS - count)) return 0;
|
||||
if (n->port >= 0 && addr <= n->port + (v4_cell)V4_PORT_HAVE && addr + (v4_cell)count > n->port) return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int boot_arrived(void)
|
||||
{
|
||||
return TO_NODE(KERNEL_PORT)->arrived || TO_NODE(CONSOLE_PORT)->arrived;
|
||||
}
|
||||
|
||||
/* The node is blocked at an operation on a wire (node.h, v4_node_doing):
|
||||
* do it, whole, and let the node go on; the operations and their answers
|
||||
* are MESH.md 7d.3's, as fabric.c does them. Returns 0 if the node sleeps
|
||||
* on: it asked to sleep (8), and no message has come since it last asked
|
||||
* which wires have one; or for room (9) that is not there. */
|
||||
static int boot_do(const v4_boot *bt)
|
||||
{
|
||||
v4_node *n = bt->n;
|
||||
v4_wire_queue *rx = n->wire <= CONSOLE_PORT ? TO_NODE(n->wire) : 0, *tx = n->wire <= CONSOLE_PORT ? FROM_NODE(n->wire) : 0;
|
||||
v4_cell a = n->wire_a, b = n->wire_b, h[V4_WIRE_HEADER], how = V4_WIRE_NO_ONE;
|
||||
unsigned cells;
|
||||
|
||||
switch (n->do_op) {
|
||||
case 1: /* look */
|
||||
if (!rx) break;
|
||||
if (rx->mark >= rx->used) how = V4_WIRE_NO_MESSAGE;
|
||||
else how = span_ok(n, a, V4_WIRE_HEADER) ? v4_wire_look(rx, &n->mem[a]) : V4_WIRE_NOT_A_MESSAGE;
|
||||
break;
|
||||
case 2: /* take */
|
||||
if (!rx) break;
|
||||
if (v4_wire_look(rx, h) != V4_WIRE_DONE) { how = V4_WIRE_NO_MESSAGE; break; }
|
||||
cells = v4_wire_cells(h[6]) - V4_WIRE_HEADER;
|
||||
if (!span_ok(n, a, V4_WIRE_HEADER) || !span_ok(n, b, cells)) how = V4_WIRE_NOT_A_MESSAGE;
|
||||
else how = v4_wire_take(rx, &n->mem[a], cells ? &n->mem[b] : &n->mem[0], cells);
|
||||
break;
|
||||
case 3: /* move */
|
||||
if (!rx) break;
|
||||
if (rx->mark >= rx->used) how = V4_WIRE_NO_MESSAGE;
|
||||
else how = a >= 0 && a <= (v4_cell)CONSOLE_PORT ? v4_wire_move(rx, FROM_NODE((unsigned)a)) : V4_WIRE_NO_ONE;
|
||||
break;
|
||||
case 4: /* drop */
|
||||
if (rx) how = v4_wire_drop(rx);
|
||||
break;
|
||||
case 5: /* put */
|
||||
if (!tx) break;
|
||||
cells = span_ok(n, a, V4_WIRE_HEADER) ? v4_wire_cells(n->mem[a + 6]) : 0;
|
||||
if (cells == 0 || !span_ok(n, b, cells - V4_WIRE_HEADER)) how = V4_WIRE_NOT_A_MESSAGE;
|
||||
else how = v4_wire_put(tx, &n->mem[a], cells > V4_WIRE_HEADER ? &n->mem[b] : &n->mem[0]);
|
||||
break;
|
||||
case 6: /* first */
|
||||
if (rx) { v4_wire_first(rx); how = V4_WIRE_DONE; }
|
||||
break;
|
||||
case 7: /* next */
|
||||
if (rx) how = v4_wire_next(rx);
|
||||
break;
|
||||
case 8: /* sleep */
|
||||
if (!boot_arrived()) return 0;
|
||||
how = V4_WIRE_DONE;
|
||||
break;
|
||||
case 9: /* sleep for room */
|
||||
if (!tx) break;
|
||||
if (a < 0 || a > (v4_cell)V4_WIRE_CELLS) { how = V4_WIRE_NO_ROOM; break; }
|
||||
if (v4_wire_room(tx, (unsigned)a) == V4_WIRE_DONE) { how = V4_WIRE_DONE; break; }
|
||||
if (!boot_arrived()) return 0;
|
||||
how = V4_WIRE_NO_ROOM;
|
||||
break;
|
||||
case 10: /* room */
|
||||
if (!tx) break;
|
||||
how = a < 0 ? V4_WIRE_NOT_A_MESSAGE : a <= (v4_cell)V4_WIRE_CELLS ? v4_wire_room(tx, (unsigned)a) : V4_WIRE_NO_ROOM;
|
||||
break;
|
||||
default: /* not an operation */
|
||||
how = V4_WIRE_NOT_A_MESSAGE;
|
||||
break;
|
||||
}
|
||||
v4_node_done(n, how);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* The node is told which of its wires have a message and executes one
|
||||
* instruction word; if it fetched which wires have one, it has seen what
|
||||
* had come. The first half of a fabric's step (fabric.c). */
|
||||
static void boot_exec(const v4_boot *b)
|
||||
{
|
||||
v4_node *n = b->n;
|
||||
v4_node_have(n, (TO_NODE(KERNEL_PORT)->used ? 1u << KERNEL_PORT : 0u) | (TO_NODE(CONSOLE_PORT)->used ? 1u << CONSOLE_PORT : 0u));
|
||||
(void)v4_exec_step_word(n, b->es, b->h);
|
||||
if (n->have_fetched) {
|
||||
TO_NODE(KERNEL_PORT)->arrived = 0;
|
||||
TO_NODE(CONSOLE_PORT)->arrived = 0;
|
||||
n->have_fetched = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Time passes for the node, in the order a fabric's step has: that, and
|
||||
* then what it asked of a wire is done. Returns 0 if it is asleep at a
|
||||
* wire and sleeps on. */
|
||||
static int boot_step(const v4_boot *b)
|
||||
{
|
||||
boot_exec(b);
|
||||
return v4_node_doing(b->n) ? boot_do(b) : 1;
|
||||
}
|
||||
|
||||
/* Hand the node a line and run it to its end. What it prints goes to the
|
||||
* console; or, if `keep` is given, into `keep`, at most `cap` characters,
|
||||
* and none of it is shown: *printed is then how many it printed in all. */
|
||||
* and none of it is shown: *printed is then how many it printed in all.
|
||||
* The console takes every message off the node's wire each time round, so
|
||||
* what the node prints never waits for room for long. */
|
||||
static int run_line(const v4_boot *b, const char *text, unsigned len, char *keep, unsigned cap, unsigned *printed)
|
||||
{
|
||||
static v4_message out, in; /* one line at a time: the boot is not re-entered */
|
||||
static v4_message out; /* one line at a time: the boot is not re-entered */
|
||||
static v4_cell in[V4_WIRE_HEADER], in_text[V4_MSG_MAX_CHARS / 4u];
|
||||
v4_node *n = b->n;
|
||||
uint64_t steps = 0;
|
||||
unsigned sent = 0, i;
|
||||
unsigned i;
|
||||
|
||||
if (!text) out.count = 0; /* no new line: the node has one it kept (boot.h) */
|
||||
else if (!v4_message_text(&out, 0, CONSOLE_ID, V4_MSG_TEXT, text, len)) return V4_BOOT_LINE_TOO_LONG;
|
||||
in.count = 0;
|
||||
if (text) { /* with none, the node has a line waiting on its wire (boot.h) */
|
||||
if (!v4_message_text(&out, 0, CONSOLE_ID, V4_MSG_TEXT, text, len)) return V4_BOOT_LINE_TOO_LONG;
|
||||
if (v4_wire_put(TO_NODE(CONSOLE_PORT), out.word, out.word + V4_MSG_HEADER) != V4_WIRE_DONE) return V4_BOOT_LINE_STOPPED;
|
||||
}
|
||||
for (;;) {
|
||||
(void)v4_exec_step_word(n, b->es, b->h);
|
||||
int awake = boot_step(b);
|
||||
if (n->stopped) return V4_BOOT_LINE_STOPPED;
|
||||
|
||||
v4_wire_first(FROM_NODE(CONSOLE_PORT)); /* what the node has sent its console, in the order it was sent */
|
||||
while (v4_wire_take(FROM_NODE(CONSOLE_PORT), in, in_text, V4_MSG_MAX_CHARS / 4u) == V4_WIRE_DONE) {
|
||||
if (in[2] == V4_MSG_OUTPUT) {
|
||||
for (i = 0; i < (unsigned)in[6]; i++) {
|
||||
char c = (char)(((v4_ucell)in_text[i / 4u] >> (8u * (i % 4u))) & 0xFFu);
|
||||
if (!keep) b->out(&c, 1);
|
||||
else { if (*printed < cap) keep[*printed] = c; (*printed)++; }
|
||||
}
|
||||
} else if (in[2] == V4_MSG_DONE) {
|
||||
/* The text has ended. The node goes on to where it next asks something of a wire, with
|
||||
* nothing of its own on either stack: whoever called may then look at its stacks, or
|
||||
* empty them. */
|
||||
for (i = 0; i < 4096u && !v4_node_doing(n) && !n->stopped && !n->asking; i++) boot_exec(b);
|
||||
return (int)in_text[0];
|
||||
}
|
||||
}
|
||||
if (!awake) {
|
||||
if (n->do_op == 9) continue; /* waiting for room, which the console has just made */
|
||||
return V4_BOOT_LINE_WAITING; /* waiting for an answer: only the console can end that */
|
||||
}
|
||||
|
||||
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 */
|
||||
v4_node_port_served(n);
|
||||
@@ -122,36 +266,12 @@ static int run_line(const v4_boot *b, const char *text, unsigned len, char *keep
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (n->asking && n->ask_port == CONSOLE_PORT) { /* a word of a message from the node */
|
||||
v4_cell word = n->request;
|
||||
v4_node_port_served(n);
|
||||
if (v4_message_word(&in, word)) {
|
||||
unsigned chars = v4_message_length(&in);
|
||||
if (v4_message_type(&in) == V4_MSG_OUTPUT) {
|
||||
for (i = 0; i < chars; i++) {
|
||||
char c = v4_message_char(&in, i);
|
||||
if (!keep) b->out(&c, 1);
|
||||
else { if (*printed < cap) keep[*printed] = c; (*printed)++; }
|
||||
}
|
||||
} else if (v4_message_type(&in) == V4_MSG_DONE) {
|
||||
return (int)in.word[V4_MSG_HEADER];
|
||||
}
|
||||
in.count = 0;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (n->asking) { /* a port with no one on it: an error on the node */
|
||||
if (n->asking) { /* a word written to a port with no one on it: an error on the node */
|
||||
if (!v4_node_port_gone(n, V4_ERROR_NO_ONE)) return V4_BOOT_LINE_STOPPED;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (n->reading && !n->given) {
|
||||
if (sent < out.count && (n->read_port == CONSOLE_PORT || n->read_port == V4_PORT_ANY)) {
|
||||
v4_node_port_give(n, CONSOLE_PORT, out.word[sent++]); /* a word of the message to the node */
|
||||
continue;
|
||||
}
|
||||
if (n->read_port != CONSOLE_PORT && n->read_port != KERNEL_PORT && v4_node_port_gone(n, V4_ERROR_NO_ONE)) continue; /* reading one port, with no one on it */
|
||||
if (n->read_port == V4_PORT_ANY) return V4_BOOT_LINE_WAITING; /* waiting for an answer: only the console can end that */
|
||||
if (n->reading && !n->given) { /* a word read from a port: no one here writes one */
|
||||
if (n->read_port != V4_PORT_ANY && v4_node_port_gone(n, V4_ERROR_NO_ONE)) continue;
|
||||
return V4_BOOT_LINE_STOPPED; /* waiting for what will not come */
|
||||
}
|
||||
if (v4_image_waiting(n, b->im)) { /* the text is reading the keyboard */
|
||||
@@ -310,10 +430,14 @@ static int load_nucleus(const v4_boot *b)
|
||||
if (!p || cap->length % CELL_BYTES != 0) { say(b, "V4: capsule " NUCLEUS_NAME " is not whole words\n"); return 0; }
|
||||
words = cap->length / CELL_BYTES;
|
||||
|
||||
/* until it has taken every word and is waiting for a message: reading
|
||||
* its ports again, but from its memory now and not from the port */
|
||||
while (at < words || !(n->reading && !n->given && n->p != b->im->port + (v4_cell)V4_PORT_ANY)) {
|
||||
(void)v4_exec_step_word(n, b->es, b->h);
|
||||
/* until it has taken every word and is where the nucleus waits for a
|
||||
* message: asleep, no message having come */
|
||||
for (;;) {
|
||||
if (!boot_step(b)) {
|
||||
if (at >= words && n->do_op == 8) break;
|
||||
say(b, "V4: the node did not take the nucleus in\n");
|
||||
return 0;
|
||||
}
|
||||
if (n->stopped || n->asking || ++steps > STEP_LIMIT) {
|
||||
say(b, "V4: the node did not take the nucleus in\n");
|
||||
return 0;
|
||||
@@ -322,7 +446,6 @@ static int load_nucleus(const v4_boot *b)
|
||||
v4_ucell word = 0;
|
||||
unsigned i;
|
||||
if (at >= words) {
|
||||
if (n->p != b->im->port + (v4_cell)V4_PORT_ANY) break; /* it is waiting for a message */
|
||||
say(b, "V4: the node wants more than the nucleus holds\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -351,10 +474,9 @@ int v4_boot_run(const v4_boot *b)
|
||||
say(b, "V4: the nucleus is not for this build of the engine\nPARITY:FAIL\nPOST: FAILED\n");
|
||||
return 0;
|
||||
}
|
||||
/* what is on its two ports, the kernel and the console, takes a word
|
||||
* when it is written: the node need never wait before writing to them
|
||||
* (node.h, v4_node_port_status) */
|
||||
v4_node_port_status(b->n, 0, (1u << KERNEL_PORT | 1u << CONSOLE_PORT) * (1u + (1u << V4_PORTS))); /* each of them there, and taking */
|
||||
/* its two wires, the kernel's and the console's, with nothing on them */
|
||||
v4_wire_reset(TO_NODE(KERNEL_PORT)); v4_wire_reset(FROM_NODE(KERNEL_PORT));
|
||||
v4_wire_reset(TO_NODE(CONSOLE_PORT)); v4_wire_reset(FROM_NODE(CONSOLE_PORT));
|
||||
v4_blocks_init(&boot_blocks, b->im->block_window);
|
||||
if (!load_nucleus(b)) { say(b, "PARITY:FAIL\nPOST: FAILED\n"); return 0; }
|
||||
|
||||
|
||||
+58
-39
@@ -65,12 +65,7 @@
|
||||
#define SRC (BVARS + 8)
|
||||
#define SRC_HOOK (BVARS + 9)
|
||||
#define QUIET (BUF0_W - 10) /* 1 while the node does something that is no text's doing: an error then is not told (quit.v4, (RAISED)) */
|
||||
#define A_WORD (BUF0_W - 113) /* AWAIT's look through the messages waiting: the answer's word, */
|
||||
#define A_LEFT (BUF0_W - 114) /* how many cells are still to be looked at, */
|
||||
#define A_FOUND (BUF0_W - 115) /* and what was found */
|
||||
#define REFUSED (BUF0_W - 7) /* how many of this node's messages it has been told were refused */
|
||||
#define OWED_COUNT (BUF0_W - 8) /* how many refusals this node owes and has not sent yet, at most 8 */
|
||||
#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 - 140) /* the node's ports (node.h): V4_PORTS of them, then "any port", "which port" and the rest as far as V4_PORT_HAVE. Port 0 is where its requests go. */
|
||||
@@ -79,24 +74,42 @@
|
||||
#define ROUTE_DEFAULT (BUF0_W - 18) /* the port address for a node not in the table, or 0: there is none */
|
||||
#define LOST (BUF0_W - 19) /* how many messages have been let go for want of a way */
|
||||
#define PRINT_TO (BUF0_W - 20) /* where what the text being served prints is to go: the node, */
|
||||
#define WRITERS (PORT + (v4_cell)V4_PORTS + 2) /* which ports have a neighbour waiting to write to this node (node.h) */
|
||||
#define READERS (PORT + (v4_cell)V4_PORTS + 3) /* and which have one waiting to read from it */
|
||||
#define PRINT_PORT (BUF0_W - 33) /* and the port address that leads there */
|
||||
#define DONE_PORT (BUF0_W - 34) /* the port address that leads back to whoever sent the text being served */
|
||||
#define MQ_HEAD (BUF0_W - 35) /* the messages waiting to be dealt with: where the oldest begins, */
|
||||
#define MQ_TAIL (BUF0_W - 36) /* where the next will go, */
|
||||
#define MQ_COUNT (BUF0_W - 37) /* and how many cells they take */
|
||||
#define PRINT_PORT (BUF0_W - 33) /* and the port, by its number, that leads there */
|
||||
#define DONE_PORT (BUF0_W - 34) /* the port, by its number, that leads back to whoever sent the text being served */
|
||||
/* WHOLE MESSAGES (MESH.md 7d.4; core.v4). A node keeps no message but the one it is doing: these are what it
|
||||
* builds one in and looks at one with, and where it has got to in going through its wires. */
|
||||
#define WIRE (PORT + (v4_cell)V4_PORT_WIRE) /* the six addresses after the ports (node.h): the wire an operation is about, */
|
||||
#define WIRE_A (PORT + (v4_cell)V4_PORT_A) /* what it needs, */
|
||||
#define WIRE_B (PORT + (v4_cell)V4_PORT_B)
|
||||
#define WIRE_DO (PORT + (v4_cell)V4_PORT_DO) /* the operation, */
|
||||
#define WIRE_HOW (PORT + (v4_cell)V4_PORT_HOW) /* how it went, */
|
||||
#define WIRE_HAVE (PORT + (v4_cell)V4_PORT_HAVE) /* and which wires have a message */
|
||||
#define S_HAVE (BUF0_W - 35) /* which wires had a message when the node last asked, */
|
||||
#define S_MASK (BUF0_W - 36) /* those of them still to be gone through, */
|
||||
#define S_WIRE (BUF0_W - 37) /* the one being gone through, */
|
||||
#define S_ON (BUF0_W - 38) /* and whether its mark has been put at its first message */
|
||||
#define KEEP (BUF0_W - 40) /* the port the answer to the text being done goes by, on whose wire 8 cells are kept for it; -1: none */
|
||||
#define DEFER (BUF0_W - 41) /* a port whose wire had no room for the answer to text that waits to be begun; -1: none */
|
||||
#define IN_HDR (BUF0_W - 56) /* the seven words of the message a wire's mark is at */
|
||||
#define ONE (BUF0_W - 49) /* the one word of text of a NACK, a GONE or an answer, coming or going */
|
||||
#define OUT_HDR (BUF0_W - 112) /* the seven words of a message this node is putting on a wire */
|
||||
#define W_NEED (BUF0_W - 105) /* the wait for room: how many cells, */
|
||||
#define W_PORT (BUF0_W - 104) /* and on which port's wire */
|
||||
#define P_WAY (BUF0_W - 103) /* the port a message being passed on goes by */
|
||||
#define A_SELF (BUF0_W - 102) /* 1: a message of this node's own, for the node it waits on, came back and was let go here */
|
||||
#define SEND_PTR (BUF0_W - 101) /* where the next word of a text being made ready to send goes */
|
||||
#define OUT_TEXT (OUT_W - 64) /* what was printed, four characters to a cell, as it goes on the wire: 64 cells */
|
||||
#define SEND_TEXT (OUT_W - 400) /* the text of a message SEND is putting, four characters to a cell: 256 cells */
|
||||
/* FREE, plain memory: BUF0_W - 8; - 32 .. - 21; - 100 .. - 97; - 115 .. - 113; and OUT_W - 144 .. OUT_W - 65. They and
|
||||
* the cells above held the messages waiting, the refusals owed and what went with them (MESH.md 7a, 7b), which a
|
||||
* node no longer keeps. */
|
||||
#define AWAIT_FROM (BUF0_W - 39) /* the node whose word of how text ended is waited for */
|
||||
#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 MQ_HDR (BUF0_W - 56) /* a message being taken in: its seven words and the port it came on */
|
||||
#define MQ_W (OUT_W - 400) /* the messages waiting: each is its seven words, the port it came on, its text */
|
||||
#define MQ_CELLS 400
|
||||
#define ROUTES (BUF0_W - 96) /* the table of ways: 16 entries of a node and the port address that leads to it */
|
||||
#define ROUTE_MAX 16
|
||||
#define ME (BUF0_W - 13) /* this node's number: a message is for it when its first word is this */
|
||||
#define OUT_PTR (BUF0_W - 14) /* where the next character printed goes: a cell of the output buffer */
|
||||
#define REPLY (BUF0_W - 15) /* the address of the port the message being served came on */
|
||||
#define REPLY (BUF0_W - 15) /* the port, by its number, the text being served came on */
|
||||
#define MSG (BUF0_W - 48) /* the header of the message being served, 7 cells */
|
||||
#define OUT_W (BUF0_W - 400) /* the output buffer: 256 cells, a character to a cell */
|
||||
#define OUT_CELLS 256
|
||||
@@ -106,12 +119,12 @@
|
||||
#define BOOT_CELLS (BVARS + 14) /* (BOOT): DP and LATEST as the loader left them, 2 cells */
|
||||
#define BUF0_W (BVARS - (v4_cell)(V4_BLOCK_SLOTS * V4_BLOCK_CELLS)) /* the block window: the kernel's slots, 256 cells each (v4/blocks.h) */
|
||||
/* The ports and the addresses after them, through V4_PORT_HAVE (node.h, MESH.md 7d.3), are not memory and must be where
|
||||
* no variable is: after the output buffer, which ends at BUF0_W - 144, and before A_FOUND, the first variable above it.
|
||||
* no variable is: after the output buffer, which ends at BUF0_W - 144, and before OUT_HDR, the first variable above it.
|
||||
* With 8 ports that is 18 addresses, BUF0_W - 140 to BUF0_W - 123. (They were once among the variables, and what was
|
||||
* stored to the variables was stored to ports.) */
|
||||
typedef char host_map_ports_fit[(PORT >= OUT_W + OUT_CELLS && PORT + (v4_cell)V4_PORT_HAVE < A_FOUND) ? 1 : -1];
|
||||
/* Nothing printed and no message waiting: a node's variables for them, as at switch-on. */
|
||||
#define HOST_MESSAGES_EMPTY(n) do { (n)->mem[OUT_PTR] = OUT_W; (n)->mem[MQ_HEAD] = MQ_W; (n)->mem[MQ_TAIL] = MQ_W; (n)->mem[MQ_COUNT] = 0; } while (0)
|
||||
typedef char host_map_ports_fit[(PORT >= OUT_W + OUT_CELLS && PORT + (v4_cell)V4_PORT_HAVE < OUT_HDR) ? 1 : -1];
|
||||
/* Nothing printed: a node's variable for it, as at switch-on. */
|
||||
#define HOST_MESSAGES_EMPTY(n) do { (n)->mem[OUT_PTR] = OUT_W; } while (0)
|
||||
#define QVARS (BUF0_W - 4) /* (Q): quit.v4, system.v4 and blocks.v4, 4 cells */
|
||||
#define WBUF (WBUF_W * 4)
|
||||
#define TIB (TIB_W * 4)
|
||||
@@ -125,8 +138,8 @@ typedef char host_map_ports_fit[(PORT >= OUT_W + OUT_CELLS && PORT + (v4_cell)V4
|
||||
#ifndef DICT_END_W
|
||||
#define DICT_END_W ((v4_cell)13824) /* 512 cells lower than it was: the block window is four slots now */
|
||||
#endif
|
||||
/* the messages waiting are above the dictionary */
|
||||
typedef char host_map_queue_fits[(MQ_W >= DICT_END_W) ? 1 : -1];
|
||||
/* the texts made ready for the wire are above the dictionary */
|
||||
typedef char host_map_texts_fit[(SEND_TEXT >= DICT_END_W && SEND_TEXT + 256 <= OUT_TEXT && OUT_TEXT + 64 == OUT_W) ? 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. */
|
||||
@@ -194,27 +207,33 @@ static int host_load(v4_text *tx, v4_node *n, const char *const *files, unsigned
|
||||
v4_text_constant(tx, "(PRINT-PORT)", PRINT_PORT);
|
||||
v4_text_constant(tx, "(DONE-PORT)", DONE_PORT);
|
||||
v4_text_constant(tx, "(ROUTES)", ROUTES);
|
||||
v4_text_constant(tx, "(WRITERS)", WRITERS);
|
||||
v4_text_constant(tx, "(READERS)", READERS);
|
||||
v4_text_constant(tx, "-PORTS", -(v4_cell)V4_PORTS);
|
||||
v4_text_constant(tx, "(NEAR)", NEAR);
|
||||
v4_text_constant(tx, "(GATE-PORT)", GATE_PORT);
|
||||
v4_text_constant(tx, "(AWAIT-FROM)", AWAIT_FROM);
|
||||
v4_text_constant(tx, "(MQ)", MQ_W);
|
||||
v4_text_constant(tx, "(MQ-END)", MQ_W + MQ_CELLS);
|
||||
v4_text_constant(tx, "-MQ-ORDINARY", -(v4_cell)(MQ_CELLS - 7 - 36)); /* the same for a message that is not a refusal or a GONE: 36 cells are kept for those */
|
||||
v4_text_constant(tx, "(WIRE)", WIRE);
|
||||
v4_text_constant(tx, "(WIRE-A)", WIRE_A);
|
||||
v4_text_constant(tx, "(WIRE-B)", WIRE_B);
|
||||
v4_text_constant(tx, "(WIRE-DO)", WIRE_DO);
|
||||
v4_text_constant(tx, "(WIRE-HOW)", WIRE_HOW);
|
||||
v4_text_constant(tx, "(WIRE-HAVE)", WIRE_HAVE);
|
||||
v4_text_constant(tx, "(S-HAVE)", S_HAVE);
|
||||
v4_text_constant(tx, "(S-MASK)", S_MASK);
|
||||
v4_text_constant(tx, "(S-WIRE)", S_WIRE);
|
||||
v4_text_constant(tx, "(S-ON)", S_ON);
|
||||
v4_text_constant(tx, "(KEEP)", KEEP);
|
||||
v4_text_constant(tx, "(DEFER)", DEFER);
|
||||
v4_text_constant(tx, "(IN)", IN_HDR);
|
||||
v4_text_constant(tx, "(ONE)", ONE);
|
||||
v4_text_constant(tx, "(OUT-HDR)", OUT_HDR);
|
||||
v4_text_constant(tx, "(W-NEED)", W_NEED);
|
||||
v4_text_constant(tx, "(W-PORT)", W_PORT);
|
||||
v4_text_constant(tx, "(P-WAY)", P_WAY);
|
||||
v4_text_constant(tx, "(A-SELF)", A_SELF);
|
||||
v4_text_constant(tx, "(SEND^)", SEND_PTR);
|
||||
v4_text_constant(tx, "(OUT-TEXT)", OUT_TEXT);
|
||||
v4_text_constant(tx, "(SEND-TEXT)", SEND_TEXT);
|
||||
v4_text_constant(tx, "(QUIET)", QUIET);
|
||||
v4_text_constant(tx, "(A-WORD)", A_WORD);
|
||||
v4_text_constant(tx, "(A-LEFT)", A_LEFT);
|
||||
v4_text_constant(tx, "(A-FOUND)", A_FOUND);
|
||||
v4_text_constant(tx, "(REFUSED)", REFUSED);
|
||||
v4_text_constant(tx, "(OWED#)", OWED_COUNT);
|
||||
v4_text_constant(tx, "(OWED)", OWED);
|
||||
v4_text_constant(tx, "-MQ-ROOM", -(v4_cell)(MQ_CELLS - 7)); /* cells taken + a text's words + this is below zero while the message fits */
|
||||
v4_text_constant(tx, "(MQ-HEAD)", MQ_HEAD);
|
||||
v4_text_constant(tx, "(MQ-TAIL)", MQ_TAIL);
|
||||
v4_text_constant(tx, "(MQ#)", MQ_COUNT);
|
||||
v4_text_constant(tx, "(MQ-HDR)", MQ_HDR);
|
||||
v4_text_constant(tx, "(OUT^)", OUT_PTR);
|
||||
v4_text_constant(tx, "(REPLY)", REPLY);
|
||||
v4_text_constant(tx, "(MSG)", MSG);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+432
-225
File diff suppressed because it is too large
Load Diff
+90
-36
@@ -42,6 +42,7 @@
|
||||
*/
|
||||
#include "v4/text.h"
|
||||
#include "v4/message.h"
|
||||
#include "v4/wire.h"
|
||||
#include "v4/testcode.h"
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
@@ -63,7 +64,6 @@ static v4_heat h;
|
||||
static v4_text tx;
|
||||
static v4_cell w_key, w_key_end, w_fault, capsule_latest;
|
||||
static v4_cell w_idle; /* (IDLE): where the node waits for a message */
|
||||
static v4_cell w_idle_end; /* (PASS-ON): the word after it */
|
||||
static char out[V4_CONSOLE_CAP + 1];
|
||||
|
||||
/* STORAGE (MESH.md section 8). The node asks its kernel for its blocks, and
|
||||
@@ -190,17 +190,81 @@ static const char *node_name(v4_cell xt)
|
||||
static char typed[8192]; /* typed and not yet taken */
|
||||
static unsigned typed_len;
|
||||
static int line_open; /* a line has been sent and has not ended */
|
||||
static v4_message to_node, from_node; /* the message going in, and the one coming out */
|
||||
static unsigned to_sent; /* how many words of to_node the node has taken */
|
||||
static v4_message to_node; /* the message going in */
|
||||
static char shown[V4_CONSOLE_CAP]; /* what the node has printed since the console last looked */
|
||||
static unsigned shown_len;
|
||||
static int shown_dropped;
|
||||
static int text_ended; /* how the text ended, when it has */
|
||||
|
||||
/* 1 if the node is where it waits for a message, with nothing given it. */
|
||||
/* THE WIRES (MESH.md 7d.5). The node is alone and has no fabric: these
|
||||
* tests keep the queues of its console's wire, do what it asks of them, and
|
||||
* are what is on the other end, as v4/system/boot.c is for the products.
|
||||
* Every other port has no one on it. */
|
||||
static v4_wire_queue wire_in, wire_out; /* to the node from its console, and from it */
|
||||
|
||||
static int span_ok(v4_cell addr, unsigned count)
|
||||
{
|
||||
if (count == 0) return 1;
|
||||
if (addr < 0 || (v4_ucell)addr > (v4_ucell)(V4_NODE_WORDS - count)) return 0;
|
||||
return !(addr <= PORT + (v4_cell)V4_PORT_HAVE && addr + (v4_cell)count > PORT);
|
||||
}
|
||||
/* Do the operation the node is blocked at. 0: it sleeps on. */
|
||||
static int wire_do(void)
|
||||
{
|
||||
v4_wire_queue *rx = n.wire == CONSOLE_PORT ? &wire_in : 0, *tx = n.wire == CONSOLE_PORT ? &wire_out : 0;
|
||||
v4_cell a = n.wire_a, b = n.wire_b, hd[V4_WIRE_HEADER], how = V4_WIRE_NO_ONE;
|
||||
unsigned cells;
|
||||
|
||||
switch (n.do_op) {
|
||||
case 1: if (rx) how = span_ok(a, V4_WIRE_HEADER) || rx->mark >= rx->used ? v4_wire_look(rx, span_ok(a, V4_WIRE_HEADER) ? &n.mem[a] : hd) : V4_WIRE_NOT_A_MESSAGE; break;
|
||||
case 2:
|
||||
if (!rx) break;
|
||||
if (v4_wire_look(rx, hd) != V4_WIRE_DONE) { how = V4_WIRE_NO_MESSAGE; break; }
|
||||
cells = v4_wire_cells(hd[6]) - V4_WIRE_HEADER;
|
||||
how = span_ok(a, V4_WIRE_HEADER) && span_ok(b, cells) ? v4_wire_take(rx, &n.mem[a], cells ? &n.mem[b] : hd, cells) : V4_WIRE_NOT_A_MESSAGE;
|
||||
break;
|
||||
case 3: if (rx) how = rx->mark >= rx->used ? V4_WIRE_NO_MESSAGE : a == (v4_cell)CONSOLE_PORT ? v4_wire_move(rx, &wire_out) : V4_WIRE_NO_ONE; break;
|
||||
case 4: if (rx) how = v4_wire_drop(rx); break;
|
||||
case 5:
|
||||
if (!tx) break;
|
||||
cells = span_ok(a, V4_WIRE_HEADER) ? v4_wire_cells(n.mem[a + 6]) : 0;
|
||||
how = cells && span_ok(b, cells - V4_WIRE_HEADER) ? v4_wire_put(tx, &n.mem[a], cells > V4_WIRE_HEADER ? &n.mem[b] : hd) : V4_WIRE_NOT_A_MESSAGE;
|
||||
break;
|
||||
case 6: if (rx) { v4_wire_first(rx); how = V4_WIRE_DONE; } break;
|
||||
case 7: if (rx) how = v4_wire_next(rx); break;
|
||||
case 8: if (!wire_in.arrived) return 0; how = V4_WIRE_DONE; break;
|
||||
case 9:
|
||||
if (!tx) break;
|
||||
if (a < 0 || a > (v4_cell)V4_WIRE_CELLS) { how = V4_WIRE_NO_ROOM; break; }
|
||||
if (v4_wire_room(tx, (unsigned)a) == V4_WIRE_DONE) { how = V4_WIRE_DONE; break; }
|
||||
if (!wire_in.arrived) return 0;
|
||||
how = V4_WIRE_NO_ROOM;
|
||||
break;
|
||||
case 10: if (tx) how = a < 0 ? V4_WIRE_NOT_A_MESSAGE : a <= (v4_cell)V4_WIRE_CELLS ? v4_wire_room(tx, (unsigned)a) : V4_WIRE_NO_ROOM; break;
|
||||
default: how = V4_WIRE_NOT_A_MESSAGE; break;
|
||||
}
|
||||
v4_node_done(&n, how);
|
||||
return 1;
|
||||
}
|
||||
/* One instruction word, and then what the node asked of its wires; as the
|
||||
* fabric orders a step (fabric.c). */
|
||||
static void node_step(void)
|
||||
{
|
||||
v4_node_have(&n, wire_in.used > 0 ? 1u << CONSOLE_PORT : 0u);
|
||||
(void)v4_exec_step_word(&n, &es, &h);
|
||||
if (n.have_fetched) { wire_in.arrived = 0; n.have_fetched = 0; }
|
||||
if (v4_node_doing(&n)) (void)wire_do();
|
||||
}
|
||||
/* The console puts a line on the node's wire. */
|
||||
static int console_put(const v4_message *m)
|
||||
{
|
||||
return v4_wire_put(&wire_in, m->word, m->word + V4_MSG_HEADER) == V4_WIRE_DONE;
|
||||
}
|
||||
|
||||
/* 1 if the node is asleep with nothing to do: where it waits for a message. */
|
||||
static int node_idle(void)
|
||||
{
|
||||
return n.reading && !n.given && n.read_port == V4_PORT_ANY && n.p > w_idle && n.p <= w_idle_end; /* in (IDLE), where it reads */
|
||||
return v4_node_doing(&n) && n.do_op == 8 && wire_in.used == 0 && n.mem[AWAIT_FROM] == 0;
|
||||
}
|
||||
|
||||
/* Run until the text ends, or until it has been inside KEY with nothing to
|
||||
@@ -208,35 +272,27 @@ static int node_idle(void)
|
||||
* for a character, 0: neither. */
|
||||
static int run_line(long max)
|
||||
{
|
||||
static v4_cell hd[V4_WIRE_HEADER], text[V4_MSG_MAX_CHARS / 4u];
|
||||
long steps = 0;
|
||||
unsigned idle = 0;
|
||||
while (steps < max && idle < 64) {
|
||||
(void)v4_exec_step_word(&n, &es, &h);
|
||||
node_step();
|
||||
steps++;
|
||||
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 */
|
||||
v4_cell word = n.request;
|
||||
v4_node_port_served(&n);
|
||||
if (v4_message_word(&from_node, word)) {
|
||||
unsigned i, chars = v4_message_length(&from_node);
|
||||
if (v4_message_type(&from_node) == V4_MSG_OUTPUT) {
|
||||
for (i = 0; i < chars; i++) {
|
||||
if (shown_len < sizeof shown) shown[shown_len++] = v4_message_char(&from_node, i);
|
||||
else shown_dropped = 1;
|
||||
}
|
||||
} else if (v4_message_type(&from_node) == V4_MSG_DONE) {
|
||||
text_ended = (int)from_node.word[V4_MSG_HEADER];
|
||||
from_node.count = 0;
|
||||
last_steps = steps;
|
||||
return 1;
|
||||
if (n.asking) { (void)v4_node_port_gone(&n, 18); continue; } /* a port with no one on it */
|
||||
v4_wire_first(&wire_out);
|
||||
while (v4_wire_take(&wire_out, hd, text, V4_MSG_MAX_CHARS / 4u) == V4_WIRE_DONE) { /* what the node has sent */
|
||||
if (hd[2] == V4_MSG_OUTPUT) {
|
||||
unsigned i;
|
||||
for (i = 0; i < (unsigned)hd[6]; i++) {
|
||||
if (shown_len < sizeof shown) shown[shown_len++] = (char)(((v4_ucell)text[i / 4u] >> (8u * (i % 4u))) & 0xFFu);
|
||||
else shown_dropped = 1;
|
||||
}
|
||||
from_node.count = 0;
|
||||
} else if (hd[2] == V4_MSG_DONE) {
|
||||
text_ended = (int)text[0];
|
||||
last_steps = steps;
|
||||
return 1;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (n.reading && !n.given && to_sent < to_node.count && (n.read_port == CONSOLE_PORT || n.read_port == V4_PORT_ANY)) {
|
||||
v4_node_port_give(&n, CONSOLE_PORT, to_node.word[to_sent++]); /* a word of the message to the node */
|
||||
continue;
|
||||
}
|
||||
if (n.input_pos == n.input_len && n.p >= w_key && n.p < w_key_end) idle++; else idle = 0;
|
||||
}
|
||||
@@ -279,7 +335,7 @@ static void boot_with(unsigned depth)
|
||||
for (i = 0; i < depth; i++) v4_dstack_push(&n.ds, (v4_cell)(0x5A000000 + i));
|
||||
v4_dstack_push(&n.ds, CANARY);
|
||||
v4_node_port_attach(&n, PORT);
|
||||
v4_node_port_status(&n, 0, (1u | 1u << CONSOLE_PORT) * (1u + (1u << V4_PORTS))); /* the kernel and the console take what is written when it is written */
|
||||
v4_wire_reset(&wire_in); v4_wire_reset(&wire_out);
|
||||
n.mem[WORD_DEFINED] = 0; /* no one is told of entries, until a test says so */
|
||||
n.mem[WORD_FORGOTTEN] = 0;
|
||||
known_n = 0;
|
||||
@@ -287,13 +343,13 @@ static void boot_with(unsigned depth)
|
||||
n.mem[LINE_STATUS] = 1;
|
||||
n.mem[ME] = 0; /* it has no number yet: a message to 0 is for it */
|
||||
HOST_MESSAGES_EMPTY(&n);
|
||||
n.mem[REPLY] = PORT + CONSOLE_PORT;
|
||||
n.mem[REPLY] = CONSOLE_PORT;
|
||||
n.p = w_idle; /* waiting for a message */
|
||||
typed_len = 0;
|
||||
line_open = 0;
|
||||
to_node.count = 0; to_sent = 0; from_node.count = 0;
|
||||
to_node.count = 0;
|
||||
shown_len = 0; shown_dropped = 0;
|
||||
for (i = 0; i < 64 && !n.reading; i++) (void)v4_exec_step_word(&n, &es, &h); /* no message waits: it reads its ports, and is blocked */
|
||||
for (i = 0; i < 256 && !node_idle(); i++) node_step(); /* no message waits: it sleeps */
|
||||
}
|
||||
static void boot(void) { boot_with(0); }
|
||||
/* The same with nothing at all on the data stack. */
|
||||
@@ -335,7 +391,7 @@ static const char *say(const char *input)
|
||||
if (!nl) break;
|
||||
len = (unsigned)(nl - typed);
|
||||
if (!v4_message_text(&to_node, 0, 1, V4_MSG_TEXT, typed, len)) return "(line too long)";
|
||||
to_sent = 0;
|
||||
if (!console_put(&to_node)) return "(no room on the wire)";
|
||||
memmove(typed, nl + 1, typed_len - len - 1);
|
||||
typed_len -= len + 1;
|
||||
line_open = 1;
|
||||
@@ -376,8 +432,7 @@ static int post_line(void *self, const char *text, unsigned text_len, char *o, u
|
||||
(void)self;
|
||||
*len = 0;
|
||||
if (post_state_fails && text_len == 25 && memcmp(text, "DECIMAL FORTH DEFINITIONS", 25) == 0) text = "NOSUCHWORD-FOR-STATE ";
|
||||
if (!v4_message_text(&to_node, 0, 1, V4_MSG_TEXT, text, text_len)) return -1;
|
||||
to_sent = 0; from_node.count = 0;
|
||||
if (!v4_message_text(&to_node, 0, 1, V4_MSG_TEXT, text, text_len) || !console_put(&to_node)) return -1;
|
||||
shown_len = 0; shown_dropped = 0;
|
||||
ended = run_line(post_step_limit);
|
||||
if (ended != 1) return -1;
|
||||
@@ -870,7 +925,6 @@ int main(void)
|
||||
printf(" capsule: %ld words\n", (long)v4_text_here(&tx) - 16);
|
||||
if (failures) { printf(" %d checks, %d failures\n", checks, failures); return 1; }
|
||||
w_idle = v4_text_word(&tx, "(IDLE)");
|
||||
w_idle_end = v4_text_word(&tx, "(PASS-ON)"); /* the word after (IDLE) in quit.v4 */
|
||||
w_key = v4_text_word(&tx, "KEY");
|
||||
w_key_end = v4_text_word(&tx, "CR"); /* the word after KEY in core.v4 */
|
||||
w_fault = v4_text_word(&tx, "(FAULTS)");
|
||||
@@ -1153,7 +1207,7 @@ int main(void)
|
||||
(void)say("WORDS\n");
|
||||
for (at = 0; at < plain; at++) if (n.mem[OUT_W + OUT_CELLS + at] != 0) { over++; worst = k; break; }
|
||||
if (n.mem[OUT_PTR] > OUT_W + OUT_CELLS) { over++; worst = k; }
|
||||
if (n.asking || n.doing) { over++; worst = k; } /* it stored to a port */
|
||||
if (n.asking) { over++; worst = k; } /* it stored to a port */
|
||||
CHECK(strcmp(say("1 2 + .\n"), "3 ok\nok> ") == 0, "WORDS with %u values on the stack: the node goes on", k);
|
||||
}
|
||||
CHECK(over == 0, "nothing was ever put past the end of the output buffer (it was, with %u values on the stack)", worst);
|
||||
|
||||
+104
-74
@@ -63,6 +63,8 @@ static int storage_made(void)
|
||||
}
|
||||
static v4_place places[PLACES];
|
||||
static v4_fabric f;
|
||||
#define QUEUES 64u /* two for every wire there is at once: five kernels, the console, four to Hera, four round the square */
|
||||
static v4_wire_queue queues[QUEUES];
|
||||
static v4_image im; /* only what the dictionary hash needs of it */
|
||||
|
||||
/* ---- the capsules ---- */
|
||||
@@ -196,10 +198,10 @@ static int kernel_take(void *self, v4_cell value) /* a request */
|
||||
}
|
||||
static const v4_device kernel = { kernel_take, kernel_give, 0, 0 };
|
||||
|
||||
/* ---- the console: a device on Hera's port 1 ---- */
|
||||
/* ---- the console: a device on Hera's port 1 ----
|
||||
* It speaks whole messages: it puts them on the wire to Hera and takes them
|
||||
* off the wire from her, through the fabric (MESH.md 7d.2). */
|
||||
#define CONSOLE_ID 1
|
||||
static v4_message going, coming;
|
||||
static unsigned going_at;
|
||||
static char printed[8192]; /* what has come back since the console last sent */
|
||||
static unsigned printed_len;
|
||||
static v4_cell printed_from;
|
||||
@@ -207,48 +209,45 @@ static int ended;
|
||||
static v4_cell ended_from, ended_how;
|
||||
static unsigned post_passed[32]; /* by node number: POST tallies with no failures seen from it */
|
||||
static unsigned post_other; /* tallies with failures */
|
||||
static const v4_device console = { 0, 0, 0, 0 };
|
||||
|
||||
static int console_give(void *self, v4_cell *value)
|
||||
static unsigned console_takes(void)
|
||||
{
|
||||
(void)self;
|
||||
if (going_at >= going.count) return 0;
|
||||
*value = going.word[going_at++];
|
||||
return 1;
|
||||
}
|
||||
static int console_take(void *self, v4_cell value)
|
||||
{
|
||||
(void)self;
|
||||
if (v4_message_word(&coming, value)) {
|
||||
if (v4_message_type(&coming) == V4_MSG_OUTPUT) {
|
||||
unsigned i, chars = v4_message_length(&coming), start = printed_len;
|
||||
for (i = 0; i < chars && printed_len + 1 < sizeof printed; i++) printed[printed_len++] = v4_message_char(&coming, i);
|
||||
static v4_cell hd[V4_WIRE_HEADER], text[V4_MSG_MAX_CHARS / 4u];
|
||||
unsigned took = 0;
|
||||
while (v4_fabric_device_take(&f, hera_place, 1, hd, text, V4_MSG_MAX_CHARS / 4u) == V4_WIRE_DONE) {
|
||||
took++;
|
||||
if (hd[2] == V4_MSG_OUTPUT) {
|
||||
unsigned i, start = printed_len;
|
||||
for (i = 0; i < (unsigned)hd[6] && printed_len + 1 < sizeof printed; i++) printed[printed_len++] = (char)(((v4_ucell)text[i / 4u] >> (8u * (i % 4u))) & 0xFFu);
|
||||
printed[printed_len] = 0;
|
||||
printed_from = v4_message_from(&coming);
|
||||
printed_from = hd[1];
|
||||
if (strstr(printed + start, "PARITY:V4_POST")) {
|
||||
if (strstr(printed + start, " fail=0\n") && printed_from >= 0 && printed_from < 32) post_passed[printed_from]++;
|
||||
else post_other++;
|
||||
}
|
||||
} else if (v4_message_type(&coming) == V4_MSG_DONE) {
|
||||
} else if (hd[2] == V4_MSG_DONE) {
|
||||
ended++;
|
||||
ended_from = v4_message_from(&coming);
|
||||
ended_how = coming.word[V4_MSG_HEADER];
|
||||
ended_from = hd[1];
|
||||
ended_how = text[0];
|
||||
}
|
||||
coming.count = 0;
|
||||
}
|
||||
return 1;
|
||||
return took;
|
||||
}
|
||||
static int console_pending(void *self) { (void)self; return going_at < going.count; }
|
||||
static const v4_device console = { console_take, console_give, 0, console_pending };
|
||||
|
||||
/* Is there a node that will execute at the next step? A step in which a
|
||||
* node only faulted counts nothing done (exec.h), and it goes on from its
|
||||
* fault handler at the next. */
|
||||
/* Is there a node, other than those known to be going round for ever, that
|
||||
* will execute at the next step? A node asleep at a wire will not; a step
|
||||
* in which a node only faulted counts nothing done (exec.h), and it goes on
|
||||
* from its fault handler at the next. */
|
||||
static unsigned spinning; /* bit n: the node numbered n is known to be stuck in a loop, and is not waited for */
|
||||
static int any_running(void)
|
||||
{
|
||||
unsigned k;
|
||||
for (k = 0; k < PLACES; k++) {
|
||||
const v4_fabric_node *x = v4_fabric_node_at(&f, k);
|
||||
if (x && !places[k].asleep && !x->n.stopped && !x->n.asking && !(x->n.reading && !x->n.given)) return 1;
|
||||
if (!x || places[k].asleep || x->n.stopped || x->n.asking || x->n.doing || (x->n.reading && !x->n.given)) continue;
|
||||
if (x->n.mem[ME] >= 0 && x->n.mem[ME] < 32 && (spinning & 1u << x->n.mem[ME])) continue;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -260,21 +259,31 @@ static void stuck(void)
|
||||
unsigned k;
|
||||
for (k = 0; k < PLACES; k++) {
|
||||
const v4_fabric_node *x = v4_fabric_node_at(&f, k);
|
||||
if (x) printf(" stuck: node %ld p=%ld asking=%d port=%u reading=%d rport=%u given=%d stopped=%d mq=%ld lost=%ld\n", (long)x->n.mem[ME], (long)x->n.p,
|
||||
x->n.asking, x->n.ask_port, x->n.reading, x->n.read_port, x->n.given, x->n.stopped, (long)x->n.mem[MQ_COUNT], (long)x->n.mem[LOST]);
|
||||
if (x) printf(" stuck: node %ld p=%ld asking=%d port=%u reading=%d doing=%d op=%ld wire=%u stopped=%d await=%ld lost=%ld\n", (long)x->n.mem[ME], (long)x->n.p,
|
||||
x->n.asking, x->n.ask_port, x->n.reading, x->n.doing, (long)x->n.do_op, x->n.wire, x->n.stopped, (long)x->n.mem[AWAIT_FROM], (long)x->n.mem[LOST]);
|
||||
}
|
||||
}
|
||||
/* Send text to a node and let everything that follows from it happen. */
|
||||
/* Send text to a node and let everything that follows from it happen: until
|
||||
* nothing can; or, with nodes known to be stuck, until no other node has
|
||||
* executed and the console has been sent nothing for a while. */
|
||||
static const char *tell(v4_cell node, const char *text)
|
||||
{
|
||||
unsigned long steps = 0;
|
||||
static v4_message m;
|
||||
unsigned long steps = 0, quiet = 0;
|
||||
printed_len = 0; printed[0] = 0; printed_from = -1;
|
||||
ended = 0; ended_from = -1; ended_how = -1;
|
||||
if (!v4_message_text(&going, node, CONSOLE_ID, V4_MSG_TEXT, text, (unsigned)strlen(text))) return "(too long)";
|
||||
going_at = 0;
|
||||
while (steps < step_limit && (v4_fabric_step(&f) != 0 || any_running())) steps++;
|
||||
if (steps >= step_limit) { stuck(); return "(still running)"; }
|
||||
return printed;
|
||||
if (!v4_message_text(&m, node, CONSOLE_ID, V4_MSG_TEXT, text, (unsigned)strlen(text))) return "(too long)";
|
||||
if (v4_fabric_device_put(&f, hera_place, 1, m.word, m.word + V4_MSG_HEADER) != V4_WIRE_DONE) return "(not put)";
|
||||
while (steps < step_limit) {
|
||||
unsigned did = v4_fabric_step(&f), took = console_takes();
|
||||
int run = any_running();
|
||||
steps++;
|
||||
if (did == 0 && took == 0 && !run) return printed;
|
||||
quiet = (spinning && !run && took == 0) ? quiet + 1 : 0;
|
||||
if (quiet >= 400000ul) return printed;
|
||||
}
|
||||
stuck();
|
||||
return "(still running)";
|
||||
}
|
||||
static int told(v4_cell node, const char *text) { return tell(node, text)[0] == 0 && ended == 1 && ended_how == V4_TEXT_COMPLETED; }
|
||||
|
||||
@@ -314,7 +323,7 @@ static unsigned load(v4_cell node, const char *file)
|
||||
while ((n = next_line(&r, line)) >= 0) {
|
||||
line[n] = 0;
|
||||
(void)tell(node, line);
|
||||
if (ended != 1 || ended_how != V4_TEXT_COMPLETED) { if (!bad) { const v4_node *q = &pool[0].n; printf(" %s: \"%s\" -> \"%s\" ended=%d how=%ld; stopped=%d asking=%d port=%u reading=%d rport=%u p=%ld fault=%u@%ld\n", file, line, printed, ended, (long)ended_how, q->stopped, q->asking, q->ask_port, q->reading, q->read_port, (long)q->p, q->fault_kind, (long)q->fault_addr); } bad++; }
|
||||
if (ended != 1 || ended_how != V4_TEXT_COMPLETED) { if (!bad) { const v4_node *q = &pool[0].n; printf(" %s: \"%s\" -> \"%s\" ended=%d how=%ld; stopped=%d asking=%d port=%u doing=%d op=%ld p=%ld fault=%u@%ld\n", file, line, printed, ended, (long)ended_how, q->stopped, q->asking, q->ask_port, q->doing, (long)q->do_op, (long)q->p, q->fault_kind, (long)q->fault_addr); } bad++; }
|
||||
}
|
||||
return bad;
|
||||
}
|
||||
@@ -331,10 +340,18 @@ static v4_uheat_t clock_of(v4_cell number)
|
||||
for (k = 0; k < PLACES; k++) if (v4_fabric_node_at(&f, k) && v4_fabric_node_at(&f, k)->n.mem[ME] == number) return v4_fabric_node_at(&f, k)->es.anticlock;
|
||||
return 0;
|
||||
}
|
||||
/* 1 if the node is asleep (operation 8): it executes nothing until a message comes. */
|
||||
static int waiting(v4_cell number)
|
||||
{
|
||||
const v4_node *n = node_numbered(number);
|
||||
return n && n->reading && !n->given && n->read_port == V4_PORT_ANY;
|
||||
return n && n->doing && n->do_op == 8;
|
||||
}
|
||||
/* how many cells of messages wait on the wire going from a port of the node */
|
||||
static unsigned going_from(v4_cell number, unsigned port)
|
||||
{
|
||||
unsigned k;
|
||||
for (k = 0; k < PLACES; k++) if (v4_fabric_node_at(&f, k) && v4_fabric_node_at(&f, k)->n.mem[ME] == number && places[k].wire[port].tx) return places[k].wire[port].tx->used;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
@@ -381,18 +398,17 @@ int main(void)
|
||||
|
||||
/* ---- Hera is born empty, and takes in the nucleus ---- */
|
||||
v4_fabric_init(&f, places, PLACES);
|
||||
v4_fabric_queues(&f, queues, QUEUES);
|
||||
v4_fabric_gone_error(&f, V4_ERROR_NO_ONE);
|
||||
v4_fabric_interrupt_error(&f, V4_ERROR_INTERRUPTED);
|
||||
place = host_born(0);
|
||||
CHECK(place == 0, "a node is born");
|
||||
hera_place = (unsigned)place;
|
||||
for (i = 0; i < 4096 && pool[0].n.mem[i] == 0; i++) { }
|
||||
CHECK(i == 4096 && pool[0].n.p == PORT + (v4_cell)V4_PORT_ANY, "with nothing in it, listening at its ports");
|
||||
CHECK(v4_fabric_wire_device(&f, hera_place, 0, &kernel) && v4_fabric_wire_device(&f, hera_place, 1, &console), "the kernel is on its port 0 and the console on its port 1");
|
||||
going.count = 0; going_at = 0;
|
||||
for (i = 0; i < 400000 && v4_fabric_step(&f) != 0; i++) { }
|
||||
CHECK(given_cells == f18_count && pool[0].n.reading && pool[0].n.read_port == V4_PORT_ANY && !pool[0].n.stopped,
|
||||
"it takes in the nucleus through its port and waits for a message: %u of %u cells", given_cells, f18_count);
|
||||
CHECK(given_cells == f18_count && pool[0].n.doing && pool[0].n.do_op == 8 && !pool[0].n.stopped,
|
||||
"it takes in the nucleus through its port, a word at a time, and sleeps until a message comes: %u of %u cells", given_cells, f18_count);
|
||||
|
||||
CHECK(told(0, "10 (ME) ! 1 (CONSOLE) ! 1 1 ROUTE") && ended_from == 10, "it is told it is 10, and where the console is");
|
||||
{
|
||||
@@ -501,64 +517,78 @@ int main(void)
|
||||
CHECK(strcmp(tell(10, "(LOST) @ ."), "0 ") == 0 && strcmp(tell(12, "(LOST) @ ."), "0 ") == 0, "and no message was let go");
|
||||
CHECK(waiting(10) && waiting(11) && waiting(12) && waiting(13) && waiting(14), "all five are waiting at their ports again");
|
||||
|
||||
/* ---- Hera kills a node by number, and tells the others it is gone (MESH.md 7b) ---- */
|
||||
|
||||
/* a node is killed while its neighbour is writing to it */
|
||||
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(12, ": TO14 S\" 65 EMIT\" 14 SEND ; TO14"), "(still running)") == 0 && node_numbered(12)->asking,
|
||||
"node 12, beside it, sends it text: 14 never reads, and 12 is blocked writing to it");
|
||||
/* ---- Hera kills a node by number, and tells the others it is gone (MESH.md 7b, 7d) ----
|
||||
* A node that is stuck holds up no one: what is sent to it waits on its wire until the wire is full, and
|
||||
* is refused from then on; Hera can be typed to whatever any other node is doing, and her KILL of one
|
||||
* stuck node is not held by another (acceptance 3 and 4). */
|
||||
step_limit = 60000000ul;
|
||||
spinning = 1u << 13 | 1u << 14;
|
||||
(void)tell(14, ": SPIN BEGIN 0 UNTIL ; SPIN");
|
||||
(void)tell(13, ": SPIN BEGIN 0 UNTIL ; SPIN");
|
||||
CHECK(!waiting(14) && !waiting(13) && ended == 0, "nodes 13 and 14 are each stuck in a loop that never ends");
|
||||
CHECK(tell(12, ": TO14 S\" 65 EMIT\" 14 SEND ; TO14")[0] == 0 && ended == 1 && ended_how == V4_TEXT_COMPLETED && waiting(12) && going_from(12, 3) == 9,
|
||||
"node 12, beside 14, sends it text: the text waits on the wire, 14 never taking it, and node 12's line ends: \"%s\", %u cells", printed, going_from(12, 3));
|
||||
{
|
||||
v4_cell refused = node_numbered(12)->mem[REFUSED];
|
||||
CHECK(strstr(tell(12, ": FILL14 200 0 DO S\" 65 EMIT\" 14 SEND LOOP ; FILL14"), "Message refused") != NULL && ended == 1 && ended_how == V4_TEXT_ERROR
|
||||
&& node_numbered(12)->mem[REFUSED] == refused + 1 && going_from(12, 3) > V4_WIRE_CELLS - 9u,
|
||||
"it sends more: when the wire is full the next is refused at once, and told: \"%s\", %u cells on the wire", printed, going_from(12, 3));
|
||||
CHECK(strcmp(tell(12, "12 100 * ."), "1200 ") == 0 && waiting(12) && strcmp(tell(11, "11 100 * ."), "1100 ") == 0, "node 12 is not held, and goes on doing what it is sent; so does node 11");
|
||||
}
|
||||
CHECK(strcmp(tell(10, "1 2 + ."), "3 ") == 0, "Hera is typed to, with two nodes stuck");
|
||||
{
|
||||
unsigned born = born_count;
|
||||
const char *r = tell(10, "14 KILL 65 EMIT");
|
||||
CHECK(strstr(printed, "A") != NULL && node_numbered(14) == NULL && born_count == born, "Hera kills node 14, by its number: \"%s\"", r);
|
||||
CHECK(strcmp(r, "A") == 0 && ended == 1 && ended_how == V4_TEXT_COMPLETED && node_numbered(14) == NULL && born_count == born,
|
||||
"Hera kills node 14, by its number, with node 13 still stuck: the KILL ends, and the rest of her line is done: \"%s\"", r);
|
||||
CHECK(going_from(10, 4) == 8 && node_numbered(13) != NULL && !waiting(13), "her word to node 13 that 14 is gone waits on the wire to it: it is stuck, and she did not wait for it: %u cells", going_from(10, 4));
|
||||
}
|
||||
CHECK(!node_numbered(12)->asking && !node_numbered(12)->stopped && waiting(12), "node 12 is no longer blocked: it is waiting at its ports");
|
||||
CHECK(strstr(printed, "No one on that port") != NULL, "what it was doing ended in an error, which said so: \"%s\"", printed);
|
||||
step_limit = 30000000ul;
|
||||
CHECK(strcmp(tell(12, "12 100 * ."), "1200 ") == 0 && strcmp(tell(13, "13 100 * ."), "1300 ") == 0 && strcmp(tell(10, "1 2 + ."), "3 ") == 0, "and everything else keeps running: \"%s\"", printed);
|
||||
spinning = 1u << 13;
|
||||
CHECK(waiting(12) && waiting(11) && waiting(10), "every node that is not stuck is asleep again");
|
||||
CHECK(strcmp(tell(12, "12 100 * ."), "1200 ") == 0 && strcmp(tell(11, "11 100 * ."), "1100 ") == 0 && strcmp(tell(10, "1 2 + ."), "3 ") == 0, "and everything else keeps running: \"%s\"", printed);
|
||||
CHECK(strstr(tell(12, "5 3 PORT!"), "No one on that port") != NULL && waiting(12), "a write to the port node 14 was on is an error at once, not a wait: \"%s\"", printed);
|
||||
{
|
||||
v4_cell refused = node_numbered(12)->mem[REFUSED];
|
||||
v4_cell refused = node_numbered(12)->mem[REFUSED], r11 = node_numbered(11)->mem[REFUSED];
|
||||
CHECK(tell(12, ": AGAIN14 S\" 65 EMIT\" 14 SEND ; AGAIN14")[0] == 0 && node_numbered(12)->mem[REFUSED] == refused + 1 && waiting(12),
|
||||
"every node was told 14 is gone and forgot the way: what node 12 sends it now goes toward Hera, who has no way either, and is refused: \"%s\"", printed);
|
||||
"node 12 was told 14 is gone and forgot the way: what it sends it now goes toward Hera, who has no way either, and is refused: \"%s\"", printed);
|
||||
CHECK(tell(11, ": AGAIN14 S\" 65 EMIT\" 14 SEND ; AGAIN14")[0] == 0 && node_numbered(11)->mem[REFUSED] == r11 + 1 && waiting(11), "and so was node 11");
|
||||
CHECK(strstr(tell(10, ": H14 S\" 1\" 14 SEND ; H14"), "Argument out of range") != NULL, "Hera herself has no way to it");
|
||||
}
|
||||
|
||||
/* a node waits on one that is stuck and is not its neighbour; Hera kills the stuck one */
|
||||
step_limit = 3000000ul;
|
||||
CHECK(strcmp(tell(13, ": SPIN BEGIN 0 UNTIL ; SPIN"), "(still running)") == 0, "node 13 is stuck in a loop");
|
||||
CHECK(strcmp(tell(12, ": W13 13 AWAIT . ; W13"), "(still running)") == 0 && node_numbered(12)->mem[AWAIT_FROM] == 13 && waiting(12),
|
||||
"node 12, which is not wired to it, waits for its answer");
|
||||
(void)tell(12, ": W13 13 AWAIT . ; W13");
|
||||
CHECK(ended == 0 && node_numbered(12)->mem[AWAIT_FROM] == 13 && waiting(12), "node 12, which is not wired to node 13, waits for its answer, asleep");
|
||||
(void)tell(10, "13 KILL");
|
||||
CHECK(node_numbered(13) == NULL && strstr(printed, "Node gone") != NULL && node_numbered(12)->mem[AWAIT_FROM] == 0 && waiting(12),
|
||||
"Hera kills node 13: node 12's wait ends, with an error that says the node is gone: \"%s\"", printed);
|
||||
step_limit = 30000000ul;
|
||||
spinning = 0;
|
||||
CHECK(strcmp(tell(12, "12 100 * ."), "1200 ") == 0 && strcmp(tell(11, "11 100 * ."), "1100 ") == 0, "and the others go on");
|
||||
|
||||
/* Hera's own wait, on a node that is stuck: a line from the console ends it */
|
||||
step_limit = 3000000ul;
|
||||
CHECK(strcmp(tell(12, ": SPIN BEGIN 0 UNTIL ; SPIN"), "(still running)") == 0, "node 12 is stuck in a loop");
|
||||
CHECK(strcmp(tell(10, "12 AWAIT ."), "(still running)") == 0 && node_numbered(10)->mem[AWAIT_FROM] == 12 && waiting(10), "Hera waits for its answer, which will not come");
|
||||
spinning = 1u << 12;
|
||||
(void)tell(12, ": SPIN BEGIN 0 UNTIL ; SPIN");
|
||||
(void)tell(10, "12 AWAIT .");
|
||||
CHECK(ended == 0 && !waiting(12) && node_numbered(10)->mem[AWAIT_FROM] == 12 && waiting(10), "node 12 is stuck in a loop; Hera waits for its answer, which will not come");
|
||||
{
|
||||
const char *r = tell(10, "12 KILL 66 EMIT");
|
||||
CHECK(strstr(r, "Interrupted\n") != NULL && strstr(r, "B") != NULL && strstr(r, "Interrupted\n") < strstr(r, "B") && ended == 2,
|
||||
"a line typed at the console ends her wait, with an error that says so, and is then done: \"%s\"", r);
|
||||
CHECK(node_numbered(12) == NULL && node_numbered(10)->mem[AWAIT_FROM] == 0, "the line killed the stuck node");
|
||||
}
|
||||
step_limit = 30000000ul;
|
||||
spinning = 0;
|
||||
|
||||
/* Hera blocked writing to a node that is stuck: a line from the console lets her go (MESH.md 7b.7) */
|
||||
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(10, ": T11 S\" 1 DROP\" 11 SEND 67 EMIT ; T11"), "(still running)") == 0 && node_numbered(10)->asking,
|
||||
"Hera sends it text: its number is the higher, so she writes without looking, and is blocked");
|
||||
/* Hera sends to a node that is stuck: her line ends, and when the wire is full she is refused; she is never held */
|
||||
spinning = 1u << 11;
|
||||
(void)tell(11, ": SPIN BEGIN 0 UNTIL ; SPIN");
|
||||
CHECK(strcmp(tell(10, ": T11 S\" 1 DROP\" 11 SEND 67 EMIT ; T11"), "C") == 0 && ended == 1 && !waiting(11) && waiting(10),
|
||||
"node 11 is stuck in a loop; Hera sends it text, which waits on the wire, and the rest of her line is done: \"%s\"", printed);
|
||||
CHECK(strstr(tell(10, ": F11 200 0 DO S\" 1 DROP\" 11 SEND LOOP 67 EMIT ; F11"), "Message refused") != NULL && strstr(printed, "C") == NULL && ended_how == V4_TEXT_ERROR && waiting(10),
|
||||
"she sends more than the wire holds: the one there is no room for is refused at once, and her line ends there: \"%s\"", printed);
|
||||
{
|
||||
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"),
|
||||
"a line typed at the console ends the line that was blocked, with an error that says so, and is then done: \"%s\"", r);
|
||||
CHECK(node_numbered(11) == NULL && !node_numbered(10)->asking && waiting(10), "the line killed the stuck node, and Hera is back at her ports");
|
||||
CHECK(strcmp(r, "D") == 0 && node_numbered(11) == NULL && waiting(10), "she is typed to, and kills the stuck node: \"%s\"", r);
|
||||
}
|
||||
spinning = 0;
|
||||
CHECK(strcmp(tell(10, "1 2 + ."), "3 ") == 0, "and she goes on");
|
||||
step_limit = 30000000ul;
|
||||
|
||||
|
||||
@@ -65,6 +65,8 @@ for k in [0, 1, shallow] + list(range(ddepth - 10, ddepth + 3)):
|
||||
check('SEND with no way', k, [': SD S" 1 DROP" 5 SEND ;'] + v + ['SD', '77 .'], 'Argument out of range' if sh else None)
|
||||
check('a write to an empty port', k, v + ['5 7 PORT!', '77 .'], 'No one on that port' if sh else None)
|
||||
check('an unknown word', k, v + ['NOSUCHWORD', '77 .'], 'UNKNOWN WORD' if sh else None)
|
||||
check('a loop that prints 400 numbers', k, [': PR 400 0 DO I . LOOP ;'] + v + ['PR', '77 .'], ' 398 399 ok' if sh else None)
|
||||
check('SEND by a way with no one on the port', k, ['5 7 ROUTE', ': SP S" 1 DROP" 5 SEND ;'] + v + ['SP', '77 .'], 'No one on that port' if sh else None)
|
||||
|
||||
def nest(n, inner):
|
||||
return [': N1 %s ;' % inner] + [': N%d N%d ;' % (i, i - 1) for i in range(2, n + 1)] + ['N%d' % n]
|
||||
@@ -76,6 +78,8 @@ for n in [1, rshallow] + list(range(rdepth - 12, rdepth + 3)):
|
||||
check('WORDS, calls deep', n, nest(n, 'WORDS') + ['77 .'], '77 ok')
|
||||
check('a line typed during AWAIT, calls deep', n, nest(n, '5 AWAIT') + ['77 .', '88 .'], '77 ok')
|
||||
check('SEND with no way, calls deep', n, nest(n, 'S" 1 DROP" 5 SEND') + ['77 .'], '77 ok')
|
||||
check('a loop that prints 400 numbers, calls deep', n, nest(n, '400 0 DO I . LOOP') + ['77 .'], '77 ok')
|
||||
check('SEND by a way with no one on the port, calls deep', n, ['5 7 ROUTE'] + nest(n, 'S" 1 DROP" 5 SEND') + ['77 .'], '77 ok')
|
||||
|
||||
with ThreadPoolExecutor(max_workers=8) as pool:
|
||||
found = [f for f in pool.map(judge, cases) if f]
|
||||
|
||||
Reference in New Issue
Block a user