Files
LithosAnanke/v4/include/v4/fabric.h
T
Claude 3bd81097e1 chore(license): an SPDX header on every source file that had none (LicenseRef-Starship-1.0) [skip ci]
The header scan (Captain Bob, 2026-11-10): 788 source files -- 351 with the
SL-1.0 header, 12 with the old name (docs/working/experiments, kept by
ruling), 31 under someone else's licence (tools/kconfig, Linux's, GPL-2.0:
kept), and 394 with none. Ruled: the SPDX form; capsules (.4th, .f18) and
proofs (.thy) left for a ruling of their own (mkcapsule's 64x16 block format,
content-addressed capsules).

253 files now begin
    SPDX-License-Identifier: LicenseRef-Starship-1.0
    Copyright (c) 2023-2026 Robert A. James
as a /* */ block (C, headers, assembly, linker scripts) or # lines (scripts,
Makefiles, Kconfig; below a shebang or a coding line); tools/mkcapsule.c,
which had the copyright line, gets the SPDX line only.

Not touched: docs/working/ (archive and old campaign material, as the
ruling kept the experiments); v4/system/post_cases.c (generated by
v4/tools/mkpost.py, "do not edit" -- the generator has the header).

Comments only: the kernel builds on amd64, aarch64, riscv64; make -C v4
test-32: 39 programs, 0 failures; every changed script parses.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GE3SDvQn5EoxdGufk84zz2
2026-11-10 04:12:49 +00:00

184 lines
9.8 KiB
C

/* SPDX-License-Identifier: LicenseRef-Starship-1.0
* Copyright (c) 2023-2026 Robert A. James
*/
/* fabric.h -- the nodes there are, how their ports are wired, and time
* passing for all of them at once. docs/v4.0.0/MESH.md 4.3.
*
* On the fabric every node runs at the same time and a node with nothing to
* do is blocked at a port. Here that is: every node that is awake and not
* blocked executes one instruction word; then every operation on a whole
* message that a node asked for is done (WHOLE MESSAGES, below); and then
* every word written that has a reader waiting on the other end of its wire
* is handed over. There is no choice of whose turn it is.
*
* THE GEOMETRY IS DATA. Which port connects to what is a table, and it can
* be changed while the nodes run; nodes can be added and removed while they
* run. Nothing here knows what shape the wiring makes.
*
* Nothing here knows what the nodes are for, either: no message, no
* capsule, no StarForth. And nothing here uses the C library: whoever owns
* the fabric supplies the memory for it and for each node.
*/
#ifndef V4_FABRIC_H
#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. */
typedef struct {
int (*take)(void *self, v4_cell value); /* the node wrote `value`: 1 if it is taken, 0 if not yet */
int (*give)(void *self, v4_cell *value); /* the node is reading: 1 with a word in *value, 0 if not yet */
void *self;
} v4_device;
/* One end of a wire. */
enum { V4_WIRE_NONE = 0, V4_WIRE_NODE, V4_WIRE_DEVICE };
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
* these for every node there is to be. */
typedef struct {
v4_node n;
v4_exec_state es;
v4_heat h;
} v4_fabric_node;
/* A place in the fabric: a node, or nothing. */
typedef struct {
v4_fabric_node *node; /* 0: no node here */
int asleep;
int cut; /* not 0: a wire of this node's has been cut since it was last woken by that (operation 9) */
v4_wire wire[V4_PORTS];
} v4_place;
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_fabric;
/* 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
* left there: when it is blocked writing to, or reading from, one port that
* nothing is wired to -- because nothing ever was, or because what was
* there has been removed or the wire cut -- the error `code` is raised on
* it and it goes on (node.h, v4_node_port_gone). A node whose neighbour is
* asleep waits, and so does one reading "any port". With `code` 0, which
* is how a fabric begins, nothing is raised. */
void v4_fabric_gone_error(v4_fabric *f, v4_cell code);
/* A WORD NO ONE WILL READ. The same error is raised on a node that can take
* one and is blocked writing a word to a port whose other end will not read
* it: a device that takes no words (its `take` is 0: it speaks whole
* messages only), or a node that speaks whole messages -- one that has
* asked for a whole-message operation (below) since it was born, whatever
* it is doing now: asleep at its wires, running, stuck in a loop, or put to
* sleep by the fabric. Such a node does not read words, and before this a
* word written to one blocked its writer for ever (found by the final
* review of step 6e). A node that has asked for none yet may still read:
* the writer waits for it. That is a newborn node being sent its nucleus
* a word at a time, which executes from its ports and has asked nothing of
* a wire. */
/* More room: `places`, of `capacity` no less than before, takes over; every
* node keeps its number and its wiring. */
void v4_fabric_grow(v4_fabric *f, v4_place *places, unsigned capacity);
/* A node is born (node.h, v4_node_born): empty, its ports at `port_base`,
* wired to nothing, awake and blocked reading its ports. Returns its
* number, or -1 if there is no room. */
int v4_fabric_add(v4_fabric *f, v4_fabric_node *storage, v4_cell port_base);
/* A node is removed, whatever it was doing. Every wire to it is cut; a
* neighbour blocked on one of them stays blocked, as it would on a port
* with nothing there. Returns the storage it was given, or 0. */
v4_fabric_node *v4_fabric_remove(v4_fabric *f, unsigned id);
/* The node numbered `id`, or 0 if there is none. */
v4_fabric_node *v4_fabric_node_at(const v4_fabric *f, unsigned id);
/* Wire port `pa` of node `a` to port `pb` of node `b`, or to a device; or
* cut whatever is on it. Whatever was on a port before is cut, at both
* ends. Each returns 1, or 0 if a node or a port is not there. */
int v4_fabric_wire(v4_fabric *f, unsigned a, unsigned pa, unsigned b, unsigned pb);
int v4_fabric_wire_device(v4_fabric *f, unsigned a, unsigned pa, const v4_device *device);
/* THE KERNEL ON A PORT (MESH.md 7d.5, ruled 2026-10-08). A node asks its
* kernel by words written to a port, and the kernel takes no messages: its
* port is wired to the device as above but is given no queues. Words are
* handed over as for any device; every operation on whole messages there
* answers that no one is on the port, so that a message sent to it is
* refused at once and nothing waits for it. */
int v4_fabric_wire_kernel(v4_fabric *f, unsigned a, unsigned pa, const v4_device *device);
int v4_fabric_unwire(v4_fabric *f, unsigned a, unsigned pa);
/* A node that is asleep executes nothing, and nothing is handed to it or
* taken from it. It can be put to sleep and woken at any instruction
* word. */
void v4_fabric_sleep(v4_fabric *f, unsigned id);
void v4_fabric_wake(v4_fabric *f, unsigned id);
/* 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).
* Take, move and drop are done only on the message the node last looked at
* on that wire (wire.h, THE LOOK): if the mark is at any other -- the wire
* was cut and made again since -- they answer 3 and nothing is written.
* 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. 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.
* SLEEP FOR ROOM (9) answers 0 at once if the wire it names has the room,
* and 1 at once if it can never have it. Otherwise the node sleeps until a
* message has come, as for 8, or a message has come off ANY of the wires
* that lead from it -- taken, moved on or dropped by whoever reads that wire
* -- since it was last woken that way, or one of its wires has been cut;
* then it is answered 0 if the wire it named now has the room and 1 if not,
* and it asks which wire has what (10). A node may be waiting to put on several wires at once and can name
* only one. A wake-up that finds nothing to do is answered by asking
* again.
*
* 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
* nothing can happen until something outside changes. */
unsigned v4_fabric_step(v4_fabric *f);
#endif /* V4_FABRIC_H */