/* 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 */