feat(v4.0.0): the wire in the engine -- whole messages, put and taken in one step
Queues of whole messages on the wires between nodes (v4/wire.h), the six addresses and ten operations a node asks of the fabric (MESH.md 7d.3), and the fabric's pool of queues. Engine only: the nucleus does not use any of it yet. The ports in the host map move to BUF0_W - 140 so that the new addresses are not among the nucleus variables, with a compile-time check. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
50be766e7b
commit
d5cd64605d
+1
-1
@@ -98,7 +98,7 @@ ROOT := $(abspath $(HERE)/..)
|
||||
NUCLEUS_64 := $(ROOT)/capsules/v4/nucleus-64.f18
|
||||
NUCLEUS_32 := $(BINDIR)/nucleus-32.f18
|
||||
HOST_DEFS := -DV4_NODE_WORDS=$(HOST_WORDS) -DV4_DATA_RING=$(HOST_DATA_RING) -DV4_RET_RING=$(HOST_RET_RING)
|
||||
ENGINE_SRCS := $(addprefix $(HERE)/src/,node.c exec.c stack.c iword.c heat.c guard.c image.c fabric.c capsule.c message.c)
|
||||
ENGINE_SRCS := $(addprefix $(HERE)/src/,node.c exec.c stack.c iword.c heat.c guard.c image.c fabric.c capsule.c message.c wire.c)
|
||||
|
||||
define IMAGE_RULE
|
||||
$(BINDIR)/mkimage-$(1): $(HERE)/tools/mkimage.c $$(SRCS) $$(wildcard $(HERE)/include/v4/*.h) $(HERE)/tests/host_map.h $(HERE)/Makefile
|
||||
|
||||
+37
-1
@@ -19,6 +19,7 @@
|
||||
#define V4_FABRIC_H
|
||||
|
||||
#include "v4/exec.h"
|
||||
#include "v4/wire.h"
|
||||
|
||||
/* What is on the other end of a port that is not a node: a console, a disk,
|
||||
* the kernel that serves a node's requests. Either function may be 0. */
|
||||
@@ -35,6 +36,7 @@ typedef struct {
|
||||
int kind;
|
||||
unsigned node, port; /* V4_WIRE_NODE: the other node and its port */
|
||||
const v4_device *device; /* V4_WIRE_DEVICE */
|
||||
v4_wire_queue *rx, *tx; /* the messages that come to this end of the wire, and that go from it (7d.2); 0: none */
|
||||
} v4_wire;
|
||||
|
||||
/* A node, with what the engine needs to run it. Its owner supplies one of
|
||||
@@ -55,6 +57,9 @@ typedef struct {
|
||||
typedef struct {
|
||||
v4_place *place;
|
||||
unsigned capacity;
|
||||
v4_wire_queue *queue; /* see v4_fabric_queues; 0: none were given */
|
||||
unsigned queue_count, queue_free; /* how many, and how many are not on a wire */
|
||||
unsigned queue_first; /* the first that is not, and the index plus one; 0: none. Each links to the next by its `head`. */
|
||||
v4_cell gone_error; /* see v4_fabric_gone_error; 0: none */
|
||||
v4_cell interrupt_error; /* see v4_fabric_interrupt_error; 0: none */
|
||||
} v4_fabric;
|
||||
@@ -62,6 +67,27 @@ typedef struct {
|
||||
/* An empty fabric with room for `capacity` nodes, in `places`. */
|
||||
void v4_fabric_init(v4_fabric *f, v4_place *places, unsigned capacity);
|
||||
|
||||
/* THE WIRES' MEMORY (MESH.md 7d.2). Between two nodes that are wired, or a
|
||||
* node and a device, the fabric keeps two queues of whole messages, one each
|
||||
* way (wire.h). The owner gives it the queues: `count` of them, in `pool`,
|
||||
* before anything is wired. Wiring a port takes two, cutting it gives them
|
||||
* back, emptied, and wiring fails, changing nothing, when there are not two
|
||||
* to take (counting those a port that is wired already would give back).
|
||||
* The fabric allocates nothing.
|
||||
*
|
||||
* A fabric that was never given queues wires as it always did, and the
|
||||
* operations on a node's wires answer that no one is on the port. That is
|
||||
* how the fabrics of nothing that uses messages on the wire are made. */
|
||||
void v4_fabric_queues(v4_fabric *f, v4_wire_queue *pool, unsigned count);
|
||||
|
||||
/* What is on the other end of a device's wire, put or taken as a whole
|
||||
* message through the fabric. The device's side of `node`'s port `port`:
|
||||
* put adds a message to those coming to the node, take removes the front
|
||||
* message of those going from it. Each answers a V4_WIRE_ result, and
|
||||
* V4_WIRE_NO_ONE when the port is not wired to a device. */
|
||||
int v4_fabric_device_put(v4_fabric *f, unsigned node, unsigned port, const v4_cell *header7, const v4_cell *text);
|
||||
int v4_fabric_device_take(v4_fabric *f, unsigned node, unsigned port, v4_cell *header7, v4_cell *text, unsigned text_cap);
|
||||
|
||||
/* A PORT WITH NOTHING ON IT. On the fabric a node that writes to such a
|
||||
* port, or reads from it, waits for ever (docs/v4.0.0/MESH.md 4.1). That
|
||||
* is so here for a bare node. But a node that can take an error is not
|
||||
@@ -116,7 +142,17 @@ int v4_fabric_unwire(v4_fabric *f, unsigned a, unsigned pa);
|
||||
void v4_fabric_sleep(v4_fabric *f, unsigned id);
|
||||
void v4_fabric_wake(v4_fabric *f, unsigned id);
|
||||
|
||||
/* Time passes: every node that is awake and not blocked executes one
|
||||
/* WHOLE MESSAGES. A node that has stored an operation to Do (node.h) is
|
||||
* blocked, and in the step in which it did the operation is done, whole, and
|
||||
* the node is let go: put, take, move and drop and the rest (MESH.md 7d.3).
|
||||
* Sleep (8) and sleep for room (9) are the two that wait: the node stays
|
||||
* blocked, executing nothing, until a message has come to one of its wires
|
||||
* since it last fetched which wires have one -- or, for 9, until its wire
|
||||
* has the room. A message has "come" when a put or a move or a device put
|
||||
* put it on the back of a queue; fetching Have clears that for all of the
|
||||
* node's wires.
|
||||
*
|
||||
* Time passes: every node that is awake and not blocked executes one
|
||||
* instruction word; then every write with a reader waiting on the other end
|
||||
* of its wire is handed over, and every device is asked. Returns how many
|
||||
* instructions were executed and words handed over together: 0 means
|
||||
|
||||
+46
-3
@@ -44,6 +44,15 @@
|
||||
#endif
|
||||
#define V4_PORT_ANY V4_PORTS /* not a port: "whichever port has a neighbour writing" */
|
||||
|
||||
/* The indexes, after the ports, of what a node asks of the fabric about whole
|
||||
* messages on its wires (docs/v4.0.0/MESH.md 7d.3); see v4_node_port_attach. */
|
||||
#define V4_PORT_WIRE (V4_PORTS + 4u) /* store the number of a port: what follows is about that wire */
|
||||
#define V4_PORT_A (V4_PORTS + 5u) /* store a value an operation needs */
|
||||
#define V4_PORT_B (V4_PORTS + 6u) /* store a second */
|
||||
#define V4_PORT_DO (V4_PORTS + 7u) /* store the number of an operation: the node is blocked until it is done */
|
||||
#define V4_PORT_HOW (V4_PORTS + 8u) /* fetch how it went */
|
||||
#define V4_PORT_HAVE (V4_PORTS + 9u) /* fetch which wires have a message waiting: bit k for port k */
|
||||
|
||||
#ifndef V4_NODE_WORDS
|
||||
#define V4_NODE_WORDS 1024u
|
||||
#endif
|
||||
@@ -113,6 +122,15 @@ typedef struct {
|
||||
unsigned readers; /* bit k: the neighbour on port k is blocked reading from this node */
|
||||
unsigned since_look; /* words taken from this node since it last fetched either of those */
|
||||
|
||||
/* Whole messages on the wires (MESH.md 7d.3). See v4_node_port_attach. */
|
||||
unsigned wire; /* the port that A, B and Do are about; V4_PORTS if what was stored is not a port */
|
||||
v4_cell wire_a, wire_b; /* A and B */
|
||||
int doing; /* non-zero: blocked, having stored `do_op` to Do */
|
||||
v4_cell do_op; /* the operation */
|
||||
v4_cell how; /* how the last one went */
|
||||
unsigned have; /* which wires had a message waiting, as the fabric last said: bit k for port k */
|
||||
int have_fetched; /* non-zero: `have` has been fetched since the fabric last cleared what had arrived */
|
||||
|
||||
/* DSTACK-DEPTH and RSTACK-DEPTH (D-16). See v4_node_stack_regs_attach. */
|
||||
v4_cell dstack_reg; /* its word address, or -1 */
|
||||
v4_cell rstack_reg; /* its word address, or -1 */
|
||||
@@ -187,11 +205,24 @@ void v4_node_console_attach(v4_node *n, v4_cell addr);
|
||||
* ports have anything on them at all: bit V4_PORTS + k
|
||||
* for port k. A port with nothing on it can never
|
||||
* be read from or written to.
|
||||
* The last two are how a node looks before it writes (MESH.md 7a), as the
|
||||
* base + V4_PORTS + 4 .. + 9 whole messages on the wires (MESH.md 7d.3), the
|
||||
* indexes V4_PORT_WIRE, V4_PORT_A, V4_PORT_B, V4_PORT_DO,
|
||||
* V4_PORT_HOW and V4_PORT_HAVE:
|
||||
* Wire, A, B store: what an operation is about and
|
||||
* needs; fetch gives back what was stored
|
||||
* Do store: the number of an operation. The
|
||||
* node is blocked until whoever connects
|
||||
* its ports has done it (v4_node_doing,
|
||||
* v4_node_done); fetch gives the number
|
||||
* How fetch: how the last one went
|
||||
* Have fetch: which wires have a message
|
||||
* waiting, bit k for port k, as the fabric
|
||||
* last said (v4_node_have)
|
||||
* The two before them are how a node looks before it writes (MESH.md 7a), as the
|
||||
* F18's io register lets it. They are what whoever connects the ports last
|
||||
* said, v4_node_port_status; fetching them waits for nothing and changes
|
||||
* nothing.
|
||||
* (read only)
|
||||
* nothing. Of the whole-message addresses How and Have are read only: a
|
||||
* store to either is an address fault, as to "any port".
|
||||
* None of them is memory while the ports are attached.
|
||||
*
|
||||
* A WRITE. A store to a port does not write memory: the node keeps the
|
||||
@@ -219,6 +250,18 @@ void v4_node_port_attach(v4_node *n, v4_cell base);
|
||||
/* The neighbour has taken what the node wrote. */
|
||||
void v4_node_port_served(v4_node *n);
|
||||
|
||||
/* WHOLE MESSAGES (MESH.md 7d.3). A store to Do records the operation in
|
||||
* n->do_op and blocks the node, as a store to a port does: n->doing. For
|
||||
* whoever connects the node's ports: 1 if the node is blocked at one; the
|
||||
* operation is done, `how` is what the node will fetch from How, and the
|
||||
* node goes on from the opcode after the store; and `mask` is which wires
|
||||
* have a message waiting, as the node will fetch it from Have. Fetching
|
||||
* Have also sets n->have_fetched, for whoever connects the ports to see and
|
||||
* clear. */
|
||||
int v4_node_doing(const v4_node *n);
|
||||
void v4_node_done(v4_node *n, v4_cell how);
|
||||
void v4_node_have(v4_node *n, unsigned mask);
|
||||
|
||||
/* How many words the node has had taken since it last looked at its
|
||||
* neighbours (a fetch of either of the two status addresses). A node that
|
||||
* looks before it begins a message (MESH.md 7a) and is blocked writing with
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
/* wire.h -- a queue of whole messages, one way along a wire.
|
||||
* docs/v4.0.0/MESH.md section 7d.2.
|
||||
*
|
||||
* A message is what section 6 says: seven words, of which the seventh
|
||||
* (word 6) is how many characters of text follow, 0 to 1024, and then the
|
||||
* text, four characters to a word. On a queue it takes 7 cells and one for
|
||||
* each word of text, and the queue holds V4_WIRE_CELLS of them, going round.
|
||||
*
|
||||
* ALL OF A MESSAGE OR NONE. Every function here leaves the queue exactly as
|
||||
* it found it unless it answers V4_WIRE_DONE. There is no state in which
|
||||
* part of a message has been put on, or taken off.
|
||||
*
|
||||
* THE MARK. The one that reads a queue has a mark on it: the message that
|
||||
* look, take, move and drop are about. `mark` counts the cells from the
|
||||
* front to where that message begins; equal to `used`, it is at no message,
|
||||
* and those four answer V4_WIRE_NO_MESSAGE. A message put on the back of a
|
||||
* queue whose mark is at no message is the one the mark is then at.
|
||||
*
|
||||
* Nothing here allocates, and nothing uses the C library: whoever owns the
|
||||
* queue supplies its memory.
|
||||
*/
|
||||
#ifndef V4_WIRE_H
|
||||
#define V4_WIRE_H
|
||||
|
||||
#include "v4/cell.h"
|
||||
|
||||
#ifndef V4_WIRE_CELLS
|
||||
#define V4_WIRE_CELLS 1024
|
||||
#endif
|
||||
|
||||
#define V4_WIRE_HEADER 7u /* the words of a message that are not its text */
|
||||
|
||||
typedef struct {
|
||||
v4_cell cell[V4_WIRE_CELLS]; /* the messages, one after another, going round */
|
||||
unsigned head; /* where the front message begins */
|
||||
unsigned used; /* how many cells they take */
|
||||
unsigned mark; /* how many cells from the front the reader's mark is; == used: at no message */
|
||||
unsigned arrived; /* not 0: a message has come since the reader last asked which wires have one */
|
||||
} v4_wire_queue;
|
||||
|
||||
enum { V4_WIRE_DONE = 0, V4_WIRE_NO_ROOM = 1, V4_WIRE_NO_ONE = 2, V4_WIRE_NO_MESSAGE = 3, V4_WIRE_NOT_A_MESSAGE = 4 };
|
||||
|
||||
/* Empty, and the mark at no message. */
|
||||
void v4_wire_reset(v4_wire_queue *q);
|
||||
|
||||
/* The cells a message of `length` characters takes: 7 and a cell for each
|
||||
* four characters. 0 if `length` is not 0 to 1024: it is not a message. */
|
||||
unsigned v4_wire_cells(v4_cell length);
|
||||
|
||||
/* V4_WIRE_DONE if `cells` more would fit, V4_WIRE_NO_ROOM if not. Changes
|
||||
* nothing. */
|
||||
int v4_wire_room(const v4_wire_queue *q, unsigned cells);
|
||||
|
||||
/* The message whose seven words are at `header7` and whose text, a cell to
|
||||
* four characters, is at `text` goes on the back. V4_WIRE_NOT_A_MESSAGE if
|
||||
* word 6 is not 0 to 1024, V4_WIRE_NO_ROOM if it does not fit. */
|
||||
int v4_wire_put(v4_wire_queue *q, const v4_cell *header7, const v4_cell *text);
|
||||
|
||||
/* The mark to the front message; to the one after the marked one, or
|
||||
* V4_WIRE_NO_MESSAGE if there is none (the mark is then at no message). */
|
||||
void v4_wire_first(v4_wire_queue *q);
|
||||
int v4_wire_next(v4_wire_queue *q);
|
||||
|
||||
/* The marked message: its seven words copied out and nothing taken;
|
||||
* copied out and taken off the queue, its text to `text`, which holds
|
||||
* `text_cap` cells (V4_WIRE_NOT_A_MESSAGE if that is too few); taken off and
|
||||
* gone. After a take or a drop the mark is at the message that followed.
|
||||
* V4_WIRE_NO_MESSAGE with the mark at no message. */
|
||||
int v4_wire_look(const v4_wire_queue *q, v4_cell *header7);
|
||||
int v4_wire_take(v4_wire_queue *q, v4_cell *header7, v4_cell *text, unsigned text_cap);
|
||||
int v4_wire_drop(v4_wire_queue *q);
|
||||
|
||||
/* The marked message of `from` goes to the back of `to`, with its fourth
|
||||
* word (word 3) one fewer: how many more nodes may pass it on. V4_WIRE_NO_ROOM
|
||||
* if `to` has no room for it, or is `from`. */
|
||||
int v4_wire_move(v4_wire_queue *from, v4_wire_queue *to);
|
||||
|
||||
#endif /* V4_WIRE_H */
|
||||
+3
-3
@@ -260,7 +260,7 @@ unsigned v4_exec_step_word(v4_node *n, v4_exec_state *es, v4_heat *h)
|
||||
|
||||
/* D-14: a node that faulted with no handler has stopped. A node that
|
||||
* has written to its port is blocked until it has been served. */
|
||||
if (n->stopped || n->asking || n->reading) return 0;
|
||||
if (n->stopped || n->asking || n->reading || n->doing) return 0;
|
||||
if (es->resume && es->resume_faults != n->faults) es->resume = 0; /* it faulted while blocked: P is the handler */
|
||||
if (es->resume) {
|
||||
/* unblocked: the rest of the word it was in */
|
||||
@@ -305,8 +305,8 @@ unsigned v4_exec_step_word(v4_node *n, v4_exec_state *es, v4_heat *h)
|
||||
}
|
||||
executed++;
|
||||
|
||||
if (n->asking) {
|
||||
/* The store was to the port. The node is blocked here; what is
|
||||
if (n->asking || n->doing) {
|
||||
/* The store was to the port, or to Do. The node is blocked here; what is
|
||||
* left of the word waits until it has been served. */
|
||||
if (slot + 1 < V4_SLOT_COUNT) {
|
||||
es->resume = 1;
|
||||
|
||||
+236
-4
File diff suppressed because it is too large
Load Diff
+36
-2
@@ -87,13 +87,17 @@ void v4_node_store(v4_node *n, v4_cell addr, v4_cell value)
|
||||
* neighbour has taken it. */
|
||||
{
|
||||
int port = v4_node_port_index(n, addr);
|
||||
if (port == (int)V4_PORT_WIRE) { n->wire = (value >= 0 && value < (v4_cell)V4_PORTS) ? (unsigned)value : V4_PORTS; return; }
|
||||
if (port == (int)V4_PORT_A) { n->wire_a = value; return; }
|
||||
if (port == (int)V4_PORT_B) { n->wire_b = value; return; }
|
||||
if (port == (int)V4_PORT_DO) { n->do_op = value; n->doing = 1; return; }
|
||||
if (port >= 0 && port < (int)V4_PORTS) {
|
||||
n->request = value;
|
||||
n->ask_port = (unsigned)port;
|
||||
n->asking = 1;
|
||||
return;
|
||||
}
|
||||
if (port >= 0) { v4_node_fault(n, V4_FAULT_ADDRESS, addr); return; } /* "any" and "which port" are not written to */
|
||||
if (port >= 0) { v4_node_fault(n, V4_FAULT_ADDRESS, addr); return; } /* "any", "which port", How and Have are not written to */
|
||||
}
|
||||
/* The stack registers: a store empties the stack. -1 when not attached. */
|
||||
if (addr == n->dstack_reg) { v4_dstack_clear(&n->ds); return; }
|
||||
@@ -122,6 +126,12 @@ v4_cell v4_node_fetch(v4_node *n, v4_cell addr)
|
||||
if (port == (int)V4_PORTS + 1) return (v4_cell)n->last_from;
|
||||
if (port == (int)V4_PORTS + 2) { n->since_look = 0; return (v4_cell)n->writers; }
|
||||
if (port == (int)V4_PORTS + 3) { n->since_look = 0; return (v4_cell)n->readers; }
|
||||
if (port == (int)V4_PORT_WIRE) return (v4_cell)n->wire;
|
||||
if (port == (int)V4_PORT_A) return n->wire_a;
|
||||
if (port == (int)V4_PORT_B) return n->wire_b;
|
||||
if (port == (int)V4_PORT_DO) return n->do_op;
|
||||
if (port == (int)V4_PORT_HOW) return n->how;
|
||||
if (port == (int)V4_PORT_HAVE) { n->have_fetched = 1; return (v4_cell)n->have; }
|
||||
if (port >= 0) {
|
||||
/* the word its neighbour wrote; v4_node_read_ready has said it is there */
|
||||
if (!n->given) return 0;
|
||||
@@ -180,6 +190,14 @@ void v4_node_port_attach(v4_node *n, v4_cell base)
|
||||
n->writers = 0;
|
||||
n->readers = 0;
|
||||
n->since_look = 0;
|
||||
n->wire = 0;
|
||||
n->wire_a = 0;
|
||||
n->wire_b = 0;
|
||||
n->doing = 0;
|
||||
n->do_op = 0;
|
||||
n->how = 0;
|
||||
n->have = 0;
|
||||
n->have_fetched = 0;
|
||||
}
|
||||
|
||||
void v4_node_port_status(v4_node *n, unsigned writers, unsigned readers)
|
||||
@@ -190,7 +208,7 @@ void v4_node_port_status(v4_node *n, unsigned writers, unsigned readers)
|
||||
|
||||
int v4_node_port_index(const v4_node *n, v4_cell addr)
|
||||
{
|
||||
if (n->port < 0 || addr < n->port || addr > n->port + (v4_cell)V4_PORTS + 3) return -1;
|
||||
if (n->port < 0 || addr < n->port || addr > n->port + (v4_cell)V4_PORT_HAVE) return -1;
|
||||
return (int)(addr - n->port);
|
||||
}
|
||||
|
||||
@@ -225,6 +243,22 @@ void v4_node_port_served(v4_node *n)
|
||||
n->since_look++;
|
||||
}
|
||||
|
||||
int v4_node_doing(const v4_node *n)
|
||||
{
|
||||
return n->doing;
|
||||
}
|
||||
|
||||
void v4_node_done(v4_node *n, v4_cell how)
|
||||
{
|
||||
n->how = how;
|
||||
n->doing = 0;
|
||||
}
|
||||
|
||||
void v4_node_have(v4_node *n, unsigned mask)
|
||||
{
|
||||
n->have = mask;
|
||||
}
|
||||
|
||||
unsigned v4_node_words_since_look(const v4_node *n)
|
||||
{
|
||||
return n->since_look;
|
||||
|
||||
+124
@@ -0,0 +1,124 @@
|
||||
/* wire.c -- a queue of whole messages. See wire.h.
|
||||
*
|
||||
* Nothing here uses the C library: the bare-metal kernel links this file.
|
||||
*/
|
||||
#include "v4/wire.h"
|
||||
#include "v4/message.h"
|
||||
|
||||
void v4_wire_reset(v4_wire_queue *q)
|
||||
{
|
||||
unsigned i;
|
||||
for (i = 0; i < V4_WIRE_CELLS; i++) q->cell[i] = 0;
|
||||
q->head = 0;
|
||||
q->used = 0;
|
||||
q->mark = 0;
|
||||
q->arrived = 0;
|
||||
}
|
||||
|
||||
unsigned v4_wire_cells(v4_cell length)
|
||||
{
|
||||
if (length < 0 || length > (v4_cell)V4_MSG_MAX_CHARS) return 0;
|
||||
return V4_WIRE_HEADER + ((unsigned)length + 3u) / 4u;
|
||||
}
|
||||
|
||||
/* the cell `off` cells from the front */
|
||||
static unsigned slot(const v4_wire_queue *q, unsigned off)
|
||||
{
|
||||
unsigned s = q->head + off;
|
||||
return s >= V4_WIRE_CELLS ? s - V4_WIRE_CELLS : s;
|
||||
}
|
||||
|
||||
/* the cells the message `off` cells from the front takes */
|
||||
static unsigned size_at(const v4_wire_queue *q, unsigned off)
|
||||
{
|
||||
return v4_wire_cells(q->cell[slot(q, off + 6u)]);
|
||||
}
|
||||
|
||||
int v4_wire_room(const v4_wire_queue *q, unsigned cells)
|
||||
{
|
||||
return cells <= V4_WIRE_CELLS - q->used ? V4_WIRE_DONE : V4_WIRE_NO_ROOM;
|
||||
}
|
||||
|
||||
/* `n` cells, already checked to fit, onto the back */
|
||||
static unsigned back(v4_wire_queue *q, unsigned n)
|
||||
{
|
||||
unsigned at = q->used;
|
||||
q->used += n;
|
||||
q->arrived = 1;
|
||||
return at;
|
||||
}
|
||||
|
||||
int v4_wire_put(v4_wire_queue *q, const v4_cell *header7, const v4_cell *text)
|
||||
{
|
||||
unsigned n = v4_wire_cells(header7[6]), at, i;
|
||||
if (n == 0) return V4_WIRE_NOT_A_MESSAGE;
|
||||
if (v4_wire_room(q, n) != V4_WIRE_DONE) return V4_WIRE_NO_ROOM;
|
||||
at = back(q, n);
|
||||
for (i = 0; i < V4_WIRE_HEADER; i++) q->cell[slot(q, at + i)] = header7[i];
|
||||
for (; i < n; i++) q->cell[slot(q, at + i)] = text[i - V4_WIRE_HEADER];
|
||||
return V4_WIRE_DONE;
|
||||
}
|
||||
|
||||
void v4_wire_first(v4_wire_queue *q)
|
||||
{
|
||||
q->mark = 0;
|
||||
}
|
||||
|
||||
int v4_wire_next(v4_wire_queue *q)
|
||||
{
|
||||
if (q->mark >= q->used) return V4_WIRE_NO_MESSAGE;
|
||||
q->mark += size_at(q, q->mark);
|
||||
return q->mark < q->used ? V4_WIRE_DONE : V4_WIRE_NO_MESSAGE;
|
||||
}
|
||||
|
||||
int v4_wire_look(const v4_wire_queue *q, v4_cell *header7)
|
||||
{
|
||||
unsigned i;
|
||||
if (q->mark >= q->used) return V4_WIRE_NO_MESSAGE;
|
||||
for (i = 0; i < V4_WIRE_HEADER; i++) header7[i] = q->cell[slot(q, q->mark + i)];
|
||||
return V4_WIRE_DONE;
|
||||
}
|
||||
|
||||
/* the marked message is off the queue; the mark is at the one that followed */
|
||||
static void remove_marked(v4_wire_queue *q)
|
||||
{
|
||||
unsigned n = size_at(q, q->mark), i;
|
||||
if (q->mark == 0) {
|
||||
q->head = slot(q, n);
|
||||
} else {
|
||||
for (i = q->mark; i + n < q->used; i++) q->cell[slot(q, i)] = q->cell[slot(q, i + n)];
|
||||
}
|
||||
q->used -= n;
|
||||
}
|
||||
|
||||
int v4_wire_take(v4_wire_queue *q, v4_cell *header7, v4_cell *text, unsigned text_cap)
|
||||
{
|
||||
unsigned n, i;
|
||||
if (q->mark >= q->used) return V4_WIRE_NO_MESSAGE;
|
||||
n = size_at(q, q->mark);
|
||||
if (n - V4_WIRE_HEADER > text_cap) return V4_WIRE_NOT_A_MESSAGE;
|
||||
for (i = 0; i < V4_WIRE_HEADER; i++) header7[i] = q->cell[slot(q, q->mark + i)];
|
||||
for (; i < n; i++) text[i - V4_WIRE_HEADER] = q->cell[slot(q, q->mark + i)];
|
||||
remove_marked(q);
|
||||
return V4_WIRE_DONE;
|
||||
}
|
||||
|
||||
int v4_wire_drop(v4_wire_queue *q)
|
||||
{
|
||||
if (q->mark >= q->used) return V4_WIRE_NO_MESSAGE;
|
||||
remove_marked(q);
|
||||
return V4_WIRE_DONE;
|
||||
}
|
||||
|
||||
int v4_wire_move(v4_wire_queue *from, v4_wire_queue *to)
|
||||
{
|
||||
unsigned n, at, i;
|
||||
if (from->mark >= from->used) return V4_WIRE_NO_MESSAGE;
|
||||
n = size_at(from, from->mark);
|
||||
if (from == to || v4_wire_room(to, n) != V4_WIRE_DONE) return V4_WIRE_NO_ROOM;
|
||||
at = back(to, n);
|
||||
for (i = 0; i < n; i++) to->cell[slot(to, at + i)] = from->cell[slot(from, from->mark + i)];
|
||||
to->cell[slot(to, at + 3u)] = (v4_cell)(from->cell[slot(from, from->mark + 3u)] - 1);
|
||||
remove_marked(from);
|
||||
return V4_WIRE_DONE;
|
||||
}
|
||||
+6
-3
@@ -73,7 +73,7 @@
|
||||
#define OWED (BUF0_W - 112) /* those: for each, the node it is owed to and the node the refused message was for */
|
||||
#define ACL_HOOK (BUF0_W - 6) /* the xt of the access control recheck word, or 0 */
|
||||
#define LINE_STATUS (BUF0_W - 9) /* how the last line ended: 0 QUIT, 1 completed, 2 an error */
|
||||
#define PORT (BUF0_W - 32) /* the node's ports (node.h): V4_PORTS of them, then "any port" and "which port". Port 0 is where its requests go. */
|
||||
#define PORT (BUF0_W - 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. */
|
||||
#define CONSOLE (BUF0_W - 16) /* the node what this one prints is sent to; 0: whoever sent the text being served */
|
||||
#define ROUTE_COUNT (BUF0_W - 17) /* how many entries the table of ways holds */
|
||||
#define ROUTE_DEFAULT (BUF0_W - 18) /* the port address for a node not in the table, or 0: there is none */
|
||||
@@ -105,8 +105,11 @@
|
||||
#define LOG_LEVEL (BUF0_W - 5) /* the level at or below which a message is printed */
|
||||
#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' ten addresses and the two that follow them end below BUF0_W - 20 */
|
||||
typedef char host_map_ports_fit[(V4_PORTS <= 8u) ? 1 : -1];
|
||||
/* 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.
|
||||
* 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)
|
||||
#define QVARS (BUF0_W - 4) /* (Q): quit.v4, system.v4 and blocks.v4, 4 cells */
|
||||
|
||||
+397
-1
File diff suppressed because it is too large
Load Diff
@@ -1136,19 +1136,24 @@ int main(void)
|
||||
/* A flush of the output buffer that ends in a fault leaves the buffer full: what is printed next -- the
|
||||
* error's own message -- must not go past its end (MESH.md 7c.6). */
|
||||
{
|
||||
/* The cells just after the buffer that are still plain memory: the ports follow them (host_map.h), and a
|
||||
* store to a port is not a store to memory -- it blocks the node, which is seen below, as is the pointer
|
||||
* itself: it is past the end only if something was put there. */
|
||||
const unsigned plain = (unsigned)(PORT - (OUT_W + OUT_CELLS));
|
||||
unsigned over = 0, worst = 0, at;
|
||||
for (k = most > 6 ? most - 6 : 0; k <= most + 3; k++) {
|
||||
char line[4 * V4_DATA_DEPTH + 16];
|
||||
size_t n_at = 0;
|
||||
unsigned m;
|
||||
boot_bare();
|
||||
for (m = 0; m < 28; m++) n.mem[OUT_W + 256 + m] = 0;
|
||||
for (m = 0; m < plain; m++) n.mem[OUT_W + OUT_CELLS + m] = 0;
|
||||
for (m = 0; m < k; m++) n_at += (size_t)snprintf(line + n_at, sizeof line - n_at, "1 ");
|
||||
snprintf(line + n_at, sizeof line - n_at, "\n");
|
||||
(void)say(line);
|
||||
(void)say("WORDS\n");
|
||||
for (at = 0; at < 28; at++) if (n.mem[OUT_W + 256 + at] != 0) { over++; worst = k; break; }
|
||||
if (n.mem[OUT_PTR] > OUT_W + 256) { over++; worst = k; }
|
||||
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 */
|
||||
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);
|
||||
|
||||
@@ -0,0 +1,294 @@
|
||||
/* test_wire.c -- a queue of whole messages, one way along a wire, with no
|
||||
* node and no fabric: docs/v4.0.0/MESH.md section 7d.2.
|
||||
*
|
||||
* The first attempt at messages that wait (7c.6) moved a message a word at a
|
||||
* time and left both ends out of step when a fault came in the middle. So
|
||||
* what is tested here, over and over, is that a queue which answers anything
|
||||
* but "done" is exactly as it was.
|
||||
*/
|
||||
#include "v4/wire.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)
|
||||
|
||||
static v4_wire_queue q, other, before, before_other;
|
||||
|
||||
/* A message of `len` characters: seven words, then its text. */
|
||||
static void make(v4_cell *h, v4_cell *t, v4_cell len, v4_cell seed)
|
||||
{
|
||||
unsigned i, n = len > 0 ? (unsigned)(len + 3) / 4u : 0;
|
||||
for (i = 0; i < 7; i++) h[i] = seed * 100 + (v4_cell)i;
|
||||
h[6] = len;
|
||||
for (i = 0; i < n; i++) t[i] = seed * 1000 + 17 * (v4_cell)i + 5;
|
||||
}
|
||||
|
||||
/* 1 if the message taken back is the one made from `len` and `seed` */
|
||||
static int same(const v4_cell *h, const v4_cell *t, v4_cell len, v4_cell seed)
|
||||
{
|
||||
v4_cell wh[7], wt[V4_WIRE_CELLS];
|
||||
unsigned i, n = (unsigned)(len + 3) / 4u;
|
||||
make(wh, wt, len, seed);
|
||||
for (i = 0; i < 7; i++) if (h[i] != wh[i]) return 0;
|
||||
for (i = 0; i < n; i++) if (t[i] != wt[i]) return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void put_ok(v4_wire_queue *w, v4_cell len, v4_cell seed, int *ok)
|
||||
{
|
||||
v4_cell h[7], t[V4_WIRE_CELLS];
|
||||
make(h, t, len, seed);
|
||||
if (v4_wire_put(w, h, t) != V4_WIRE_DONE) *ok = 0;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
static const v4_cell lens[] = { 0, 1, 4, 5, 1024 };
|
||||
v4_cell h[7], t[V4_WIRE_CELLS], h2[7], t2[V4_WIRE_CELLS];
|
||||
unsigned i, k, round, count, wrapped, cap_cells;
|
||||
int ok;
|
||||
|
||||
printf("v4 wire tests: V4_CELL_BITS=%d, %d cells to a queue\n", V4_CELL_BITS, V4_WIRE_CELLS);
|
||||
|
||||
/* ---- an empty queue ---- */
|
||||
v4_wire_reset(&q);
|
||||
CHECK(v4_wire_room(&q, 1024) == V4_WIRE_DONE, "an empty queue has room for 1,024");
|
||||
CHECK(v4_wire_room(&q, 1025) == V4_WIRE_NO_ROOM, "and not for 1,025");
|
||||
memcpy(&before, &q, sizeof q);
|
||||
CHECK(v4_wire_look(&q, h) == V4_WIRE_NO_MESSAGE, "look at nothing answers 3");
|
||||
CHECK(v4_wire_take(&q, h, t, V4_WIRE_CELLS) == V4_WIRE_NO_MESSAGE, "take answers 3");
|
||||
CHECK(v4_wire_drop(&q) == V4_WIRE_NO_MESSAGE, "drop answers 3");
|
||||
CHECK(v4_wire_next(&q) == V4_WIRE_NO_MESSAGE, "next answers 3");
|
||||
CHECK(memcmp(&before, &q, sizeof q) == 0, "and none of them changed it");
|
||||
CHECK(q.mark == q.used && q.used == 0 && q.arrived == 0, "its mark is at no message, and nothing has arrived");
|
||||
|
||||
/* ---- the cells a message takes ---- */
|
||||
CHECK(v4_wire_cells(0) == 7 && v4_wire_cells(1) == 8 && v4_wire_cells(4) == 8 && v4_wire_cells(5) == 9 && v4_wire_cells(1024) == 263,
|
||||
"a message takes 7 cells and one for each four characters: 7 8 8 9 and 263");
|
||||
CHECK(v4_wire_cells(-1) == 0 && v4_wire_cells(1025) == 0, "a length of -1 or 1,025 is not a message");
|
||||
|
||||
/* ---- lengths of 0, 1, 4, 5 and 1,024, each put and taken back ---- */
|
||||
for (k = 0; k < sizeof lens / sizeof lens[0]; k++) {
|
||||
v4_wire_reset(&q);
|
||||
make(h, t, lens[k], (v4_cell)k + 1);
|
||||
CHECK(v4_wire_put(&q, h, t) == V4_WIRE_DONE, "a message of %ld characters is put", (long)lens[k]);
|
||||
CHECK(q.used == v4_wire_cells(lens[k]), "it takes %u cells: %u", v4_wire_cells(lens[k]), q.used);
|
||||
memset(h2, 0, sizeof h2); memset(t2, 0, sizeof t2);
|
||||
CHECK(v4_wire_look(&q, h2) == V4_WIRE_DONE && memcmp(h, h2, sizeof h2) == 0, "look gives its seven words");
|
||||
CHECK(q.used == v4_wire_cells(lens[k]), "and takes nothing");
|
||||
CHECK(v4_wire_take(&q, h2, t2, V4_WIRE_CELLS) == V4_WIRE_DONE && same(h2, t2, lens[k], (v4_cell)k + 1),
|
||||
"taken, its seven words and every word of its text are what was put");
|
||||
CHECK(q.used == 0 && q.mark == 0, "and the queue is empty again, its mark at no message");
|
||||
}
|
||||
|
||||
/* ---- round the end of the array: fill and empty it three times, with lengths that do not divide 1,024 ---- */
|
||||
v4_wire_reset(&q);
|
||||
wrapped = 0;
|
||||
for (round = 0; round < 3; round++) {
|
||||
static const v4_cell mix[] = { 100, 37, 333, 1, 250, 58, 0, 411 };
|
||||
v4_cell seed[200], len[200];
|
||||
count = 0;
|
||||
for (;;) {
|
||||
v4_cell l = mix[(count + round * 3) % 8];
|
||||
make(h, t, l, (v4_cell)(round * 50 + count + 1));
|
||||
if (q.head + v4_wire_cells(l) > V4_WIRE_CELLS && q.used + v4_wire_cells(l) <= V4_WIRE_CELLS) wrapped++;
|
||||
if (v4_wire_put(&q, h, t) != V4_WIRE_DONE) break;
|
||||
seed[count] = (v4_cell)(round * 50 + count + 1);
|
||||
len[count++] = l;
|
||||
}
|
||||
CHECK(count > 5 && count < 200 && q.used <= V4_WIRE_CELLS, "round %u: %u messages fill it, %u cells", round, count, q.used);
|
||||
ok = 1;
|
||||
for (i = 0; i < count; i++) {
|
||||
if (v4_wire_take(&q, h2, t2, V4_WIRE_CELLS) != V4_WIRE_DONE || !same(h2, t2, len[i], seed[i])) ok = 0;
|
||||
}
|
||||
CHECK(ok && q.used == 0, "round %u: they come out whole and in the order they went in", round);
|
||||
}
|
||||
CHECK(wrapped > 0, "and some of them went across the end of the array: %u", wrapped);
|
||||
|
||||
/* ---- a put that does not fit changes nothing ---- */
|
||||
v4_wire_reset(&q);
|
||||
ok = 1;
|
||||
put_ok(&q, 1024, 1, &ok); /* 263 */
|
||||
put_ok(&q, 1024, 2, &ok); /* 526 */
|
||||
put_ok(&q, 1024, 3, &ok); /* 789 */
|
||||
CHECK(ok && q.used == 789, "three long messages take 789 cells");
|
||||
CHECK(v4_wire_room(&q, 235) == V4_WIRE_DONE && v4_wire_room(&q, 236) == V4_WIRE_NO_ROOM, "which leaves room for 235 and not 236");
|
||||
make(h, t, 4 * (235 - 7) + 1, 9); /* 236 cells */
|
||||
memcpy(&before, &q, sizeof q);
|
||||
CHECK(v4_wire_put(&q, h, t) == V4_WIRE_NO_ROOM, "a message of 236 cells is refused, 1");
|
||||
CHECK(memcmp(&before, &q, sizeof q) == 0, "and the queue is exactly as it was: head, used, mark and every cell");
|
||||
make(h, t, 4 * (235 - 7), 9); /* 235 cells: the last that fits */
|
||||
CHECK(v4_wire_put(&q, h, t) == V4_WIRE_DONE && q.used == V4_WIRE_CELLS, "one of 235 cells fits, and fills it");
|
||||
CHECK(v4_wire_room(&q, 0) == V4_WIRE_DONE && v4_wire_room(&q, 1) == V4_WIRE_NO_ROOM, "a full queue has room for none");
|
||||
/* with 200 cells free: a short message goes, a long one is refused */
|
||||
v4_wire_reset(&q);
|
||||
ok = 1;
|
||||
put_ok(&q, 1024, 1, &ok); put_ok(&q, 1024, 2, &ok); put_ok(&q, 1024, 3, &ok);
|
||||
put_ok(&q, 4 * (35 - 7), 4, &ok);
|
||||
CHECK(ok && V4_WIRE_CELLS - q.used == 200, "a queue with 200 cells free: %u", V4_WIRE_CELLS - q.used);
|
||||
make(h, t, 4 * (201 - 7), 5);
|
||||
memcpy(&before, &q, sizeof q);
|
||||
CHECK(v4_wire_put(&q, h, t) == V4_WIRE_NO_ROOM && memcmp(&before, &q, sizeof q) == 0, "refuses a message of 201 cells and is as it was");
|
||||
make(h, t, 4 * (200 - 7), 5);
|
||||
CHECK(v4_wire_put(&q, h, t) == V4_WIRE_DONE, "and takes one of 200");
|
||||
v4_wire_reset(&q);
|
||||
put_ok(&q, 1024, 1, &ok); put_ok(&q, 1024, 2, &ok); put_ok(&q, 1024, 3, &ok); put_ok(&q, 4 * (35 - 7), 4, &ok);
|
||||
make(h, t, 40, 6);
|
||||
CHECK(v4_wire_put(&q, h, t) == V4_WIRE_DONE && q.used == 824 + v4_wire_cells(40), "and a short one, of 40 characters");
|
||||
|
||||
/* ---- not a message ---- */
|
||||
v4_wire_reset(&q);
|
||||
ok = 1;
|
||||
put_ok(&q, 100, 1, &ok); put_ok(&q, 7, 2, &ok);
|
||||
memcpy(&before, &q, sizeof q);
|
||||
make(h, t, 10, 3);
|
||||
h[6] = -1;
|
||||
CHECK(v4_wire_put(&q, h, t) == V4_WIRE_NOT_A_MESSAGE, "a length of -1 answers 4");
|
||||
CHECK(memcmp(&before, &q, sizeof q) == 0, "and changes nothing");
|
||||
h[6] = 1025;
|
||||
CHECK(v4_wire_put(&q, h, t) == V4_WIRE_NOT_A_MESSAGE, "a length of 1,025 answers 4");
|
||||
CHECK(memcmp(&before, &q, sizeof q) == 0, "and changes nothing");
|
||||
v4_wire_reset(&q);
|
||||
ok = 1;
|
||||
put_ok(&q, 1024, 1, &ok); put_ok(&q, 1024, 2, &ok); put_ok(&q, 1024, 3, &ok); put_ok(&q, 900, 4, &ok);
|
||||
memcpy(&before, &q, sizeof q);
|
||||
h[6] = 1025;
|
||||
CHECK(v4_wire_put(&q, h, t) == V4_WIRE_NOT_A_MESSAGE && memcmp(&before, &q, sizeof q) == 0,
|
||||
"a length that is not a message is 4 and not 1 even when the queue is nearly full");
|
||||
|
||||
/* ---- first and next walk the messages; take and drop of the middle one ---- */
|
||||
v4_wire_reset(&q);
|
||||
ok = 1;
|
||||
put_ok(&q, 20, 1, &ok); put_ok(&q, 33, 2, &ok); put_ok(&q, 9, 3, &ok);
|
||||
v4_wire_first(&q);
|
||||
CHECK(ok && q.mark == 0 && v4_wire_look(&q, h) == V4_WIRE_DONE && h[0] == 100, "first puts the mark at the front message");
|
||||
CHECK(v4_wire_next(&q) == V4_WIRE_DONE && v4_wire_look(&q, h) == V4_WIRE_DONE && h[0] == 200, "next, at the second");
|
||||
CHECK(v4_wire_next(&q) == V4_WIRE_DONE && v4_wire_look(&q, h) == V4_WIRE_DONE && h[0] == 300, "next, at the third");
|
||||
memcpy(&before, &q, sizeof q);
|
||||
CHECK(v4_wire_next(&q) == V4_WIRE_NO_MESSAGE, "and then 3");
|
||||
CHECK(q.mark == q.used && v4_wire_look(&q, h) == V4_WIRE_NO_MESSAGE, "the mark is then at no message");
|
||||
CHECK(v4_wire_next(&q) == V4_WIRE_NO_MESSAGE && q.mark == q.used, "and stays there");
|
||||
|
||||
v4_wire_first(&q);
|
||||
(void)v4_wire_next(&q); /* the middle one */
|
||||
CHECK(v4_wire_take(&q, h, t, V4_WIRE_CELLS) == V4_WIRE_DONE && same(h, t, 33, 2), "take of the middle one gives it");
|
||||
CHECK(q.used == v4_wire_cells(20) + v4_wire_cells(9) && v4_wire_look(&q, h) == V4_WIRE_DONE && h[0] == 300, "the mark is at the third");
|
||||
v4_wire_first(&q);
|
||||
CHECK(v4_wire_take(&q, h, t, V4_WIRE_CELLS) == V4_WIRE_DONE && same(h, t, 20, 1), "the first is still first");
|
||||
CHECK(v4_wire_take(&q, h, t, V4_WIRE_CELLS) == V4_WIRE_DONE && same(h, t, 9, 3), "and the third follows it, whole");
|
||||
CHECK(q.used == 0, "and nothing is left");
|
||||
|
||||
v4_wire_reset(&q);
|
||||
ok = 1;
|
||||
put_ok(&q, 20, 1, &ok); put_ok(&q, 33, 2, &ok); put_ok(&q, 9, 3, &ok);
|
||||
v4_wire_first(&q);
|
||||
(void)v4_wire_next(&q);
|
||||
CHECK(v4_wire_drop(&q) == V4_WIRE_DONE && v4_wire_look(&q, h) == V4_WIRE_DONE && h[0] == 300, "drop of the middle one: the mark is at the third");
|
||||
v4_wire_first(&q);
|
||||
CHECK(v4_wire_take(&q, h, t, V4_WIRE_CELLS) == V4_WIRE_DONE && same(h, t, 20, 1) && v4_wire_take(&q, h, t, V4_WIRE_CELLS) == V4_WIRE_DONE && same(h, t, 9, 3) && q.used == 0,
|
||||
"the first and third are left, in order");
|
||||
/* the last one, dropped, leaves the mark at no message */
|
||||
ok = 1;
|
||||
put_ok(&q, 20, 1, &ok); put_ok(&q, 33, 2, &ok);
|
||||
v4_wire_first(&q);
|
||||
(void)v4_wire_next(&q);
|
||||
CHECK(v4_wire_drop(&q) == V4_WIRE_DONE && q.mark == q.used && q.used == v4_wire_cells(20), "drop of the last: the mark is at no message");
|
||||
|
||||
/* middle take across the end of the array */
|
||||
v4_wire_reset(&q);
|
||||
ok = 1;
|
||||
put_ok(&q, 1024, 1, &ok); put_ok(&q, 1024, 2, &ok); put_ok(&q, 1024, 3, &ok);
|
||||
v4_wire_first(&q);
|
||||
(void)v4_wire_take(&q, h, t, V4_WIRE_CELLS); /* head is at 263 */
|
||||
put_ok(&q, 1024, 4, &ok); put_ok(&q, 77, 5, &ok); /* the last of them goes round */
|
||||
CHECK(ok && q.head + q.used > V4_WIRE_CELLS, "messages are across the end of the array");
|
||||
v4_wire_first(&q);
|
||||
(void)v4_wire_next(&q); (void)v4_wire_next(&q); /* the 1,024 that came last but one */
|
||||
CHECK(v4_wire_take(&q, h, t, V4_WIRE_CELLS) == V4_WIRE_DONE && same(h, t, 1024, 4), "the middle one is taken, and the one after it moves down across the end");
|
||||
CHECK(v4_wire_look(&q, h) == V4_WIRE_DONE && h[0] == 500, "the mark is at the one that followed it");
|
||||
v4_wire_first(&q);
|
||||
ok = 1;
|
||||
ok &= v4_wire_take(&q, h, t, V4_WIRE_CELLS) == V4_WIRE_DONE && same(h, t, 1024, 2);
|
||||
ok &= v4_wire_take(&q, h, t, V4_WIRE_CELLS) == V4_WIRE_DONE && same(h, t, 1024, 3);
|
||||
ok &= v4_wire_take(&q, h, t, V4_WIRE_CELLS) == V4_WIRE_DONE && same(h, t, 77, 5);
|
||||
CHECK(ok && q.used == 0, "and the others come out whole, in order");
|
||||
|
||||
/* ---- move ---- */
|
||||
v4_wire_reset(&q); v4_wire_reset(&other);
|
||||
ok = 1;
|
||||
put_ok(&q, 20, 1, &ok); put_ok(&q, 33, 2, &ok); put_ok(&other, 5, 7, &ok);
|
||||
other.arrived = 0; q.arrived = 0;
|
||||
v4_wire_first(&q);
|
||||
(void)v4_wire_next(&q);
|
||||
CHECK(ok && v4_wire_move(&q, &other) == V4_WIRE_DONE, "the marked message moves to another queue");
|
||||
CHECK(other.arrived != 0, "which has had a message arrive");
|
||||
CHECK(v4_wire_look(&q, h) == V4_WIRE_NO_MESSAGE && q.used == v4_wire_cells(20), "it is off the first, whose mark is at no message");
|
||||
v4_wire_first(&other);
|
||||
CHECK(v4_wire_take(&other, h, t, V4_WIRE_CELLS) == V4_WIRE_DONE && same(h, t, 5, 7), "the other's own message is first");
|
||||
CHECK(v4_wire_take(&other, h, t, V4_WIRE_CELLS) == V4_WIRE_DONE && h[3] == 203 - 1 && h[0] == 200 && h[6] == 33 && h[5] == 205,
|
||||
"and the moved one is at the back, its fourth word one fewer and the rest as they were");
|
||||
{
|
||||
v4_cell wh[7], wt[V4_WIRE_CELLS];
|
||||
unsigned n;
|
||||
make(wh, wt, 33, 2);
|
||||
for (n = 0; n < 9 && t[n] == wt[n]; n++) { }
|
||||
CHECK(n == 9, "and every word of its text");
|
||||
}
|
||||
/* no room in the other: 1, and neither changes */
|
||||
v4_wire_reset(&q); v4_wire_reset(&other);
|
||||
ok = 1;
|
||||
put_ok(&q, 100, 1, &ok);
|
||||
put_ok(&other, 1024, 2, &ok); put_ok(&other, 1024, 3, &ok); put_ok(&other, 1024, 4, &ok);
|
||||
put_ok(&other, 4 * (226 - 7), 5, &ok);
|
||||
v4_wire_first(&q);
|
||||
memcpy(&before, &q, sizeof q); memcpy(&before_other, &other, sizeof other);
|
||||
CHECK(ok && V4_WIRE_CELLS - other.used < v4_wire_cells(100), "the other has %u cells free, a message of 100 characters takes %u", V4_WIRE_CELLS - other.used, v4_wire_cells(100));
|
||||
CHECK(v4_wire_move(&q, &other) == V4_WIRE_NO_ROOM, "move to a queue with no room answers 1");
|
||||
CHECK(memcmp(&before, &q, sizeof q) == 0 && memcmp(&before_other, &other, sizeof other) == 0, "and changes neither");
|
||||
v4_wire_reset(&other);
|
||||
v4_wire_reset(&q);
|
||||
CHECK(v4_wire_move(&q, &other) == V4_WIRE_NO_MESSAGE && other.used == 0, "move with nothing marked answers 3");
|
||||
|
||||
/* ---- take with too little room for the text ---- */
|
||||
v4_wire_reset(&q);
|
||||
ok = 1;
|
||||
put_ok(&q, 100, 1, &ok); /* 25 cells of text */
|
||||
memcpy(&before, &q, sizeof q);
|
||||
memset(h2, 0, sizeof h2); memset(t2, 0, sizeof t2);
|
||||
cap_cells = 24;
|
||||
CHECK(v4_wire_take(&q, h2, t2, cap_cells) == V4_WIRE_NOT_A_MESSAGE, "take with room for 24 cells of a text of 25 answers 4");
|
||||
CHECK(memcmp(&before, &q, sizeof q) == 0, "the queue is as it was");
|
||||
ok = 1;
|
||||
for (i = 0; i < 7; i++) if (h2[i] != 0) ok = 0;
|
||||
for (i = 0; i < 25; i++) if (t2[i] != 0) ok = 0;
|
||||
CHECK(ok, "and nothing was copied out");
|
||||
CHECK(v4_wire_take(&q, h2, t2, 25) == V4_WIRE_DONE && same(h2, t2, 100, 1), "with room for 25 it is taken");
|
||||
v4_wire_reset(&q);
|
||||
ok = 1;
|
||||
put_ok(&q, 0, 1, &ok);
|
||||
CHECK(ok && v4_wire_take(&q, h2, 0, 0) == V4_WIRE_DONE, "a message with no text needs no room for any");
|
||||
|
||||
/* ---- arrived ---- */
|
||||
v4_wire_reset(&q); v4_wire_reset(&other);
|
||||
CHECK(q.arrived == 0, "a queue that has had nothing has had nothing arrive");
|
||||
ok = 1;
|
||||
put_ok(&q, 4, 1, &ok);
|
||||
CHECK(ok && q.arrived != 0, "a put sets arrived");
|
||||
q.arrived = 0;
|
||||
make(h, t, 4, 2);
|
||||
h[6] = 2000;
|
||||
(void)v4_wire_put(&q, h, t);
|
||||
CHECK(q.arrived == 0, "a put that is refused does not");
|
||||
put_ok(&other, 4, 3, &ok);
|
||||
other.arrived = 0;
|
||||
v4_wire_first(&other);
|
||||
CHECK(v4_wire_move(&other, &q) == V4_WIRE_DONE && q.arrived != 0, "nor does a move into it, when it is done, fail to");
|
||||
q.arrived = 0;
|
||||
v4_wire_first(&q);
|
||||
(void)v4_wire_take(&q, h, t, V4_WIRE_CELLS);
|
||||
CHECK(q.arrived == 0, "a take does not set it");
|
||||
|
||||
printf(" %d checks, %d failures\n", checks, failures);
|
||||
return failures != 0;
|
||||
}
|
||||
Reference in New Issue
Block a user