docs(v4.0.0): step 6e -- messages on the wire: the second attempt at the wait
MESH.md 7d: the rulings, the wire, what a node asks of the fabric, the nucleus, the products, the limits, the acceptance, and the later step of keeping the wires in the system's blocks. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
8967c93241
commit
7eb545f387
@@ -463,6 +463,8 @@ Two types, beside text (1), output (2) and done (3):
|
||||
|
||||
## 7c. The wait (ruled 2026-10-07; built, found unsound, and backed out 2026-10-08)
|
||||
|
||||
*(The second attempt is section 7d.)*
|
||||
|
||||
**This section is a record, not what the code does.** The wait was built
|
||||
(step 6d), two independent reviews in a row found it unsound, and Captain
|
||||
Bob ruled it backed out: the engine and nucleus are as they were before
|
||||
@@ -702,6 +704,212 @@ a line. POST 538 of 538, `word_count=317`,
|
||||
`-120624` and `-121117` are amd64 boots of the same code in which the
|
||||
typed session did not put the wait on a deep stack; they are kept.)
|
||||
|
||||
## 7d. Messages on the wire (ruled 2026-10-08): the second attempt at step 6d
|
||||
|
||||
**What it is for.** The same end as section 7c: a node that is stuck holds
|
||||
up no other node. Section 7c.6 and 7c.7 say why the first attempt failed.
|
||||
Three things made it unsound, and this design removes each instead of
|
||||
guarding it:
|
||||
|
||||
1. A message could be half moved: it went a word at a time, and a fault
|
||||
after the first word left both ends out of step.
|
||||
2. A node kept other nodes' messages in a ring in its own memory, which
|
||||
it had to turn; a fault in the middle left the ring in pieces, and a
|
||||
stuck neighbour's traffic could fill it.
|
||||
3. All of it ran on the stacks the node's text was using.
|
||||
|
||||
**v3's way** (`kernel/src/vm/kernel_hermes.c`, read again for this). A
|
||||
send is one call to the kernel, `sk_hermes_send_one(from, to, ...)`: it
|
||||
delivers to exactly one VM, by its id. The kernel copies the whole
|
||||
message into its own pool and puts it on the target's queue; if anything
|
||||
is full the send is refused at once and nothing has changed. A VM never
|
||||
passes a message on for another, never holds another's, and never has
|
||||
one half moved. v3 has none of the three problems.
|
||||
|
||||
**What is taken from v3, and what is not.** Taken: whole messages, moved
|
||||
by something that is not the node's own text; a send that is accepted or
|
||||
refused at once; nothing in transit held by a node. Not taken: delivery
|
||||
by id from one place that knows where every node is. A node is still
|
||||
wired only to its neighbours and messages still go from neighbour to
|
||||
neighbour, which is what a grid of nodes needs (ruling 1).
|
||||
|
||||
### 7d.1 The rulings
|
||||
|
||||
1. **The fabric moves whole messages between neighbours.** Put to Captain
|
||||
Bob with v3's way in full and with leaving step 6d undone.
|
||||
2. **A message waits on the wire, in the fabric**, not in a node. Each
|
||||
wire has a queue each way. A node holds nothing in transit.
|
||||
3. **What a text prints waits for room; everything else is refused at
|
||||
once.** `SEND`, an answer, a NACK and a GONE either go or are refused.
|
||||
Printing waits, so that output is not lost.
|
||||
4. **A wire holds 1,024 cells each way**, a build parameter, as the number
|
||||
of ports and the sizes of the stacks are.
|
||||
5. **The wires' memory is the fabric's own, for now, behind one
|
||||
interface**; given only to ports that are wired. Backing it with the
|
||||
system's blocks is a later step (7d.8).
|
||||
|
||||
The stacks stay at 32 (ruled the same day): section 7c.7 showed their
|
||||
size was never the cause.
|
||||
|
||||
### 7d.2 The wire
|
||||
|
||||
- Between two nodes that are wired, the fabric keeps two queues of whole
|
||||
messages, one each way, of `V4_WIRE_CELLS` cells (1,024).
|
||||
- A message is what section 6 says: seven words and its text, four
|
||||
characters to a word. On a wire it takes seven cells and one for each
|
||||
word of text.
|
||||
- **Putting a message on a wire, and taking one off, is each one step:
|
||||
all of it or none.** There is no state in which part of a message has
|
||||
moved.
|
||||
- A wire is full when the message offered does not fit. One with 200
|
||||
cells free takes a short message and refuses a long one.
|
||||
- A wire to a device -- the console, the kernel -- is the same to the
|
||||
node. Whatever is on the other end puts and takes whole messages
|
||||
through the fabric.
|
||||
- When a node is removed, or a wire cut, what was waiting on its wires is
|
||||
let go.
|
||||
|
||||
### 7d.3 What a node asks of the fabric
|
||||
|
||||
Addresses after the node's ports and the two words of section 7a. None
|
||||
is memory while the ports are attached. Each operation is one step of
|
||||
the engine and happens whole.
|
||||
|
||||
| Address | What |
|
||||
|---|---|
|
||||
| `base + V4_PORTS + 4` | Wire: store the number of a port. What follows is about that wire. |
|
||||
| `base + V4_PORTS + 5` | A: store a value an operation needs |
|
||||
| `base + V4_PORTS + 6` | B: store a second |
|
||||
| `base + V4_PORTS + 7` | Do: store the number of an operation |
|
||||
| `base + V4_PORTS + 8` | How it went: fetch. 0 done; 1 no room; 2 nothing on that port; 3 no such message; 4 not a message |
|
||||
| `base + V4_PORTS + 9` | Which wires have a message waiting: fetch, a bit to a port |
|
||||
|
||||
| Operation | What it does |
|
||||
|---|---|
|
||||
| 1, look | The seven words of the message at the front of the wire are copied to the word address in A. Nothing is taken. |
|
||||
| 2, take | The message is copied out, its seven words to A and its text to B, and is off the wire. |
|
||||
| 3, move | The message goes to the back of the wire of the port in A, with one fewer in its fourth word (how many more nodes may pass it on). |
|
||||
| 4, drop | The message is off the wire and gone. |
|
||||
| 5, put | The message whose seven words are at A and whose text is at B goes to the back of the wire. |
|
||||
| 6, find | The first message on the wire that is to the node in B and of the type in A becomes the one that look, take and drop are about, wherever it is in the queue. |
|
||||
| 7, next | The same, from after the one found last. |
|
||||
| 8, sleep | The node is blocked until one of its wires has a message. |
|
||||
| 9, sleep for room | The node is blocked until the wire has A cells free, or one of its wires has a message, whichever is first. |
|
||||
|
||||
- Look, take, move and drop are about the message at the front, unless
|
||||
find or next has named another.
|
||||
- Only 8 and 9 block. A node blocked there executes nothing.
|
||||
- The ports themselves stay as they are, a word at a time, for the two
|
||||
things that are not messages: a newborn node being sent its nucleus
|
||||
(section 5), and a node's requests to the kernel (section 8).
|
||||
- The looking words of section 7a, and their lower-number rule, are no
|
||||
longer used by the nucleus.
|
||||
|
||||
### 7d.4 The nucleus
|
||||
|
||||
A node keeps no messages but the one it is doing. The ring of messages
|
||||
waiting, the words that turned it, the list of NACKs owed, and
|
||||
`(GATE)` go.
|
||||
|
||||
- *With nothing to do* it sleeps (8). Woken, it looks at the front of
|
||||
each wire that has a message.
|
||||
- *For another node:* it is moved to the wire that leads there. If
|
||||
there is no way, or nothing is on that port, or the wire is full, or
|
||||
it has been passed on 16 times, it is dropped, counted in `(LOST)`,
|
||||
and a NACK is put on the wire back toward its sender. A NACK or a
|
||||
GONE that cannot go is dropped and counted; nothing is owed for it.
|
||||
- *For itself:* it is taken and dealt with as now: text is done; a
|
||||
NACK is counted; a GONE from its centre is believed.
|
||||
- *`SEND`, how text ended, a NACK, a GONE* are built in the node's own
|
||||
memory and put. Refused, a `SEND` is error 19, "Message refused"; the
|
||||
others are counted in `(LOST)`.
|
||||
- *What text prints* is put when the output buffer fills and when the
|
||||
text ends. If the wire has no room the node sleeps for room (9). Woken
|
||||
by a message instead, it passes on what is for other nodes, as above,
|
||||
and sleeps again; text for itself from its console ends the text it is
|
||||
doing in error 21, "Interrupted"; anything else for itself stays on
|
||||
the wire.
|
||||
- *`AWAIT`* sleeps (8) and, woken, passes on what is for other nodes and
|
||||
looks for what would end its wait with find and next: the answer, a
|
||||
NACK or a believed GONE about the node waited for, or text from its
|
||||
console. Other messages for itself stay on the wire, in their order.
|
||||
- *Before text begins any of these* it tries the stack it will need,
|
||||
as `(ROOM)` does now (7c.7). Between texts the rule of 28 values
|
||||
stands.
|
||||
|
||||
### 7d.5 The products
|
||||
|
||||
The lone node on the hosted and bare-metal products is given the same
|
||||
addresses by its host (`v4/system/boot.c`): its two wires are the
|
||||
console and the kernel. Nothing else changes for it: the kernel's
|
||||
requests stay a word at a time.
|
||||
|
||||
### 7d.6 What will be limits
|
||||
|
||||
- A burst to a node that is busy, beyond what its wire holds, is refused
|
||||
where today it waits.
|
||||
- An answer whose way back is full is dropped and counted, and whoever
|
||||
waits for it is not told: that wait ends as any wait on a node that
|
||||
does not answer does, by Hera's kill or from the console.
|
||||
- A text whose printing cannot reach the console, because a stuck node
|
||||
is on the way and its wire has filled, waits until that node is
|
||||
killed. It holds up no one else, and passes on meanwhile.
|
||||
- A message for a node sits at the front of a wire until that node
|
||||
takes it. A node that is stuck never does: its wires fill, and from
|
||||
then on whoever sends to it is told at once.
|
||||
|
||||
### 7d.7 Acceptance
|
||||
|
||||
1. *Whole or not at all.* At no step of the engine is part of a message
|
||||
on a wire or part of one taken.
|
||||
2. *Refused at once.* A put to a full wire, to a port with nothing on
|
||||
it, and to a wire with room, each answers in the step it is asked.
|
||||
3. *A stuck node holds up no one.* With a node stuck in a loop: every
|
||||
other node goes on doing what it is sent and passing on; what is sent
|
||||
to the stuck node is taken until its wire is full and refused from
|
||||
then on, the sender told; a node that owes it a NACK is not held.
|
||||
4. *Hera.* She can be typed to whatever any other node is doing, and
|
||||
`KILL` of one stuck node is not held by another.
|
||||
5. *Printing is not lost.* A text that prints more than a wire holds, to
|
||||
a console's node that is slow to take it, prints all of it in order.
|
||||
6. *A node removed at any step* of any message leaves every other node
|
||||
at rest and answering.
|
||||
7. *Every depth of both stacks.* Each message path -- printing, `SEND`,
|
||||
`AWAIT` and a line typed during it, passing on, a refusal -- on the
|
||||
products and on the mesh, with the data stack at every depth and from
|
||||
every depth of calls: each line typed is answered and every node comes
|
||||
back.
|
||||
8. *Order.* What one node sends another arrives in the order it was
|
||||
sent; what a text prints, and then how it ended, reach the console in
|
||||
that order.
|
||||
9. *Nothing else changes.* POST 538 of 538; the products give what the
|
||||
commit before gives on the same input, but where 7d.6 says otherwise;
|
||||
`sanitize`, `hosted-check`, lint, three boots, one hash on all six.
|
||||
|
||||
### 7d.8 A later step, not built: the wires in the system's blocks
|
||||
|
||||
Captain Bob, 2026-10-08. The system has blocks set aside for it -- the
|
||||
first 32 RAM blocks, hidden from the user, and the first 32 of every
|
||||
disk (`BLK_FORTH_SYS_RESERVED`, `BLK_DISK_SYS_RESERVED`,
|
||||
`v3/include/block_subsystem.h`) -- and each device has at its top a
|
||||
system area that starts at 128 device blocks and grows down
|
||||
(`BLK_META_FENCE_INIT`). The wires' queues could be kept in the reserved
|
||||
blocks, with a pool in that area for what does not fit: a kind of
|
||||
virtual memory for them, and messages that outlast a restart.
|
||||
|
||||
- What is there today: the 32 hidden RAM blocks would hold four queues
|
||||
of 1,024 cells at 64 bits; a unit of five nodes has sixteen.
|
||||
- What it waits on: a device v4 may write. The virtio driver writes
|
||||
(`kernel/src/virtio/virtio_blk.c`, `vblk_write`); what refuses a write
|
||||
on the v4 boot is the block subsystem, which holds a disk it does not
|
||||
know as provisional until its owner says it may be formatted
|
||||
(`blk_subsys_confirm_format`), and in v3 that owner is Artemis. v4 has
|
||||
no Artemis yet.
|
||||
- To be ruled then: what a message that outlasts a restart means; and
|
||||
what, if anything, already uses those blocks and that area.
|
||||
- What makes it possible without touching a node: ruling 5. The fabric
|
||||
reaches a queue through one interface.
|
||||
|
||||
## 8. Storage
|
||||
|
||||
**Ruled 2026-10-06 and 2026-10-07** (Captain Bob). On 2026-10-07 he found
|
||||
@@ -1326,6 +1534,9 @@ Each is tested, committed and pushed before the next.
|
||||
the hash of step 6c's mended code. The boots of `logs/20261008-01*`
|
||||
and `-06*` are of the wait as built and are kept as its record.
|
||||
|
||||
**6e. Messages on the wire (section 7d).** Ruled 2026-10-08: the
|
||||
second attempt at 6d. Not built.
|
||||
|
||||
7. **A second unit; scaling while running; sleep, wake and kill by
|
||||
command.**
|
||||
8. **The hosted product is the five nodes**, on three ISAs: acceptance 1
|
||||
|
||||
Reference in New Issue
Block a user