diff --git a/capsules/v4/nucleus-64.f18 b/capsules/v4/nucleus-64.f18 index 78a56597..6f818911 100644 Binary files a/capsules/v4/nucleus-64.f18 and b/capsules/v4/nucleus-64.f18 differ diff --git a/docs/v4.0.0/MESH.md b/docs/v4.0.0/MESH.md index 73d98380..c29e8cc5 100644 --- a/docs/v4.0.0/MESH.md +++ b/docs/v4.0.0/MESH.md @@ -104,10 +104,6 @@ word address `base`: | `base` … `base + V4_PORTS − 1` | Port 0 … port `V4_PORTS − 1` | | `base + V4_PORTS` | Any port: a read here takes from whichever port has a neighbour writing | | `base + V4_PORTS + 1` | Which port the last read from "any" came from. Read only. | -| `base + V4_PORTS + 2`, `+ 3` | Who is waiting to write to this node, and who to read from it: section 7a | -| `base + V4_PORTS + 4 + k` | The offer on port *k*: section 7c | -| `base + 2 * V4_PORTS + 4` | The wait: section 7c | -| `base + 2 * V4_PORTS + 5` | The wait for an offer only: section 7c | - **A write to a port blocks the node until the neighbour has read it.** Built 2026-10-05 for one port; the opcode after the write runs when the @@ -244,9 +240,9 @@ and these tables. Nothing else changes. ## 7a. Two neighbours writing to each other (ruled 2026-10-06) -*(The lower-number rule and the looking again of this section are -replaced by the wait, section 7c, ruled 2026-10-07. The messages waiting, -and what flooding costs, stand.)* +*(The wait of section 7c was to replace the lower-number rule and the +looking again of this section. It was backed out on 2026-10-08; this +section is what the code does.)* The fault is in section 10, step 4. **Ruled: a node writes only to a neighbour that is already reading; a node keeps the messages it takes in @@ -295,10 +291,8 @@ part of wiring them. Put to Captain Bob after it was built, and ruled: **What it costs.** A node that is flooded loses messages, of every kind: text for it, answers to text it sent, and messages it was only passing -on. In the test three nodes send each other 800 messages at once; 278 -arrive (287 as first built, before step 6d) and the nodes count some -1,400 let go (the count is of every kind of message, and since step 6d -counts some more than once; it includes answers and text +on. In the test three nodes send each other 800 messages at once; 287 +arrive and 714 messages are let go (the count includes answers and text that was to start a node sending). Six from each to each, at once, all arrive. Nothing here makes a sender slow down or send again; that is kernel-Hermes's work in v3 and is not decided for the mesh. @@ -310,8 +304,7 @@ kernel-Hermes's work in v3 and is not decided for the mesh. gone; and a wait on a node that is alive and stuck is ended by Hera's killing that node, or from the console. A message that is refused is told to whoever waits on it, while the node that refuses it has room to -remember that it owes the telling.** (Since step 6d, section 7c, a node -that is stuck also holds up no other node.) As first written the rule was "no +remember that it owes the telling.** As first written the rule was "no message is lost without its sender being told, and no node waits for ever"; the review showed that was more than what was built could keep, and section 7b.7 says exactly where it falls short. It closes two defects @@ -378,12 +371,12 @@ Two types, beside text (1), output (2) and done (3): - A NACK owed to this node itself -- its own message came back to it and was let go here -- is put with its messages waiting, as if it had come by a port. -- Paying one: it is offered on the port it goes by, with everything else - the node has to send, and the node sleeps in the wait (section 7c). One - there is no way for, or whose way is a port with nothing on it, is - dropped and counted. (As first mended it was written without looking to - a higher-numbered neighbour and kept back from a lower; step 6d - replaced that.) +- Paying one: if the neighbour it goes by is reading, it is sent. If not, + and that neighbour's number is the higher, it is written all the same, + as any message to it is (section 7a). If its number is the lower, the + NACK stays owed; the node goes on taking in what is written to it and + tries again, and does not sleep while it owes one. One there is no way + for, or whose way is a port with nothing on it, is dropped and counted. - A message is passed on by at most 16 nodes. Its fourth word, the TTL, carries how many more may pass it on; the node that would be the sixteenth refuses it as it would one it had no way for. (Ruled @@ -429,11 +422,14 @@ Two types, beside text (1), output (2) and done (3): as usual. - On the two products there is one node, and nobody but the console could ever answer it: a line typed while it waits does the same. -- **A blocked write (ruled 2026-10-07; replaced by section 7c the same - day).** As first mended the fabric raised error 21 on a node blocked on - the first word of a message while its console had a line for it. Since - step 6d no node is blocked on a first word: it offers it and sleeps, - and a console line reaches it as any message does (7c.3). +- **A blocked write (ruled 2026-10-07).** Hera has the lowest number, so + she writes to a neighbour without looking and is blocked until it reads + (section 7a); if it never does, nothing could be typed to her again. So + when a node is blocked writing the first word of a message to another + node, and its console has a line for it, the fabric raises error 21 on + it: it gives that message up and goes on to read the line. Only on the + first word, so that half a message is never left on a wire + (`v4_fabric_interrupt_error`, `v4/include/v4/fabric.h`). - **A node waiting to write to a neighbour that has been removed** is told: the word that says which neighbours are reading also says which ports have anything on them at all, and with nothing there the wait ends in @@ -447,14 +443,32 @@ Two types, beside text (1), output (2) and done (3): console. v3's pool is finite too, 32 messages, and a send beyond it is refused. - A node whose neighbour is asleep waits until it is woken (section 4.1). -- ~~A node that is stuck can hold up its neighbours until it is killed.~~ - **Closed by step 6d (section 7c).** As step 6c left it, a node that - owed a stuck neighbour a NACK, or had a message for it, was blocked or - went round until Hera killed the stuck node; and a node waiting for an - answer kept back what was passing through it. +- **A node that is stuck can hold up its neighbours until it is killed.** + A node cannot tell a neighbour that is busy for a moment from one that + is stuck for good, and there is no clock (ruling 2). So: + - a node that owes a NACK to a stuck neighbour whose number is the + higher is blocked writing it; + - a node that owes one to a stuck neighbour whose number is the lower + goes on taking in what lower-numbered neighbours write to it, but does + not sleep, and a higher-numbered neighbour with something for it + waits; + - a node with a message for a stuck neighbour waits to write it. + + Each ends when Hera kills the stuck node: the wire is then empty, the + waiting node is told so (error 18) and goes on. Hera herself can always + be typed to (7b.6). None of this can happen on a node that is gone. +- A node that is waiting for an answer keeps what comes for other nodes + and passes it on when its wait ends, not before. - A node that was never told who is on its centre's port believes no GONE. -## 7c. The wait (ruled 2026-10-07) +## 7c. The wait (ruled 2026-10-07; built, found unsound, and backed out 2026-10-08) + +**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 +it, and section 7b.7's limit stands. What is below is the design as it +was approved; 7c.6 says what happened to it and what a second attempt +must deal with first. **What it closes.** Section 7b.7's limit: a node with something for a neighbour that was not reading could only write anyway, and be blocked, or @@ -514,29 +528,13 @@ a node may offer a word on each of its ports at once.)* the fabric has its offer taken, and the other gets the word (case 2 for the one, case 1 for the other). - Whichever it is, every offer the node had is withdrawn. -- **A store to the wait withdraws every offer** and waits for nothing. - *(Added after the review, 2026-10-08: a node that goes round without - sleeping must not leave an old offer standing for its next wait to - find.)* -- **The wait for an offer only**, `base + 2 * V4_PORTS + 5`: as the wait, - but a word that comes for the node does not end it, and a writer to the - node is not served. It ends when the offer is taken, or when there - turns out to be nothing on the port. *(Added after the review: it is - how the words of a message after the first are written where no error - must come.)* - A node in the wait that is handed a word by case 2 of another node gets it by its own case 1. - A node in the wait executes nothing and spends no heat. It shows to its neighbours as waiting to read. -- With nothing on a port it has an offer on -- because nothing ever was, - or because what was there has been removed -- a node that can take an - error is woken: the fetch gives 0 and "which port" gives - `2 * V4_PORTS + k`. No error is raised: the node may be in the middle of - something that has nothing to do with that port. A bare node waits. A - neighbour that is asleep takes nothing: the offer stands. *(Changed - while it was built: as approved, error 18 was raised. That ended the - text of a node that merely had a message queued for a neighbour that - was then killed.)* +- With nothing on a port it has an offer on, a node that can take an error + has error 18 raised on it (section 4.1); a bare node waits. A neighbour that + is asleep takes nothing: the offer stands. - `v4_fabric_interrupt_error`, a device's `pending`, and `v4_node_words_since_look` are removed. - The lone node of the two products (`v4/system/boot.c`) has the same @@ -545,74 +543,30 @@ a node may offer a word on each of its ports at once.)* ### 7c.3 The nucleus -*(Rewritten 2026-10-08 as built. The first form had the node passing a -message on by beginning it at once, as its own text does. That held the -node passing on whenever the node it was for was stuck: text typed for a -stuck node left Hera deaf. So what is passed on waits, offered.)* - -- *Beginning a message from text* (`(GATE)`): offer its first word; if a - word comes instead, take that message in and offer again; when the - offer is taken, write the rest, each word waiting for the neighbour, - which has taken the first and reads to the end. With nothing on the - port it is error 18. -- *Text from its console for a node whose text is waiting there* ends that - text in error 21, "Interrupted", and is then done. This is how Hera is - typed to while her line waits on a stuck node. -- *A node with nothing to do* (`(IDLE)`) deals first with what is for - itself, the oldest first. With nothing for itself it offers, all at - once, the first message it has for each port and each NACK it owes, and - sleeps in the wait. What is taken is written and done with; the rest - stays with the messages waiting, in the order it came. A message for a - stuck node waits there and holds up nothing. -- *What a node sends other than from text* goes the same way: what a - finished text printed (type 2), how it ended (type 3), and a GONE. Each - is put with the messages waiting and goes when it can; with no room for - it, it is let go and counted. So a node that did text for a sender that then stuck - is not held by it, and `KILL` is not held by one stuck node from telling - the next. -- *Order.* A node's own messages keep the order they were made in. A node - does not begin another text from a sender while what it owes that - sender from an earlier one -- what that text printed, how it ended -- is - still with it: so what one sender is told comes in the order its texts - were done. A GONE it is sending holds nothing back. It can note eight - senders so; past that, nothing more is held. -- *No error in the middle.* The words of a message after the first are - written, when a message is passed on or a NACK paid, through the wait - for an offer only. If the reader has been removed the rest is let go - and counted; no error is raised, for an error there would leave the - messages waiting in pieces. For the same reason a text may leave no - more than 28 values on the stack: one that leaves more ends "Stack - overflow". And a message half taken in when its writer is removed is - let go, not kept. -- *A node waiting for an answer* (`AWAIT`) does all of this but begin - text: it looks through the messages waiting for what would end its - wait, offers what is to be passed on and what it owes, and sleeps in - the wait. It no longer keeps back what passes through it. +- *Beginning a message* (`(GATE)`): offer its first word; if a word comes + instead, take that message in and offer again; when the offer is taken, + write the rest, each word waiting for the neighbour, which has taken the + first and reads to the end. +- *Paying what is owed* (`(PAY)`): every NACK owed is offered at once, + each on the port it goes by (one to a port). A node with nothing to do + that owes one sleeps in the wait; it no longer goes round, and it no + longer writes one without looking. +- *Text from its console for a node that is waiting to begin a message* + ends what the node was doing in error 21, "Interrupted", and is then + done, as in `AWAIT` (7b.6). This is how Hera is typed to while she waits + on a stuck node. - `NEIGHBOUR` stays: a node must know who is on its centre's port to believe a GONE (7b.4). It is no longer needed to write. ### 7c.4 What is still a limit -- A node's own text that must begin a message (`SEND`, or what it prints) - to a stuck node waits until Hera kills that node or, on Hera, a line is - typed. It takes in everything sent to it meanwhile, but passes nothing - on until its text ends. -- A line typed at Hera while her text is waiting to begin a message ends - that text, whether the node it waits on is stuck or only busy. -- While what a node owes a sender -- or the console -- cannot go, because - the way there is through a stuck node, it begins no more text from that - sender. It goes on passing on, paying, and doing text from others, and - when the stuck node is killed it lets go what could not go and does - what it kept back, in order. -- What text prints while it is still running, when the output buffer - fills, is begun as a message from text is, and waits as that does. -- An answer that reaches a waiting node whose messages waiting are full - is refused, and the wait ends "Message refused". +- A node's own line that must begin a message to a stuck node waits until + Hera kills that node or, on Hera, a line is typed. It takes in + everything sent to it meanwhile, and holds up no one. +- A node in `AWAIT` keeps what comes for other nodes, and what it owes, + until its wait ends (7b.7). - A message once begun is written to its end. A node that takes a first word and is then removed leaves its writer with error 18. -- The messages waiting are finite, 400 cells: a node that is passing on - for a stuck node keeps what is for it until that node is killed, and - with no room left refuses what comes, telling the sender. ### 7c.5 Acceptance @@ -630,13 +584,68 @@ stuck node left Hera deaf. So what is passed on waits, offered.)* 5. *Hera.* Waiting to begin a message to a stuck node, she is typed to: the line that waited ends "Interrupted", and the new line, which kills the stuck node, is done. -6. *An empty port.* Text that begins a message on a port with nothing on - it ends in error 18. +6. *An empty port.* An offer to a port with nothing on it is error 18. 7. *Nothing deadlocks.* The flood of section 7a's test, 800 messages among three nodes, comes to rest. 8. *Nothing else changes.* POST 538 of 538, the existing tests, and one hash on all six systems; `sanitize`, `hosted-check`, lint, three boots. +### 7c.6 What happened (2026-10-08) + +Built in commits `075385ab` to `2cf37aab`; the code is in history there, +with its tests (`v4/tests/test_host_mesh.c` at `2cf37aab` has a scene for +each defect below). Backed out by restoring the engine, nucleus and tests +to `dfabfa46`. + +**What it grew into.** The approved design had a node pass a message on +by beginning it at once. That held a node passing on for a stuck node, +so what is passed on, how text ended, what a finished text printed, and +a GONE all came to wait with the messages waiting, offered; `AWAIT` came +to pass on and pay while it waited; an offer on an empty port came to +wake the node instead of raising an error; and a second wait, for an +offer only, was added so that the words after a message's first could be +written without an error. + +**What the two reviews found**, after each of which it was mended and +still not sound: +- Passes over the messages waiting were left in pieces by any fault or + error in the middle of one: a stack overflow, or a neighbour removed. + A node then went round for ever. +- Offers left standing sent a text's message to the wrong port. +- What a finished text printed held the node when the way to the console + was through a stuck node. +- A node with many messages of its own waiting began no text. +- On the products, a line leaving 29 values on the stack hung the node; + and after that was mended, `WORDS` with 26 values on the stack did. +- A stack fault after a neighbour had taken a message's first word left + the two out of step for good, and hung the neighbour. +- `AWAIT` on a deep stack, a refusal with 28 values waiting, output out + of order, and a neighbour removed in mid-message ending an unrelated + wait. + +**The cause they share.** A node's message machinery runs on the node's +own two stacks, the ones its text is using, and a fault abandons +whatever was in progress. The wait put more of that machinery, needing +more of the stack, into more places: between texts, inside `AWAIT`, and +wherever text prints. The tests ran those paths on a shallow stack with +every node present. + +**What a second attempt must settle before anything is built.** +1. What a node does when a fault or an error comes in the middle of + writing or reading a message, on both ends of the wire, at every word. +2. How much of each stack the machinery may use, and where that is + checked, so that text which leaves too little ends in an error before + anything is on a wire. +3. Tests that run every message path at every depth of both stacks, and + with a node removed at every stage of a message, on writer's and + reader's side; and the products compared with the commit before on the + same input. +4. Whether to keep the messages a node passes on in the node at all, or + to do as v3 does: the kernel queues, and a send never blocks. + +Two guards from this are kept in `hosted-check`: a line that leaves 29 +values on the stack, and `WORDS` with 26 values on it. + ## 8. Storage **Ruled 2026-10-06 and 2026-10-07** (Captain Bob). On 2026-10-07 he found @@ -1249,75 +1258,17 @@ Each is tested, committed and pushed before the next. 8. *Nothing else changes.* POST 538 of 538, the existing tests, and the same hashes on all six systems. - **6d. The wait (section 7c).** Ruled 2026-10-07. **Built 2026-10-08.** - - *The engine* (`v4/src/node.c`, `fabric.c`): the offer addresses and - the wait; the fabric hands an offer over in one step; a node told - there is nothing on a port it offered on. Removed: the console - release of 7b.6, a device's `pending`, the count of words since a - look. - - *The nucleus* (`v4/capsule/core.v4`, `quit.v4`): `(OFFERS)`, `(GATE)`; - `(IDLE)` as in 7c.3, with `(MINE?)`, `(FWD-SET)`, `(FWD-GO)`, - `(PAY-SET)`, `(PAY-GO)`; `(FINISH)` and `GONE` put what they send with - the messages waiting; `AWAIT` goes on passing on and paying. The - lower-number rule, the looking again, `(PASS-ON)` and `(PAY)` are - gone. The ports moved in the nucleus's map to make room. - - *The lone node* (`v4/system/boot.c`): an offer is served as a write. - - *Found while it was built, and mended, each with a check that failed - first:* a message passed on toward a stuck node held the node passing - it on (7c.3); a node waited on a stuck sender to give it its answer; - with answers queued, a line typed during a wait was done before the - earlier line's answer had gone; a node with two answers it could not - give began no text; `KILL` waited to tell a stuck node, and a typed - line then left the rest untold; removing a node raised "No one on - that port" on the unrelated text of a neighbour with something - queued for it (7c.2). Two commits on the way were wrong as pushed: - `482cc75f` (the first of these) and `1c720ab2` (pushed with - `hosted-check` failing); `35378372` and `ec817a82` mend them. - - *Tests.* `test_fabric.c` 104 checks: acceptance 1, 2, 3, 6, and the - ring, the same-step and the asleep cases. `test_host_mesh.c` 127: - acceptance 4 for a creditor of either number, two refusals owed, - order kept through a node passing on, a waiting node passing on, a - line that waits and is typed to, and 7. `test_host_unit.c` 84: - acceptance 5; a node with answers for two stuck senders; `KILL` with - another node stuck. - - *Verified.* `make -C v4 test` and `sanitize` at both widths, - `hosted-check`, lint; three bare-metal boots, `logs/20261008-011558` - (amd64), `-011833` (aarch64), `-012159` (riscv64). On all six: POST - 538 of 538, `word_count=317`, `dict_hash=0x6a39c0bb9d183418`. - - **The review (2026-10-08) found the step not sound as built**, and - Captain Bob ruled the mends (A, B, C, D and the guard: 7c.2, 7c.3). - Each has a scene in `test_host_mesh.c`, on a row of three begun - again, that failed first: - - *A line that left 29 values on the stack hung the node for ever*, - on the products too. It ends "Stack overflow" - (`test_host_quit.c`, `hosted-check`, and typed in the boots). - - *A node removed while a message was being passed on to it* left the - node passing it on going round for ever. The rest is let go. - - *Offers left standing:* text meant for one node was done by - another. A store to the wait withdraws them. - - *What a finished text printed* held the whole node when the way to - the console was through a stuck node. It is queued. - - *More than eight of a node's own messages waiting* locked out all - text, Hera's `KILL` included. Only what a sender is owed holds that - sender's text back. - - *A message half taken in when its writer was removed* was kept as - if whole. It is let go. - - Smaller: no value is left on the stack when a port goes in - mid-refusal; a refused line no longer ends a waiting text - "Interrupted"; a node's own messages may be passed on by 16 nodes. - - *Not mended, small:* with no console told of, a second text from - whoever sent the one being done interrupts it, as since step 6c; - refusals to one node go newest first; `(LOST)` counts some messages - more than once, so the flood's check is "it comes to rest", not an - exact count; the ring of three is tested in the engine only; the - five-node test is not run at 32 bits. - - *Verified, on the code as mended.* `test_fabric.c` 112 checks, - `test_host_mesh.c` 154, `test_host_unit.c` 84. `make -C v4 test` and - `sanitize` at both widths, `hosted-check`, lint; three bare-metal - boots, `logs/20261008-063450` (amd64), `-063738` (aarch64), - `-064120` (riscv64). On all six: POST 538 of 538, `word_count=317`, - `dict_hash=0xd41a6ac9448fff60`. This code has not been reviewed - again. + **6d. The wait (section 7c).** Ruled 2026-10-07. Built 2026-10-08, + found unsound by two reviews, and **backed out the same day** (ruled): + section 7c.6. The code is as it was at step 6c; the limit of 7b.7 + stands. Verified after the backing out: `make -C v4 test` and + `sanitize` at both widths, `hosted-check` with its two new cases, + lint; three bare-metal boots, `logs/20261008-072104` (amd64), + `-072354` (aarch64), `-072743` (riscv64), each typing 29 values on a + line and `WORDS` with 26 on the stack; POST 538 of 538, + `word_count=317`, `dict_hash=0xc0769523a47b7dc3` on all six, which is + 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. 7. **A second unit; scaling while running; sleep, wake and kill by command.** diff --git a/logs/20261008-072104/amd64/qemu-amd64-20261008-072104.log b/logs/20261008-072104/amd64/qemu-amd64-20261008-072104.log new file mode 100644 index 00000000..5a99376b --- /dev/null +++ b/logs/20261008-072104/amd64/qemu-amd64-20261008-072104.log @@ -0,0 +1,204 @@ +[=3hBdsDxe: loading Boot0002 "UEFI QEMU DVD-ROM QM00005 " from PciRoot(0x0)/Pci(0x1F,0x2)/Sata(0x2,0xFFFF,0x0) +BdsDxe: starting Boot0002 "UEFI QEMU DVD-ROM QM00005 " from PciRoot(0x0)/Pci(0x1F,0x2)/Sata(0x2,0xFFFF,0x0) +[=3hStarKernel UEFI Loader +Loading kernel from ESP... +[CKPT 001] Entered efi_main - ConOut live +RAW SERIAL UP +[CKPT 002] Serial (COM1) initialized +Monolithic build - kernel linked directly +Collecting boot information... +CmdLine: parsed OK +[CKPT 004] Command line parsed +[CKPT 005] Kernel stack allocation decided +[CKPT 006] Boot info collected (ACPI table located) +GOP: linear framebuffer found +[CKPT 007] GOP: linear framebuffer found +[CKPT 008] About to enter ExitBootServices retry loop +EBS... +EBS OK +Calling kernel_main (monolithic)... + + + _____ _ _ __ _ + / ____| | | |/ / | | + | (___ | |_ __ _ _ __| ' / ___ _ __ _ __ ___| | + \___ \| __/ _` | '__| < / _ \ '__| '_ \ / _ \ | + ____) | || (_| | | | . \ __/ | | | | | __/ | + |_____/ \__\__,_|_| |_|\_\___|_| |_| |_|\___|_| + +LithosAnanke v2.1.0 +Architecture: amd64 +Build: Oct 8 2026 07:20:37 + +UEFI BootServices: EXITED + +=== StarKernel Boot Information === +Memory map entries: 135 +Total memory: 1023 MB +Usable memory: 966 MB +=================================== + +PMM initialized. +PMM statistics: + Total pages: 248743 + Free pages : 247220 + Used pages : 1523 + Total MB : 971 + Free MB : 965 + Used MB : 5 + +VMM initialized (mapped RAM, CR3 switched) +VMM initialized (mapped RAM, CR3 switched) +VMM self-test: mapped OK at 0xffff800000000000 +VMM self-test complete. + +IDT installed. + +APIC: init... +APIC: IA32_APIC_BASE MSR=0x00000000fee00900 +APIC: stale-ISR drain: 0 EOI(s) issued +APIC: initialized (xAPIC MMIO, TPR=0, SIVR=0x1FF, EOI-clear) +APIC: init done + +I/O APIC: init... +I/O APIC: base=0xfec00000, gsi_base=0, overrides=5 + override: source=00 gsi=2 flags=0x0000 + override: source=05 gsi=5 flags=0x000d + override: source=09 gsi=9 flags=0x000d + override: source=0a gsi=10 flags=0x000d + override: source=0b gsi=11 flags=0x000d +i8042: keyboard ACKed enable-scanning +i8042: IRQ1 enabled in controller config byte +I/O APIC: keyboard IRQ1 routed (masked) + +Timer: init... +Timer: init start +Timer: PM_TMR_BLK discovered from FADT at port 1544 +Timer: VM mode detected (hypervisor present). +Timer: HPET calibration disabled (VM-exit MMIO would poison timing). +Timer: WARNING: invariant TSC not present under hypervisor. +Timer: continuing in RELATIVE mode (no determinism guarantees). +Timer: RDTSCP not present; using RDTSC (less serialized). +Timer: CPUID frequency unavailable; trying PM Timer... +Timer: trust=1 (0=NONE,1=REL,2=ABS), TSC=2096654913 Hz +Timer: init done + +Kernel heap initialized. +Heap statistics: + Total bytes: 536870872 + Free bytes: 536870872 + Used bytes: 0 + Peak bytes: 0 + Heap base addr: 24641536 + Heap end addr: 561512448 + +Heartbeat: init... +APIC Timer: calibrating... +APIC Timer: apic_hz=1007656006, tick_hz=100, initial_count=10076560 +APIC Timer: configured (masked, ready to start) +Heartbeat: init done +Kernel initialization complete. +Boot successful! + +StarForth v4: one host node, the F18-derived engine +V4: capsule v4:nucleus-64.f18 signature: missing (unsigned) +PARITY:V4_NUCLEUS name=v4:nucleus-64.f18 capsule_id=0x5ff3672509f6c4e5 capsule_hash=0x5ff3672509f6c4e5 words=311 +V4: capsule v4:forth79.4th signature: missing (unsigned) +PARITY:V4_CAPSULE name=v4:forth79.4th capsule_id=0x4055641ee17d176b capsule_hash=0x4055641ee17d176b dict_hash=0xc07eb35ca84c4aa8 +PARITY:V4_POST tests=538 pass=538 fail=0 +PARITY:V4_SYSTEM word_count=317 dict_hash=0xc0769523a47b7dc3 +PARITY:OK +POST: PASSED +[HADES][INFO ] [KRELTSC: 0] blk: raw device LBN 2048..3071 (1024 blocks) +PCI: init... +PCI: ECAM mapped (amd64) +virtio-blk: found device +[HADES][INFO ] [KRELTSC: 79653714] blk: disk 'StarForth Volume' v2 LBN 3072..789009 (785938 user b +Artemis: virtio-blk attached +ok> : SQ DUP * ; + ok +ok> 7 SQ . 3 4 U* . . +49 0 12 ok +ok> T{ +UNKNOWN WORD: 'T{' + ERROR +ok> RS1 +UNKNOWN WORD: 'RS1' + ERROR +ok> COLD +FORTH-79 Cold Start +System initialized. + ok +ok> 3 4 U* . . +0 12 ok +ok> FORGET U* +Protected word + ERROR +ok> 9 KERNEL-WORD ASK9 ASK9 +Argument out of range + ERROR +ok> 1 BLOCK C@ . 2047 BLOCK C@ . +0 0 ok +ok> 2100 BLOCK 1024 BLANK 65 2100 BLOCK C! UPDATE SAVE-BUFFERS EMPTY-BUFFERS 2100 BLOCK C@ . +65 ok +ok> 3072 BLOCK C@ . +0 ok +ok> 3072 BLOCK DROP UPDATE SAVE-BUFFERS +Storage refused + ERROR +ok> 9999999 BLOCK +Block out of range + ERROR +ok> 5 7 PORT! +No one on that port + ERROR +ok> 1 2 + . +3 ok +ok> 5 AWAIT +7 8 * . +Interrupted + ERROR +ok> 56 ok +ok> 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 +Stack overflow + ERROR +ok> 3 4 + . +7 ok +ok> 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 + ok +ok> WORDS +ASK9 U/MOD U* (UD) (NB) (BITS) BYE ACL-WORD-ID ACL-HEAT@ ACL-INIT-PRIMITIVES +ACL-INHERIT ACL-ALLOW! ACL-TTL! ACL-MODE! ACL-PIN ACL-PINNED? ACL-ALLOW@ +ACL-TTL@ ACL-MODE@ ACL-HOOK LOG-DEBUG" LOG-TEST" LOG-INFO" LOG-WARN" +LOG-ERROR" (LOG") LOG-DEBUG-STR LOG-TEST-STR LOG-INFFO-STR LOG-WARN-STR +LOG-ERROR-STR LOG-LEVEL! LOG-LEVEL@ LOG-DEBUG LOG-TEST LOG-INFO +LOG-WARN LOG-ERROR THRU --> LOAD LIST EMPTY-BUFFERS FLUSH SAVE-BUFFERS +UPDATE BLOCK BUFFER BLK SCR Q.PRINT Q.COS Q.SIN Q.LOG Q.SQRT Q.EXP +Q./ Q.* Q.MIN Q.MAX Q.0= Q.> Q.< Q.= Q.NEG Q.ABS QQ.- Q.+ Q.SCALE +Q.0 Q.1 Q.TO-INT Q.FROM-INT DEFER@ IS DEFER COLD WARM PAGE VERSION +79-STANDARD FORGET VLIST WORDS ORDER VOCABULARY DEFINITIONS FORTH +CURRENT CONTEXT FENCE SKIP SCAN SEARCH COMPARE -TRAILING BLANK ERASE +FILL MOVE CMOVE> ?TERMINAL DMIN DMAX D< M- M+ D2/ D2* D== D0< D0= +DABS D- RSHIFT LSHIFT WITHIN MIN MAX ABS U> U< >= <= <> 0> 0<> -! +2! 2@ INVERT FALSE TRUE NOP 2R@ 2R> 2>R 2ROT 2OVER 2SWAP DUMP .S +? D. D.R U. U.R . .R OCTAL HEX DECIMAL SPACES #> #S # SIGN HOLD +<# STATE PAD BASE SPAN >IN TIB BL > < = NOT 0< 0= OR XOR AND M/MODD +*/ */MOD MOD / /MOD M* S>D 2/ 2* 2- 2+ 1- 1+ NEGATE * - + CELLS ++! ! @ UNLOOP LEAVE J I R@ R> >R ROLL PICK DEPTH 2DROP 2DUP ?DUP +-ROT ROT SWAP OVER DROP DUP S" (S") ABORT" (ABORT") ." (.") ABORT +QUIT (WORD-FORGOTTEN) (WORD-DEFINED) (REFUSED) (LOST) (CONSOLE) +(ME) NODE--ERROR (SEAL) PORT! AWAIT SEND-ON SEND NO-ROUTES GONE NO-ROUTE +DEFAULT-ROUTE NEIGHBOUR ROUTE +LOOP LOOP ?DO DO ENDCASE ENDOF OF +CASE REPEAT WHILE AGAIN UNTIL BEGIN THEN ELSE IF DOES> CONSTANT +VARIABLE CREATE [COMPILE] COMPILE IMMEDIATE EXIT KERNEL-WORD ; : +[LITERAL] ['] '' LITERAL ] [ INTERPRET EXECUTE FIND HIDDEN SMUDGE +TRAVERSE >BODY PFA CFA NAME> NFA >NAME LINK> LFA >LINK LATEST 2, +C, , ALLOT ALIGN HERE \ ( NUMBER CONVERT ENCLOSE WORD QUERY EXPECT +SOURCE TYPE COUNT SPACE CR KEY EMIT CMOVE C! C@ DNEGATE D+ UM* + ok +ok> DEPTH . +26 ok +ok> HERE . +8300 ok +ok> BYE +BYE: cold restart diff --git a/logs/20261008-072354/aarch64/qemu-aarch64-20261008-072354.log b/logs/20261008-072354/aarch64/qemu-aarch64-20261008-072354.log new file mode 100644 index 00000000..a8944360 --- /dev/null +++ b/logs/20261008-072354/aarch64/qemu-aarch64-20261008-072354.log @@ -0,0 +1,177 @@ +UEFI firmware (version 2025.11-3ubuntu7.3 built at 15:40:26 on Sep 23 2026) +[=3hBdsDxe: failed to load Boot0002 "UEFI Misc Device" from PciRoot(0x0)/Pci(0x2,0x0): Not Found +BdsDxe: failed to load Boot0003 "UEFI QEMU QEMU USB HARDDRIVE 1-0000:00:04.0-1" from PciRoot(0x0)/Pci(0x4,0x0)/USB(0x0,0x0): Not Found +BdsDxe: loading Boot0004 "UEFI Misc Device 2" from PciRoot(0x0)/Pci(0x6,0x0) +BdsDxe: starting Boot0004 "UEFI Misc Device 2" from PciRoot(0x0)/Pci(0x6,0x0) +[=3hStarKernel UEFI Loader +Loading kernel from ESP... +[CKPT 001] Entered efi_main - ConOut live +Collecting boot information... +[CKPT 004] Command line parsed +[CKPT 005] Kernel stack allocation decided +[CKPT 006] Boot info collected (ACPI table located) +[CKPT 007] GOP: linear framebuffer found +[CKPT 008] About to enter ExitBootServices retry loop + + + _____ _ _ __ _ + / ____| | | |/ / | | + | (___ | |_ __ _ _ __| ' / ___ _ __ _ __ ___| | + \___ \| __/ _` | '__| < / _ \ '__| '_ \ / _ \ | + ____) | || (_| | | | . \ __/ | | | | | __/ | + |_____/ \__\__,_|_| |_|\_\___|_| |_| |_|\___|_| + +LithosAnanke v2.1.0 +Architecture: aarch64 +Build: Oct 8 2026 07:23:27 + +UEFI BootServices: EXITED + +=== StarKernel Boot Information === +Memory map entries: 108 +Total memory: 4093 MB +Usable memory: 4054 MB +=================================== + +PMM initialized. +PMM statistics: + Total pages: 1039354 + Free pages : 1037943 + Used pages : 1411 + Total MB : 4059 + Free MB : 4054 + Used MB : 5 + +VMM initialized (mapped RAM, CR3 switched) +VMM initialized (mapped RAM, CR3 switched) +VMM self-test: mapped OK at 0xffff800000000000 +VMM self-test complete. + +AArch64: running at EL1 +IDT installed. + +APIC: init... +PSCI: no DTB -- using HVC (QEMU virt-machine default) +GICv2: no DTB GIC node -- using QEMU virt-machine defaults +GICv2: distributor+CPU interface enabled, PPI 30 +APIC: init done + +Timer: init... +Timer: AArch64 generic timer initialised. +Timer: init done + +Kernel heap initialized. +Heap statistics: + Total bytes: 2147483608 + Free bytes: 2147483608 + Used bytes: 0 + Peak bytes: 0 + Heap base addr: 1207959552 + Heap end addr: 3355443200 + +Heartbeat: init... +Heartbeat: init done +Kernel initialization complete. +Boot successful! + +StarForth v4: one host node, the F18-derived engine +V4: capsule v4:nucleus-64.f18 signature: missing (unsigned) +PARITY:V4_NUCLEUS name=v4:nucleus-64.f18 capsule_id=0x5ff3672509f6c4e5 capsule_hash=0x5ff3672509f6c4e5 words=311 +V4: capsule v4:forth79.4th signature: missing (unsigned) +PARITY:V4_CAPSULE name=v4:forth79.4th capsule_id=0x4055641ee17d176b capsule_hash=0x4055641ee17d176b dict_hash=0xc07eb35ca84c4aa8 +PARITY:V4_POST tests=538 pass=538 fail=0 +PARITY:V4_SYSTEM word_count=317 dict_hash=0xc0769523a47b7dc3 +PARITY:OK +POST: PASSED +[HADES][INFO ] [KRELTSC: 0] blk: raw device LBN 2048..3071 (1024 blocks) +PCI: init... +virtio-blk: found device +[HADES][INFO ] [KRELTSC: 1411419] blk: disk 'StarForth Volume' v2 LBN 3072..789009 (785938 user b +Artemis: virtio-blk attached +ok> : SQ DUP * ; + ok +ok> 7 SQ . 3 4 U* . . +49 0 12 ok +ok> T{ +UNKNOWN WORD: 'T{' + ERROR +ok> RS1 +UNKNOWN WORD: 'RS1' + ERROR +ok> COLD +FORTH-79 Cold Start +System initialized. + ok +ok> 3 4 U* . . +0 12 ok +ok> FORGET U* +Protected word + ERROR +ok> 9 KERNEL-WORD ASK9 ASK9 +Argument out of range + ERROR +ok> 1 BLOCK C@ . 2047 BLOCK C@ . +0 0 ok +ok> 2100 BLOCK 1024 BLANK 65 2100 BLOCK C! UPDATE SAVE-BUFFERS EMPTY-BUFFERS 2100 BLOCK C@ . +65 ok +ok> 3072 BLOCK C@ . +0 ok +ok> 3072 BLOCK DROP UPDATE SAVE-BUFFERS +Storage refused + ERROR +ok> 9999999 BLOCK +Block out of range + ERROR +ok> 5 7 PORT! +No one on that port + ERROR +ok> 1 2 + . +3 ok +ok> 5 AWAIT +7 8 * . +Interrupted + ERROR +ok> 56 ok +ok> 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 +Stack overflow + ERROR +ok> 3 4 + . +7 ok +ok> 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 + ok +ok> WORDS +ASK9 U/MOD U* (UD) (NB) (BITS) BYE ACL-WORD-ID ACL-HEAT@ ACL-INIT-PRIMITIVES +ACL-INHERIT ACL-ALLOW! ACL-TTL! ACL-MODE! ACL-PIN ACL-PINNED? ACL-ALLOW@ +ACL-TTL@ ACL-MODE@ ACL-HOOK LOG-DEBUG" LOG-TEST" LOG-INFO" LOG-WARN" +LOG-ERROR" (LOG") LOG-DEBUG-STR LOG-TEST-STR LOG-INFO-STR LOG-WARN-STR +LOG-ERROR-STR LOG-LEVEL! LOG-LEVEL@ LOG-DEBUG LOG-TEST LOG-INFO +LOG-WARN LOG-ERROR THRU --> LOAD LIST EMPTY-BUFFERS FLUSH SAVE-BUFFERS +UPDATE BLOCK BUFFER BLK SCR Q.PRINT Q.COS Q.SIN Q.LOG Q.SQRT Q.EXP +Q./ Q.* Q.MIN Q.MAX Q.0= Q.> Q.< Q.= Q.NEG Q.ABS Q.- Q.+ Q.SCALE +Q.0 Q.1 Q.TO-INT Q.FROM-INT DEFER@ IS DEFER COLD WARM PAGE VERSION +79-STANDARD FORGET VLIST WORDS ORDER VOCABULARY DEFINITIONS FORTH +CURRENT CONTEXT FENCE SKIP SCAN SEARCH COMPARE -TRAILING BLANK ERASE +FILL MOVE CMOVE> ?TERMINAL DMIN DMAX D< M- M+ D2/ D2* D= D0< D0= +DABS D- RSHIFT LSHIFT WITHIN MIN MAX ABS U> U< >= <= <> 0> 0<> -! +2! 2@ INVERT FALSE TRUE NOP 2R@ 2R> 2>R 2ROT 2OVER 2SWAP DUMP .S +? D. D.R U. U.R . .R OCTAL HEX DECIMAL SPACES #> #S # SIGN HOLD +<# STATE PAD BASE SPAN >IN TIB BL > < = NOT 0< 0= OR XOR AND M/MOD +*/ */MOD MOD / /MOD M* S>D 2/ 2* 2- 2+ 1- 1+ NEGATE * - + CELLS ++! ! @ UNLOOP LEAVE J I R@ R> >R ROLL PICK DEPTH 2DROP 2DUP ?DUP +-ROT ROT SWAP OVER DROP DUP S" (S") ABORT" (ABORT") ." (.") ABORT +QUIT (WORD-FORGOTTEN) (WORD-DEFINED) (REFUSED) (LOST) (CONSOLE) +(ME) NODE-ERROR (SEAL) PORT! AWAIT SEND-ON SEND NO-ROUTES GONE NO-ROUTE +DEFAULT-ROUTE NEIGHBOUR ROUTE +LOOP LOOP ?DO DO ENDCASE ENDOF OF +CASE REPEAT WHILE AGAIN UNTIL BEGIN THEN ELSE IF DOES> CONSTANT +VARIABLE CREATE [COMPILE] COMPILE IMMEDIATE EXIT KERNEL-WORD ; : +[LITERAL] ['] ' LITERAL ] [ INTERPRET EXECUTE FIND HIDDEN SMUDGE +TRAVERSE >BODY PFA CFA NAME> NFA >NAME LINK> LFA >LINK LATEST 2, +C, , ALLOT ALIGN HERE \ ( NUMBER CONVERT ENCLOSE WORD QUERY EXPECT +SOURCE TYPE COUNT SPACE CR KEY EMIT CMOVE C! C@ DNEGATE D+ UM* + ok +ok> DEPTH . +26 ok +ok> HERE . +8300 ok +ok> BYE +BYE: cold restart diff --git a/logs/20261008-072743/riscv64/qemu-riscv64-20261008-072743.log b/logs/20261008-072743/riscv64/qemu-riscv64-20261008-072743.log new file mode 100644 index 00000000..ba6b2241 --- /dev/null +++ b/logs/20261008-072743/riscv64/qemu-riscv64-20261008-072743.log @@ -0,0 +1,247 @@ + +OpenSBI v1.8 + ____ _____ ____ _____ + / __ \ / ____| _ \_ _| + | | | |_ __ ___ _ __ | (___ | |_) || | + | | | | '_ \ / _ \ '_ \ \___ \| _ < | | + | |__| | |_) | __/ | | |____) | |_) || |_ + \____/| .__/ \___|_| |_|_____/|____/_____| + | | + |_| + +Platform Name : riscv-virtio,qemu +Platform Features : medeleg +Platform HART Count : 1 +Platform HART Protection : pmp +Platform IPI Device : aclint-mswi +Platform Timer Device : aclint-mtimer @ 10000000Hz +Platform Console Device : uart8250 +Platform HSM Device : --- +Platform PMU Device : --- +Platform Reboot Device : syscon-reboot +Platform Shutdown Device : syscon-poweroff +Platform Suspend Device : --- +Platform CPPC Device : --- +Firmware Base : 0x80000000 +Firmware Size : 321 KB +Firmware RW Offset : 0x40000 +Firmware RW Size : 65 KB +Firmware Heap Offset : 0x47000 +Firmware Heap Size : 37 KB (total), 0 KB (reserved), 12 KB (used), 23 KB (free) +Firmware Scratch Size : 4096 B (total), 1464 B (used), 2632 B (free) +Runtime SBI Version : 3.0 +Standard SBI Extensions : ipi,pmu,srst,sse,hsm,rfnc,fwft,time,base,legacy,dbcn,dbtr +Experimental SBI Extensions : none + +Domain0 Name : root +Domain0 Boot HART : 0 +Domain0 HARTs : 0* +Domain0 Region00 : 0x0000000080040000-0x000000008005ffff M: (F,R,W) S/U: () +Domain0 Region01 : 0x0000000080000000-0x000000008003ffff M: (F,R,X) S/U: () +Domain0 Region02 : 0x0000000000100000-0x0000000000100fff M: (I,R,W) S/U: (R,W) +Domain0 Region03 : 0x0000000010000000-0x0000000010000fff M: (I,R,W) S/U: (R,W) +Domain0 Region04 : 0x0000000002000000-0x000000000200ffff M: (I,R,W) S/U: () +Domain0 Region05 : 0x000000000c400000-0x000000000c5fffff M: (I,R,W) S/U: (R,W) +Domain0 Region06 : 0x000000000c000000-0x000000000c3fffff M: (I,R,W) S/U: (R,W) +Domain0 Region07 : 0x0000000000000000-0xffffffffffffffff M: () S/U: (R,W,X) +Domain0 Next Address : 0x0000000020000000 +Domain0 Next Arg1 : 0x00000000bfe00000 +Domain0 Next Mode : S-mode +Domain0 SysReset : yes +Domain0 SysSuspend : yes + +Boot HART ID : 0 +Boot HART Domain : root +Boot HART Priv Version : v1.12 +Boot HART Base ISA : rv64imafdch +Boot HART ISA Extensions : sstc,zicntr,zihpm,zicboz,zicbom,sdtrig,svadu +Boot HART PMP Count : 16 +Boot HART PMP Granularity : 2 bits +Boot HART PMP Address Bits : 54 +Boot HART MHPM Info : 16 (0x0007fff8) +Boot HART Debug Triggers : 2 triggers +Boot HART MIDELEG : 0x0000000000001666 +Boot HART MEDELEG : 0x0000000000f4b509 +[=3hRISC-V EDK2 firmware version 2025.11-3ubuntu7.3 +Press ESCAPE within 5 seconds for boot options ERROR: C40000002:V03051002 I0 6D33944A-EC75-4855-A54D-809C75241F6C 83FFF850 +BdsDxe: failed to load Boot0001 "UEFI Misc Device" fr +om PciRoot(0x0)/Pci(0x1,0x0): Not Found +[=3hStarKernel UEFI Loader +Loading kernel from ESP... +[CKPT 001] Entered efi_main - ConOut live +Monolithic build - kernel linked directly +Collecting boot information... +CmdLine: parsed OK +[CKPT 004] Command line parsed +[CKPT 005] Kernel stack allocation decided +[CKPT 006] Boot info collected (ACPI table located) +GOP: linear framebuffer found +[CKPT 007] GOP: linear framebuffer found +[CKPT 008] About to enter ExitBootServices retry loop +Calling kernel_main (monolithic)... +riscv64 item 4.3.5a: satp state at kernel entry (before switch) + satp.MODE = 0x000000000000000a + satp.PPN = 0x00000000000bf868 + __kernel_start = 0x00000000bdc9caaa +riscv64: satp cleared -- Bare mode, explicit (item 4.3.5a) + + + _____ _ _ __ _ + / ____| | | |/ / | | + | (___ | |_ __ _ _ __| ' / ___ _ __ _ __ ___| | + \___ \| __/ _` | '__| < / _ \ '__| '_ \ / _ \ | + ____) | || (_| | | | . \ __/ | | | | | __/ | + |_____/ \__\__,_|_| |_|\_\___|_| |_| |_|\___|_| + +LithosAnanke v2.1.0 +Architecture: riscv64 +Build: Oct 8 2026 07:27:03 + +UEFI BootServices: EXITED + +=== StarKernel Boot Information === +Memory map entries: 100 +Total memory: 1020 MB +Usable memory: 975 MB +=================================== + +PMM initialized. +PMM statistics: + Total pages: 250269 + Free pages : 249617 + Used pages : 652 + Total MB : 977 + Free MB : 975 + Used MB : 2 + +VMM initialized (mapped RAM, CR3 switched) +VMM initialized (mapped RAM, CR3 switched) +VMM self-test: mapped OK at 0xffff800000000000 +VMM self-test complete. + +IDT installed. + +APIC: init... +PLIC: no DTB PLIC node -- using QEMU virt-machine default (base=0x0c000000) +PLIC: init (S-mode context 1, threshold=0) +APIC: init done + +Timer: init... +Timer: RISC-V time CSR @ 10000000 Hz (FALLBACK, no devicetree) +Timer: init done + +Kernel heap initialized. +Heap statistics: + Total bytes: 536870872 + Free bytes: 536870872 + Used bytes: 0 + Peak bytes: 0 + Heap base addr: 2214588416 + Heap end addr: 2751459328 + +Heartbeat: init... +Heartbeat: init done +Kernel initialization complete. +Boot successful! + +StarForth v4: one host node, the F18-derived engine +V4: capsule v4:nucleus-64.f18 signature: missing (unsigned) +PARITY:V4_NUCLEUS name=v4:nucleus-64.f18 capsule_id=0x5ff3672509f6c4e5 capsule_hash=0x5ff3672509f6c4e5 words=311 +V4: capsule v4:forth79.4th signature: missing (unsigned) +PARITY:V4_CAPSULE name=v4:forth79.4th capsule_id=0x4055641ee17d176b capsule_hash=0x4055641ee17d176b dict_hash=0xc07eb35ca84c4aa8 +PARITY:V4_POST tests=538 pass=538 fail=0 +PARITY:V4_SYSTEM word_count=317 dict_hash=0xc0769523a47b7dc3 +PARITY:OK +POST: PASSED +[HADES][INFO ] [KRELTSC: 0] blk: raw device LBN 2048..3071 (1024 blocks) +PCI: init... +virtio-blk: found device +[HADES][INFO ] [KRELTSC: 17911425] blk: disk 'StarForth Volume' v2 LBN 3072..789009 (785938 user b +Artemis: virtio-blk attached +ok> : SQ DUP * ; + ok +ok> 7 SQ . 3 4 U* . . +49 0 12 ok +ok> T{ +UNKNOWN WORD: 'T{' + ERROR +ok> RS1 +UNKNOWN WORD: 'RS1' + ERROR +ok> COLD +FORTH-79 Cold Start +System initialized. + ok +ok> 3 4 U* . . +0 12 ok +ok> FORGET U* +Protected word + ERROR +ok> 9 KERNEL-WORD ASK9 ASK9 +Argument out of range + ERROR +ok> 1 BLOCK C@ . 2047 BLOCK C@ . +0 0 ok +ok> 2100 BLOCK 1024 BLANK 65 2100 BLOCK C! UPDATE SAVE-BUFFERS EMPTY-BUFFERS 2100 BLOCK C@ . +65 ok +ok> 3072 BLOCK C@ . +0 ok +ok> 3072 BLOCK DROP UPDATE SAVE-BUFFERS +Storage refused + ERROR +ok> 9999999 BLOCK +Block out of range + ERROR +ok> 5 7 PORT! +No one on that port + ERROR +ok> 1 2 + . +3 ok +ok> 5 AWAIT +7 8 * . +Interrupted + ERROR +ok> 56 ok +ok> 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 +Stack overflow + ERROR +ok> 3 4 + . +7 ok +ok> 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 + ok +ok> WORDS +ASK9 U/MOD U* (UD) (NB) (BITS) BYE ACL-WORD-ID ACL-HEAT@ ACL-INIT-PRIMITIVES +ACL-INHERIT ACL-ALLOW! ACL-TTL! ACL-MODE! ACL-PIN ACL-PINNED? ACL-ALLOW@ +ACL-TTL@ ACL-MODE@ ACL-HOOK LOG-DEBUG" LOG-TEST" LOG-INFO" LOG-WARN" +LOG-ERROR" (LOG") LOG-DEBUG-STR LOG-TEST-STR LOG-INFFO-STR LOG-WARN-STR +LOG-ERROR-STR LOG-LEVEL! LOG-LEVEL@ LOG-DEBUG LOG-TEST LOG-INFO +LOG-WARN LOG-ERROR THRU --> LOAD LIST EMPTY-BUFFERS FLUSH SAVE-BUFFERS +UPDATE BLOCK BUFFER BLK SCR Q.PRINT Q.COS Q.SIN Q.LOG Q.SQRT Q.EXP +Q./ Q.* Q.MIN Q.MAX Q.0= Q.> Q.< Q.= Q.NEG Q.ABS QQ.- Q.+ Q.SCALE +Q.0 Q.1 Q.TO-INT Q.FROM-INT DEFER@ IS DEFER COLD WARM PAGE VERSION +79-STANDARD FORGET VLIST WORDS ORDER VOCABULARY DEFINITIONS FORTH +CURRENT CONTEXT FENCE SKIP SCAN SEARCH COMPARE -TRAILING BLANK ERASE +FILL MOVE CMOVE> ?TERMINAL DMIN DMAX D< M- M+ D2/ D2* DD= D0< D0= +DABS D- RSHIFT LSHIFT WITHIN MIN MAX ABS U> U< >= <= <> 0> 0<> -! +2! 2@ INVERT FALSE TRUE NOP 2R@ 2R> 2>R 2ROT 2OVER 2SWAP DUMP .S +? D. D.R U. U.R . .R OCTAL HEX DECIMAL SPACES #> #S # SIGN HOLD +<# STATE PAD BASE SPAN >IN TIB BL > < = NOT 0< 0= OR XOR AND M/MOD +*/ */MOD MOD / /MOD M* S>D 2/ 2* 2- 2+ 1- 1+ NEGATE * - + CELLS ++! ! @ UNLOOP LEAVE J I R@ R> >R ROLL PICK DEPTH 2DROP 2DUP ?DUP +-ROT ROT SWAP OVER DROP DUP S" (S") ABORT" (ABORT") ." (.") ABORT +QUIT (WORD-FORGOTTEN) (WORD-DEFINED) (REFUSED) (LOST) (CONSOLE) +(ME) NODE-EERROR (SEAL) PORT! AWAIT SEND-ON SEND NO-ROUTES GONE NO-ROUTE +DEFAULT-ROUTE NEIGHBOUR ROUTE +LOOP LOOP ?DO DO ENDCASE ENDOF OF +CASE REPEAT WHILE AGAIN UNTIL BEGIN THEN ELSE IF DOES> CONSTANT +VARIABLE CREATE [COMPILE] COMPILE IMMEDIATE EXIT KERNEL-WORD ; : +[LITERAL] ['] ' LLITERAL ] [ INTERPRET EXECUTE FIND HIDDEN SMUDGE +TRAVERSE >BODY PFA CFA NAME> NFA >NAME LINK> LFA >LINK LATEST 2, +C, , ALLOT ALIGN HERE \ ( NUMBER CONVERT ENCLOSE WORD QUERY EXPECT +SOURCE TYPE COUNT SPACE CR KEY EMIT CMOVE C! C@ DNEGATE D+ UM* + ok +ok> DEPTH . +26 ok +ok> HERE . +8300 ok +ok> BYE +BYE: cold restart diff --git a/v4/Makefile b/v4/Makefile index 3621a245..e8ae96ce 100644 --- a/v4/Makefile +++ b/v4/Makefile @@ -204,6 +204,8 @@ hosted-check: $(foreach i,$(HOSTED_ISAS),$(BINDIR)/boot-$(i).txt) $(BINDIR)/star @grep -q '^ok> Interrupted$$' $(BINDIR)/await-amd64.txt && grep -q '^ok> 3 ok$$' $(BINDIR)/await-amd64.txt && grep -q '^ok> 56 ok$$' $(BINDIR)/await-amd64.txt || { echo "hosted-check: a line typed while the node waited did not break the wait and then run"; tail -6 $(BINDIR)/await-amd64.txt; exit 1; } @printf '1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29\n1 2 + .\n' | $(BINDIR)/starforth4-amd64 > $(BINDIR)/deep-amd64.txt 2>&1; true @grep -q '^ok> Stack overflow$$' $(BINDIR)/deep-amd64.txt && grep -q '^ok> 3 ok$$' $(BINDIR)/deep-amd64.txt || { echo "hosted-check: a line that left 29 values on the stack was not an error the node came back from"; tail -4 $(BINDIR)/deep-amd64.txt; exit 1; } + @printf '1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26\nWORDS\nDEPTH .\n' | $(BINDIR)/starforth4-amd64 > $(BINDIR)/deepwords-amd64.txt 2>&1; true + @grep -q 'DUP' $(BINDIR)/deepwords-amd64.txt && grep -q '^ok> 26 ok$$' $(BINDIR)/deepwords-amd64.txt || { echo "hosted-check: WORDS with 26 values on the stack did not print and come back"; tail -4 $(BINDIR)/deepwords-amd64.txt | cut -c1-200; exit 1; } @$(BINDIR)/starforth4-postfail < /dev/null > $(BINDIR)/postfail.txt; test $$? -ne 0 || { echo "hosted-check: a boot with a failing case did not fail"; exit 1; } @grep -q '^POST FAIL: fail.wrong out<5 5 $$' $(BINDIR)/postfail.txt || { echo "hosted-check: the failing case was not named"; cat $(BINDIR)/postfail.txt; exit 1; } @grep -q '^POST FAIL: fail.many outplace = places; f->capacity = capacity; f->gone_error = 0; + f->interrupt_error = 0; for (i = 0; i < capacity; i++) clear_place(&places[i]); } @@ -26,6 +27,10 @@ void v4_fabric_gone_error(v4_fabric *f, v4_cell code) f->gone_error = code; } +void v4_fabric_interrupt_error(v4_fabric *f, v4_cell code) +{ + f->interrupt_error = code; +} void v4_fabric_grow(v4_fabric *f, v4_place *places, unsigned capacity) { @@ -147,15 +152,11 @@ static unsigned hand_over_write(v4_fabric *f, unsigned a) static unsigned hand_over_read(v4_fabric *f, unsigned a) { v4_node *n = &f->place[a].node->n; - unsigned first; - unsigned last; + unsigned first = n->read_port == V4_PORT_ANY ? 0 : n->read_port; + unsigned last = n->read_port == V4_PORT_ANY ? V4_PORTS - 1 : n->read_port; unsigned k; v4_cell value; - if (n->read_port > V4_PORT_ANY) return 0; /* the wait for an offer only: it reads no port */ - first = n->read_port == V4_PORT_ANY ? 0 : n->read_port; - last = n->read_port == V4_PORT_ANY ? V4_PORTS - 1 : n->read_port; - for (k = first; k <= last; k++) { const v4_wire *w = &f->place[a].wire[k]; if (w->kind == V4_WIRE_DEVICE && w->device->give && w->device->give(w->device->self, &value)) { @@ -208,35 +209,23 @@ unsigned v4_fabric_step(v4_fabric *f) for (i = 0; i < f->capacity; i++) if (awake(f, i) && f->place[i].node->n.reading && !f->place[i].node->n.given) done += hand_over_read(f, i); - /* THE WAIT (docs/v4.0.0/MESH.md 7c). A node in the wait that was given a - * word above is no longer in it: a word for a node comes before its own - * offer. Of the rest, each offer is taken by whoever is ready for it: a - * device that takes, or a node blocked reading that port or any -- which - * a node in the wait is, so of two that offer to each other the one at - * the lower place writes and the other's offers are withdrawn. */ - for (i = 0; i < f->capacity; i++) { - v4_node *n; - unsigned k; - if (!awake(f, i)) continue; - n = &f->place[i].node->n; - if (!v4_node_in_wait(n)) continue; - for (k = 0; k < V4_PORTS; k++) { - const v4_wire *w = &f->place[i].wire[k]; - if (!(n->offers & (1u << k))) continue; - if (w->kind == V4_WIRE_DEVICE) { - if (w->device->take && w->device->take(w->device->self, n->offer[k])) { (void)v4_node_offer_taken(n, k); done++; break; } - } else if (w->kind == V4_WIRE_NODE && awake(f, w->node)) { - v4_node *r = &f->place[w->node].node->n; - if (r->reading && !r->given && (r->read_port == w->port || r->read_port == V4_PORT_ANY)) { - v4_cell word = n->offer[k]; - (void)v4_node_offer_taken(n, k); - v4_node_port_give(r, w->port, word); - done++; + /* a node blocked on the first word of a message to a node that is not + * reading, while a device of its own has something for it */ + if (f->interrupt_error != 0) + for (i = 0; i < f->capacity; i++) { + v4_node *n; + unsigned k; + if (!awake(f, i)) continue; + n = &f->place[i].node->n; + if (!n->asking || f->place[i].wire[n->ask_port].kind != V4_WIRE_NODE || v4_node_words_since_look(n) != 0) continue; + for (k = 0; k < V4_PORTS; k++) { + const v4_wire *w = &f->place[i].wire[k]; + if (w->kind == V4_WIRE_DEVICE && w->device->pending && w->device->pending(w->device->self)) { + done += (unsigned)v4_node_port_gone(n, f->interrupt_error); break; } } } - } /* a node blocked at one port that nothing is wired to */ if (f->gone_error != 0) @@ -245,13 +234,8 @@ unsigned v4_fabric_step(v4_fabric *f) if (!awake(f, i)) continue; n = &f->place[i].node->n; if ((n->asking && f->place[i].wire[n->ask_port].kind == V4_WIRE_NONE) - || (n->reading && !n->given && n->read_port < V4_PORTS && f->place[i].wire[n->read_port].kind == V4_WIRE_NONE)) + || (n->reading && !n->given && n->read_port != V4_PORT_ANY && f->place[i].wire[n->read_port].kind == V4_WIRE_NONE)) done += (unsigned)v4_node_port_gone(n, f->gone_error); - else if (v4_node_in_wait(n)) { /* an offer on a port with nothing on it */ - unsigned k; - for (k = 0; k < V4_PORTS; k++) - if ((n->offers & (1u << k)) && f->place[i].wire[k].kind == V4_WIRE_NONE) { done += (unsigned)v4_node_offer_gone(n, k); break; } - } } return done; diff --git a/v4/src/node.c b/v4/src/node.c index 491d31ab..665310b6 100644 --- a/v4/src/node.c +++ b/v4/src/node.c @@ -93,14 +93,7 @@ void v4_node_store(v4_node *n, v4_cell addr, v4_cell value) n->asking = 1; return; } - if (port >= (int)V4_PORT_OFFER && port < (int)V4_PORT_WAIT) { /* an offer: remembered until the wait */ - unsigned k = (unsigned)port - V4_PORT_OFFER; - n->offer[k] = value; - n->offers |= 1u << k; - return; - } - if (port == (int)V4_PORT_WAIT) { n->offers = 0; return; } /* a store to the wait: every offer is withdrawn */ - if (port >= 0) { v4_node_fault(n, V4_FAULT_ADDRESS, addr); return; } /* "any", "which port" and the rest are not written to */ + if (port >= 0) { v4_node_fault(n, V4_FAULT_ADDRESS, addr); return; } /* "any" and "which port" 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; } @@ -127,16 +120,8 @@ v4_cell v4_node_fetch(v4_node *n, v4_cell addr) { int port = v4_node_port_index(n, addr); if (port == (int)V4_PORTS + 1) return (v4_cell)n->last_from; - if (port == (int)V4_PORTS + 2) return (v4_cell)n->writers; - if (port == (int)V4_PORTS + 3) return (v4_cell)n->readers; - if (port == (int)V4_PORT_WAIT || port == (int)V4_PORT_WAIT1) { - /* the wait: the word that came, or 0 for an offer taken */ - if (!n->given) return 0; - n->given = 0; - n->last_from = n->given_port; - return n->given_value; - } - if (port >= (int)V4_PORT_OFFER) return 0; /* an offer is not read back */ + 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 >= 0) { /* the word its neighbour wrote; v4_node_read_ready has said it is there */ if (!n->given) return 0; @@ -194,9 +179,7 @@ void v4_node_port_attach(v4_node *n, v4_cell base) n->last_from = 0; n->writers = 0; n->readers = 0; - n->offers = 0; - n->waiting = 0; - n->offer_asked = 0; + n->since_look = 0; } void v4_node_port_status(v4_node *n, unsigned writers, unsigned readers) @@ -207,19 +190,17 @@ 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_PORT_WAIT1) return -1; + if (n->port < 0 || addr < n->port || addr > n->port + (v4_cell)V4_PORTS + 3) return -1; return (int)(addr - n->port); } int v4_node_read_ready(v4_node *n, v4_cell addr) { int port = v4_node_port_index(n, addr); - if (port < 0 || (port > (int)V4_PORT_ANY && port != (int)V4_PORT_WAIT && port != (int)V4_PORT_WAIT1)) return 1; - if (n->given && (port >= (int)V4_PORT_ANY || (unsigned)port == n->given_port)) return 1; + if (port < 0 || port > (int)V4_PORT_ANY) return 1; + if (n->given && (port == (int)V4_PORT_ANY || (unsigned)port == n->given_port)) return 1; n->reading = 1; - n->waiting = port >= (int)V4_PORT_WAIT; /* the wait is a read of any port, with offers standing; */ - n->read_port = port == (int)V4_PORT_WAIT ? V4_PORT_ANY /* the wait for an offer only reads no port at all */ - : (unsigned)port; + n->read_port = (unsigned)port; return 0; } @@ -229,39 +210,6 @@ void v4_node_port_give(v4_node *n, unsigned port, v4_cell value) n->given_value = value; n->given_port = port; n->reading = 0; - if (n->waiting) { n->waiting = 0; n->offers = 0; } /* the wait is over: every offer is withdrawn */ -} - -int v4_node_in_wait(const v4_node *n) -{ - return n->waiting && n->reading && !n->given; -} - -int v4_node_offer_taken(v4_node *n, unsigned k) -{ - if (!v4_node_in_wait(n) || k >= V4_PORTS || !(n->offers & (1u << k))) return 0; - v4_node_port_give(n, V4_PORTS + k, 0); - return 1; -} - -int v4_node_offer_gone(v4_node *n, unsigned k) -{ - if (n->error_reg < 0 || n->fault_vector < 0 || n->stopped) return 0; - if (!v4_node_in_wait(n) || k >= V4_PORTS || !(n->offers & (1u << k))) return 0; - v4_node_port_give(n, 2u * V4_PORTS + k, 0); - return 1; -} - -int v4_node_offer_as_write(v4_node *n) -{ - unsigned k; - if (!v4_node_in_wait(n) || n->offers == 0 || n->asking) return 0; - for (k = 0; !(n->offers & (1u << k)); k++) { } - n->request = n->offer[k]; - n->ask_port = k; - n->asking = 1; - n->offer_asked = 1; - return 1; } void v4_node_born(v4_node *n, v4_cell base) @@ -274,23 +222,19 @@ void v4_node_born(v4_node *n, v4_cell base) void v4_node_port_served(v4_node *n) { n->asking = 0; - if (n->offer_asked) { /* what was served was an offer: the wait is over */ - n->offer_asked = 0; - v4_node_port_give(n, V4_PORTS + n->ask_port, 0); - } + n->since_look++; } +unsigned v4_node_words_since_look(const v4_node *n) +{ + return n->since_look; +} int v4_node_port_gone(v4_node *n, v4_cell code) { if (code == 0 || n->error_reg < 0 || n->fault_vector < 0 || n->stopped) return 0; - if (n->asking && n->offer_asked) { /* what it wrote was an offer: it is told, not faulted */ - n->asking = 0; n->offer_asked = 0; - v4_node_port_give(n, 2u * V4_PORTS + n->ask_port, 0); - return 1; - } if (n->asking) n->asking = 0; - else if (n->reading && !n->given && n->read_port < V4_PORTS) n->reading = 0; + else if (n->reading && !n->given && n->read_port != V4_PORT_ANY) n->reading = 0; else return 0; v4_node_store(n, n->error_reg, code); /* raises it: P is the node's handler now */ return 1; diff --git a/v4/system/boot.c b/v4/system/boot.c index 3e9b57b2..715a5bf6 100644 --- a/v4/system/boot.c +++ b/v4/system/boot.c @@ -107,7 +107,6 @@ static int run_line(const v4_boot *b, const char *text, unsigned len, char *keep for (;;) { (void)v4_exec_step_word(n, b->es, b->h); if (n->stopped) return V4_BOOT_LINE_STOPPED; - (void)v4_node_offer_as_write(n); /* in the wait (MESH.md 7c): what it offers is a write, here where whatever is on a port takes at once */ 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 */ diff --git a/v4/tests/host_map.h b/v4/tests/host_map.h index 29c0c8bd..98e3cca2 100644 --- a/v4/tests/host_map.h +++ b/v4/tests/host_map.h @@ -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 - 144) /* the node's ports (node.h): V4_PORTS of them, then "any port", "which port", who writes, who reads, an offer for each port, and the two waits: 2 * V4_PORTS + 6 cells, between the output buffer and A_FOUND. Port 0 is where its requests go. */ +#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 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 */ @@ -87,12 +87,6 @@ #define MQ_TAIL (BUF0_W - 36) /* where the next will go, */ #define MQ_COUNT (BUF0_W - 37) /* and how many cells they take */ #define AWAIT_FROM (BUF0_W - 39) /* the node whose word of how text ended is waited for */ -#define W_GONE (BUF0_W - 122) /* not 0: the port a message was being written to has nothing on it; its other words are let go */ -#define TAKING (BUF0_W - 121) /* not 0: a message is being taken in, and is only partly with the messages waiting; */ -#define T_TAIL (BUF0_W - 120) /* where they ended before it, */ -#define T_COUNT (BUF0_W - 119) /* and how many cells they took */ -#define GATE_WORD (BUF0_W - 32) /* the first word of a message that is being offered */ -#define PAY_K (BUF0_W - 31) /* for each port, which refusal owed is offered there, counted from 1; 0: none. V4_PORTS cells */ #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 */ @@ -130,8 +124,6 @@ typedef char host_map_ports_fit[(V4_PORTS <= 8u) ? 1 : -1]; #endif /* the messages waiting are above the dictionary */ typedef char host_map_queue_fits[(MQ_W >= DICT_END_W) ? 1 : -1]; -/* the ports, with the offers and the wait after them, end below the cells that follow */ -typedef char host_map_port_block_fits[(PORT + 2 * (v4_cell)V4_PORTS + 6 <= W_GONE && PAY_K + (v4_cell)V4_PORTS <= PRINT_TO) ? 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. */ @@ -204,16 +196,6 @@ static int host_load(v4_text *tx, v4_node *n, const char *const *files, unsigned 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, "(GATE-WORD)", GATE_WORD); - v4_text_constant(tx, "(PAY-K)", PAY_K); - v4_text_constant(tx, "PORTS-1", (v4_cell)V4_PORTS - 1); - v4_text_constant(tx, "(OFFER)", PORT + (v4_cell)V4_PORT_OFFER); - v4_text_constant(tx, "(WAIT)", PORT + (v4_cell)V4_PORT_WAIT); - v4_text_constant(tx, "(WAIT1)", PORT + (v4_cell)V4_PORT_WAIT1); - v4_text_constant(tx, "(W-GONE)", W_GONE); - v4_text_constant(tx, "(TAKING)", TAKING); - v4_text_constant(tx, "(T-TAIL)", T_TAIL); - v4_text_constant(tx, "(T-COUNT)", T_COUNT); 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); diff --git a/v4/tests/test_fabric.c b/v4/tests/test_fabric.c index 042ff1db..7af08fe6 100644 --- a/v4/tests/test_fabric.c +++ b/v4/tests/test_fabric.c @@ -15,7 +15,7 @@ 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) /* The memory map is open (D-4); the test chooses where the ports are. */ -#define PB ((v4_cell)(V4_NODE_WORDS - 32u)) /* port 0: the ports and what follows them end below the top of memory */ +#define PB ((v4_cell)(V4_NODE_WORDS - 16u)) /* port 0 */ #define ANY (PB + (v4_cell)V4_PORT_ANY) #define ORG ((v4_cell)16) /* where a test's programme starts */ #define OUT ((v4_cell)900) /* where it leaves what it found */ @@ -45,36 +45,9 @@ static unsigned loaded(unsigned k) /* the programme ends by waiting at a port nothing is wired to */ static void wait_for_ever(void) { LIT(PB + 7); O(BANG_B); O(FETCH_B); (void)v4_asm_label(&as); } /* and the last word is written out */ -/* ---- the wait (MESH.md 7c) ---- */ -static void fresh(void) -{ - unsigned k; - v4_fabric_init(&f, places, 4); - for (k = 0; k < 4; k++) v4_node_reset(&pool[k].n); -} -/* offer `word` on port k */ -static void offer(unsigned k, v4_cell word) { LIT(word); LIT(PB + (v4_cell)V4_PORT_OFFER + (v4_cell)k); O(BANG_A); O(STORE_A); } -/* the wait: what it gives goes to `at`, and which port to `at + 1` */ -static void wait_into(v4_cell at) -{ - LIT(PB + (v4_cell)V4_PORT_WAIT); O(BANG_B); O(FETCH_B); LIT(at); O(BANG_A); O(STORE_A); - LIT(PB + (v4_cell)V4_PORTS + 1); O(BANG_B); O(FETCH_B); LIT(at + 1); O(BANG_A); O(STORE_A); -} -/* the wait for an offer only */ -static void wait1_into(v4_cell at) -{ - LIT(PB + (v4_cell)V4_PORT_WAIT1); O(BANG_B); O(FETCH_B); LIT(at); O(BANG_A); O(STORE_A); - LIT(PB + (v4_cell)V4_PORTS + 1); O(BANG_B); O(FETCH_B); LIT(at + 1); O(BANG_A); O(STORE_A); -} -static void done_mark(void) { LIT(1); LIT(OUT + 2); O(BANG_A); O(STORE_A); } -static void count_for_ever(void) -{ - v4_cell again; - LIT(OUT); O(BANG_A); - again = v4_asm_label(&as); - O(FETCH_A); LIT(1); O(ADD); O(STORE_A); - v4_asm_branch(&as, V4_OP_JUMP, again); -} +/* a device that says whether it has something pending */ +static int *dev_has; +static int device_has(void *self) { (void)self; return *dev_has; } /* Let time pass until nothing more can happen, or `max` steps. */ static unsigned settle(unsigned max) @@ -394,155 +367,60 @@ int main(void) CHECK(ND(a)->n.asking && ND(a)->n.request == 5 && ND(c)->n.reading && ND(c)->n.mem[OUT] == 0, "looking takes nothing and gives nothing: both wait as they did"); CHECK(!ND(b)->n.stopped && ND(b)->n.reading, "and the one that looked did not wait to look"); - /* ---- the wait (MESH.md 7c): a node offers a word and sleeps ---- */ + /* ---- a node blocked on the first word of a message, while a device of its own has something for it ---- */ { + static int has; /* what the device says it has pending */ static stream none; - static v4_device sink; - v4_uheat_t clock; - sink.take = stream_take; sink.give = 0; sink.self = &none; - - /* taken by a reader */ - fresh(); - a = loaded(0); offer(0, 7); wait_into(OUT); done_mark(); wait_for_ever(); - b = loaded(1); LIT(PB + 3); O(BANG_B); O(FETCH_B); LIT(OUT); O(BANG_A); O(STORE_A); wait_for_ever(); - CHECK(v4_asm_ok(&as) && v4_fabric_wire(&f, a, 0, b, 3), "a node that offers, wired to one that reads"); - (void)settle(300); - CHECK(ND(b)->n.mem[OUT] == 7, "the reader has the word offered: %ld", (long)ND(b)->n.mem[OUT]); - CHECK(ND(a)->n.mem[OUT] == 0 && ND(a)->n.mem[OUT + 1] == (v4_cell)V4_PORTS + 0 && ND(a)->n.mem[OUT + 2] == 1 && ND(a)->n.offers == 0, - "the one that offered is told its offer on port 0 was taken, and has none left: %ld from %ld", (long)ND(a)->n.mem[OUT], (long)ND(a)->n.mem[OUT + 1]); - - /* taken by a device */ - fresh(); none.took_n = 0; - a = loaded(0); offer(0, 7); wait_into(OUT); done_mark(); wait_for_ever(); - CHECK(v4_fabric_wire_device(&f, a, 0, &sink), "the same, wired to a device that takes what is written"); - (void)settle(300); - CHECK(none.took_n == 1 && none.took[0] == 7 && ND(a)->n.mem[OUT + 1] == (v4_cell)V4_PORTS + 0 && ND(a)->n.mem[OUT + 2] == 1, "the device has the word, and the node is told"); - - /* a neighbour that never reads */ - fresh(); - a = loaded(0); offer(0, 7); wait_into(OUT); done_mark(); wait_for_ever(); - b = loaded(1); count_for_ever(); - CHECK(v4_asm_ok(&as) && v4_fabric_wire(&f, a, 0, b, 3), "a node that offers, wired to one that never reads"); - for (i = 0; i < 200; i++) (void)v4_fabric_step(&f); - clock = ND(a)->es.anticlock; - for (i = 0; i < 100; i++) (void)v4_fabric_step(&f); - CHECK(v4_node_in_wait(&ND(a)->n) && ND(a)->es.anticlock == clock && ND(a)->n.mem[OUT + 2] == 0, "it is in the wait, and executes nothing there"); - CHECK((ND(b)->n.readers & (1u << 3)) != 0, "and shows to its neighbour as waiting to read"); - - /* withdrawn by a word that comes for it */ - c = loaded(2); LIT(PB + 0); O(BANG_B); LIT(9); O(STORE_B); wait_for_ever(); - CHECK(v4_asm_ok(&as) && v4_fabric_wire(&f, c, 0, a, 2), "a third node writes to it"); - for (i = 0; i < 100; i++) (void)v4_fabric_step(&f); - CHECK(ND(a)->n.mem[OUT] == 9 && ND(a)->n.mem[OUT + 1] == 2 && ND(a)->n.mem[OUT + 2] == 1 && ND(a)->n.offers == 0, - "it gets that word, from that port, and its offer is withdrawn: %ld from %ld", (long)ND(a)->n.mem[OUT], (long)ND(a)->n.mem[OUT + 1]); - CHECK(!ND(b)->n.given && !ND(c)->n.asking, "the node offered to was given nothing, and the writer was served"); - - /* an offer that could be taken in the step a word comes: the word comes first */ - fresh(); - a = loaded(0); offer(0, 7); wait_into(OUT); offer(0, 7); wait_into(OUT + 3); done_mark(); wait_for_ever(); - b = loaded(1); LIT(PB + 3); O(BANG_B); O(FETCH_B); LIT(OUT); O(BANG_A); O(STORE_A); wait_for_ever(); - c = loaded(2); LIT(PB + 0); O(BANG_B); LIT(9); O(STORE_B); wait_for_ever(); - CHECK(v4_asm_ok(&as) && v4_fabric_wire(&f, a, 0, b, 3) && v4_fabric_wire(&f, c, 0, a, 2), "one offers to a reader while a third writes to it"); - v4_fabric_sleep(&f, a); - for (i = 0; i < 50; i++) (void)v4_fabric_step(&f); - CHECK(ND(b)->n.reading && ND(c)->n.asking, "the reader and the writer are both blocked"); - v4_fabric_sleep(&f, b); v4_fabric_sleep(&f, c); v4_fabric_wake(&f, a); - for (i = 0; i < 50; i++) (void)v4_fabric_step(&f); - CHECK(v4_node_in_wait(&ND(a)->n), "and then the one that offers is in the wait"); - v4_fabric_wake(&f, b); v4_fabric_wake(&f, c); + static v4_device dev; + v4_fabric_init(&f, places, 4); + for (i = 0; i < 3; i++) v4_node_reset(&pool[i].n); + a = loaded(0); /* looks about, then writes two words to its port 0 */ + LIT(PB + (v4_cell)V4_PORTS + 3); O(BANG_B); O(FETCH_B); O(DROP); + LIT(PB + 0); O(BANG_B); LIT(1); O(STORE_B); LIT(2); O(STORE_B); wait_for_ever(); + b = loaded(1); /* never reads */ + LIT(OUT); O(BANG_A); + { + v4_cell again = v4_asm_label(&as); + O(FETCH_A); LIT(1); O(ADD); O(STORE_A); + v4_asm_branch(&as, V4_OP_JUMP, again); + } + CHECK(v4_asm_ok(&as) && v4_fabric_wire(&f, a, 0, b, 3), "a writer that looks first, wired to a node that never reads"); + dev.take = stream_take; dev.give = 0; dev.self = &none; dev.pending = device_has; has = 1; dev_has = &has; + CHECK(v4_fabric_wire_device(&f, a, 5, &dev), "and to a device, which has something for it"); + for (i = 0; i < 40; i++) (void)v4_fabric_step(&f); + CHECK(ND(a)->n.asking && ND(a)->n.request == 1 && v4_node_words_since_look(&ND(a)->n) == 0, "the writer is blocked on its first word, and waits: the fabric has no error for it"); + v4_fabric_interrupt_error(&f, 21); + for (i = 0; i < 40; i++) (void)v4_fabric_step(&f); + CHECK(ND(a)->n.asking && ND(a)->n.faults == 0, "nor is a bare node let go when it has: it cannot take an error"); + v4_node_error_attach(&ND(a)->n, OUT + 20); + v4_node_fault_attach(&ND(a)->n, 900); + has = 0; + for (i = 0; i < 40; i++) (void)v4_fabric_step(&f); + CHECK(ND(a)->n.asking && ND(a)->n.faults == 0, "a node that can take one waits all the same while the device has nothing for it"); + has = 1; (void)v4_fabric_step(&f); - CHECK(!v4_node_in_wait(&ND(a)->n) && !ND(c)->n.asking && ND(b)->n.reading && !ND(b)->n.given, "in one step: the word for it comes first, and the reader is given nothing"); - (void)settle(300); - CHECK(ND(a)->n.mem[OUT] == 9 && ND(a)->n.mem[OUT + 1] == 2 && ND(a)->n.mem[OUT + 3] == 0 && ND(a)->n.mem[OUT + 4] == (v4_cell)V4_PORTS + 0 && ND(b)->n.mem[OUT] == 7, - "it offers again, and the reader gets the word once"); + CHECK(!ND(a)->n.asking && ND(a)->n.fault_kind == V4_FAULT_RAISED && ND(a)->n.fault_addr == 21 && ND(a)->n.mem[OUT + 20] == 21, + "when it has, the writer is let go and the error raised on it"); - /* two nodes each offering to the other */ - fresh(); - a = loaded(0); offer(0, 5); wait_into(OUT); done_mark(); wait_for_ever(); - b = loaded(1); offer(3, 6); wait_into(OUT); done_mark(); wait_for_ever(); - CHECK(v4_asm_ok(&as) && v4_fabric_wire(&f, a, 0, b, 3), "two nodes each offer to the other"); - (void)settle(300); - CHECK(ND(a)->n.mem[OUT] == 0 && ND(a)->n.mem[OUT + 1] == (v4_cell)V4_PORTS + 0, "the one at the lower place has its offer taken"); - CHECK(ND(b)->n.mem[OUT] == 5 && ND(b)->n.mem[OUT + 1] == 3, "the other gets the word, from the port it came by"); - CHECK(ND(a)->n.offers == 0 && ND(b)->n.offers == 0 && ND(a)->n.mem[OUT + 2] == 1 && ND(b)->n.mem[OUT + 2] == 1, "and neither has an offer left"); - - /* three in a ring, each offering to the next */ - fresh(); - a = loaded(0); offer(0, 11); wait_into(OUT); done_mark(); wait_for_ever(); - b = loaded(1); offer(0, 12); wait_into(OUT); done_mark(); wait_for_ever(); - c = loaded(2); offer(0, 13); wait_into(OUT); done_mark(); wait_for_ever(); - CHECK(v4_asm_ok(&as) && v4_fabric_wire(&f, a, 0, b, 1) && v4_fabric_wire(&f, b, 0, c, 1) && v4_fabric_wire(&f, c, 0, a, 1), "three nodes in a ring, each offering to the next"); - CHECK(settle(1000) < 1000, "they come to rest"); - CHECK(ND(a)->n.mem[OUT + 1] == (v4_cell)V4_PORTS + 0 && ND(b)->n.mem[OUT] == 11 && ND(b)->n.mem[OUT + 1] == 1, "the first's offer was taken by the second"); - CHECK(v4_node_in_wait(&ND(c)->n) && ND(c)->n.offers == 1u && ND(c)->n.mem[OUT + 2] == 0 && !ND(a)->n.given, "the third still offers to the first, which is not reading: nothing was handed over twice"); - - /* a neighbour that is asleep */ - fresh(); - a = loaded(0); offer(0, 7); wait_into(OUT); done_mark(); wait_for_ever(); - b = loaded(1); LIT(PB + 3); O(BANG_B); O(FETCH_B); LIT(OUT); O(BANG_A); O(STORE_A); wait_for_ever(); - CHECK(v4_asm_ok(&as) && v4_fabric_wire(&f, a, 0, b, 3), "a node that offers, wired to a reader"); - v4_fabric_sleep(&f, a); - for (i = 0; i < 50; i++) (void)v4_fabric_step(&f); - v4_fabric_sleep(&f, b); v4_fabric_wake(&f, a); - for (i = 0; i < 100; i++) (void)v4_fabric_step(&f); - CHECK(v4_node_in_wait(&ND(a)->n) && ND(b)->n.reading && !ND(b)->n.given, "while the reader is asleep the offer stands"); - v4_fabric_wake(&f, b); - (void)settle(300); - CHECK(ND(b)->n.mem[OUT] == 7 && ND(a)->n.mem[OUT + 2] == 1, "woken, it takes it"); - - /* a port with nothing on it */ - fresh(); - a = loaded(0); offer(5, 7); wait_into(OUT); done_mark(); wait_for_ever(); - CHECK(v4_asm_ok(&as) && v4_fabric_wire_device(&f, a, 7, &sink), "a node that offers on a port nothing is wired to (the port it ends by reading has a device that gives nothing)"); - for (i = 0; i < 50; i++) (void)v4_fabric_step(&f); - v4_fabric_gone_error(&f, 18); - for (i = 0; i < 50; i++) (void)v4_fabric_step(&f); - CHECK(v4_node_in_wait(&ND(a)->n) && ND(a)->n.faults == 0, "a bare node waits, though the fabric has an error for it"); + /* once the first word has gone, it is not let go: the message must be finished */ + v4_fabric_init(&f, places, 4); + for (i = 0; i < 3; i++) v4_node_reset(&pool[i].n); + a = loaded(0); + LIT(PB + (v4_cell)V4_PORTS + 3); O(BANG_B); O(FETCH_B); O(DROP); + LIT(PB + 0); O(BANG_B); LIT(1); O(STORE_B); LIT(2); O(STORE_B); wait_for_ever(); + b = loaded(1); /* reads one word, and no more */ + LIT(PB + 3); O(BANG_B); O(FETCH_B); LIT(OUT); O(BANG_A); + { + v4_cell again = v4_asm_label(&as); + O(FETCH_A); LIT(1); O(ADD); O(STORE_A); + v4_asm_branch(&as, V4_OP_JUMP, again); + } + CHECK(v4_asm_ok(&as) && v4_fabric_wire(&f, a, 0, b, 3) && v4_fabric_wire_device(&f, a, 5, &dev), "the same, to a node that reads one word and no more"); v4_node_error_attach(&ND(a)->n, OUT + 20); v4_node_fault_attach(&ND(a)->n, 900); - for (i = 0; i < 100 && ND(a)->n.mem[OUT + 2] == 0; i++) (void)v4_fabric_step(&f); /* until it has noted what it was told */ - CHECK(!v4_node_in_wait(&ND(a)->n) && ND(a)->n.faults == 0 && ND(a)->n.offers == 0 && ND(a)->n.mem[OUT] == 0 && ND(a)->n.mem[OUT + 1] == 2 * (v4_cell)V4_PORTS + 5 && ND(a)->n.mem[OUT + 2] == 1, - "one that can take an error is woken and told there is nothing on port 5: no error is raised, and its offer is withdrawn: %ld", (long)ND(a)->n.mem[OUT + 1]); - - /* offers are withdrawn by a store to the wait (found by the review of step 6d) */ - fresh(); - a = loaded(0); offer(0, 7); LIT(0); LIT(PB + (v4_cell)V4_PORT_WAIT); O(BANG_A); O(STORE_A); done_mark(); wait_for_ever(); - for (i = 0; i < 50; i++) (void)v4_fabric_step(&f); - CHECK(v4_asm_ok(&as) && ND(a)->n.mem[OUT + 2] == 1 && ND(a)->n.offers == 0 && ND(a)->n.faults == 0, "a node that offers and then stores to the wait has no offer left"); - - /* the wait for an offer only: a word that comes for the node does not end it */ - fresh(); - a = loaded(0); offer(0, 7); wait1_into(OUT); done_mark(); wait_for_ever(); - b = loaded(1); count_for_ever(); - c = loaded(2); LIT(PB + 0); O(BANG_B); LIT(9); O(STORE_B); wait_for_ever(); - CHECK(v4_asm_ok(&as) && v4_fabric_wire(&f, a, 0, b, 3) && v4_fabric_wire(&f, c, 0, a, 2), "a node waits for its offer only, to a node that never reads, while a third writes to it"); - for (i = 0; i < 200; i++) (void)v4_fabric_step(&f); - CHECK(v4_node_in_wait(&ND(a)->n) && ND(a)->n.mem[OUT + 2] == 0 && ND(c)->n.asking && (ND(c)->n.readers & 1u) == 0, "it goes on waiting; the writer is not served, and does not see it as reading"); - CHECK(v4_fabric_remove(&f, b) != 0, "the node offered to is removed"); - v4_fabric_gone_error(&f, 18); - v4_node_error_attach(&ND(a)->n, OUT + 20); - v4_node_fault_attach(&ND(a)->n, 900); - CHECK(v4_fabric_wire_device(&f, a, 7, &sink), "(the port it ends by reading has a device that gives nothing)"); - for (i = 0; i < 100 && ND(a)->n.mem[OUT + 2] == 0; i++) (void)v4_fabric_step(&f); - CHECK(ND(a)->n.mem[OUT + 1] == 2 * (v4_cell)V4_PORTS + 0 && ND(a)->n.faults == 0 && ND(a)->n.offers == 0 && ND(c)->n.asking, "it is told there is nothing on that port; no error is raised; the writer still waits"); - fresh(); - a = loaded(0); offer(0, 7); wait1_into(OUT); done_mark(); wait_for_ever(); - b = loaded(1); LIT(PB + 3); O(BANG_B); O(FETCH_B); LIT(OUT); O(BANG_A); O(STORE_A); wait_for_ever(); - CHECK(v4_asm_ok(&as) && v4_fabric_wire(&f, a, 0, b, 3), "the same, to a node that reads"); - (void)settle(300); - CHECK(ND(b)->n.mem[OUT] == 7 && ND(a)->n.mem[OUT + 1] == (v4_cell)V4_PORTS + 0 && ND(a)->n.mem[OUT + 2] == 1, "its offer is taken, and it is told"); - - /* a host with one node, whose devices always take */ - fresh(); - a = loaded(0); offer(1, 21); offer(3, 23); wait_into(OUT); done_mark(); wait_for_ever(); - for (i = 0; i < 50; i++) (void)v4_fabric_step(&f); - CHECK(v4_node_in_wait(&ND(a)->n) && ND(a)->n.offers == ((1u << 1) | (1u << 3)), "a node offers on two ports and is in the wait"); - CHECK(v4_node_offer_as_write(&ND(a)->n) && ND(a)->n.asking && ND(a)->n.ask_port == 1 && ND(a)->n.request == 21, "its host turns that into a write of the lower port's word"); - v4_node_port_served(&ND(a)->n); - for (i = 0; i < 50; i++) (void)v4_fabric_step(&f); - CHECK(!ND(a)->n.asking && ND(a)->n.mem[OUT] == 0 && ND(a)->n.mem[OUT + 1] == (v4_cell)V4_PORTS + 1 && ND(a)->n.mem[OUT + 2] == 1 && ND(a)->n.offers == 0, - "and when it is served the node is told that offer was taken"); - CHECK(!v4_node_offer_as_write(&ND(a)->n), "a node that is not in the wait is left alone"); + v4_fabric_interrupt_error(&f, 21); + for (i = 0; i < 60; i++) (void)v4_fabric_step(&f); + CHECK(ND(a)->n.asking && ND(a)->n.request == 2 && ND(a)->n.faults == 0, "blocked on its second word with the device still pending, the writer waits"); } printf(" %d checks, %d failures\n", checks, failures); diff --git a/v4/tests/test_host_mesh.c b/v4/tests/test_host_mesh.c index bfc8082d..cde5eaa7 100644 --- a/v4/tests/test_host_mesh.c +++ b/v4/tests/test_host_mesh.c @@ -71,7 +71,8 @@ static int console_take(void *self, v4_cell value) } return 1; } -static const v4_device console = { console_take, console_give, 0 }; +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 }; static int is_empty(const char *s) { return s[0] == 0; } @@ -111,38 +112,12 @@ static unsigned starforth_node(unsigned k) return (unsigned)id; } -/* The same, answering with what has been printed: for when a node is - * spinning and the fabric never comes to rest. */ -static const char *said(v4_cell node, const char *text) { (void)tell(node, text); return printed; } - static int waiting(unsigned id) { const v4_node *n = &v4_fabric_node_at(&f, id)->n; return n->reading && !n->given && n->read_port == V4_PORT_ANY; } -/* Three nodes in a row once more, 10 at the console, 11, 12: a new fabric, each - * told who it is and the way to the others. For the scenes at the end. */ -static unsigned starforth_node(unsigned k); -static void row_again(unsigned *hera, unsigned *mid, unsigned *far) -{ - unsigned i; - v4_fabric_init(&f, places, 3); - v4_fabric_gone_error(&f, V4_ERROR_NO_ONE); - *hera = starforth_node(0); *mid = starforth_node(1); *far = starforth_node(2); - (void)v4_fabric_wire_device(&f, *hera, 1, &console); - (void)v4_fabric_wire(&f, *hera, 2, *mid, 2); (void)v4_fabric_wire(&f, *mid, 3, *far, 2); - going.count = 0; going_at = 0; - step_cap = 20000000; - for (i = 0; i < 20; i++) (void)v4_fabric_step(&f); - (void)tell(0, "10 (ME) ! 1 (CONSOLE) ! 1 1 ROUTE 0 2 ROUTE"); - (void)tell(0, "11 (ME) ! 1 (CONSOLE) ! 2 DEFAULT-ROUTE 0 3 ROUTE"); - (void)tell(0, "12 (ME) ! 1 (CONSOLE) ! 2 DEFAULT-ROUTE"); - (void)tell(10, "11 2 ROUTE 12 2 ROUTE 11 2 NEIGHBOUR"); - (void)tell(11, "12 3 ROUTE 10 2 NEIGHBOUR"); - (void)tell(12, "11 2 NEIGHBOUR"); -} - int main(void) { unsigned hera, mid, far, i; @@ -327,12 +302,12 @@ int main(void) /* no room: a node that is waiting keeps what it is sent, and has room for only so much */ { - v4_cell lost = pool[1].n.mem[LOST], refused = pool[0].n.mem[REFUSED], got, not_taken, told_of; + v4_cell lost = pool[1].n.mem[LOST], refused = pool[0].n.mem[REFUSED], got, not_taken; CHECK(is_empty(tell(11, "0 GOT !")) && is_empty(tell(11, "12 AWAIT .")) && ended == 0 && pool[1].n.mem[AWAIT_FROM] == 12, "node 11 waits for an answer from 12, which is not going to send one"); CHECK(is_empty(tell(10, ": F 49 0 DO S\" HIT\" 11 SEND LOOP ; F")) && ended == 1 && ended_from == 10, "node 10 sends it 49 messages meanwhile"); CHECK(pool[1].n.mem[MQ_COUNT] > 300 && pool[1].n.mem[MQ_COUNT] <= MQ_CELLS - 36, "node 11 has kept what it has room for, and left the room that is kept for refusals: %ld cells", (long)pool[1].n.mem[MQ_COUNT]); - CHECK(pool[1].n.mem[OWED_COUNT] == 0 && pool[0].n.mem[REFUSED] >= refused + 8, "it owed a refusal for each of the rest, as many as it could owe at once, and paid them while it waited: node 10 was told of %ld", - (long)(pool[0].n.mem[REFUSED] - refused)); + CHECK(pool[1].n.mem[OWED_COUNT] == 8, "it owes a refusal for each of the rest, as many as it can owe: %ld", (long)pool[1].n.mem[OWED_COUNT]); + CHECK(pool[0].n.mem[REFUSED] == refused, "and has sent none yet: it is still waiting"); /* one from its centre ends the wait */ CHECK(is_empty(tell(10, "12 NO-ROUTE")), "node 10 forgets the way to 12"); @@ -341,10 +316,9 @@ int main(void) got = strtol(tell(11, "GOT @ ."), NULL, 10); not_taken = 49 - got; CHECK(got >= 30 && not_taken > 8, "it then did every message it had kept: %ld of the 49", (long)got); - told_of = pool[0].n.mem[REFUSED] - refused; - CHECK(told_of >= 8 && told_of <= not_taken, "node 10 was told of %ld of them", (long)told_of); - CHECK(pool[1].n.mem[LOST] == lost + not_taken + (not_taken - told_of), "it counted each message it did not take, and each refusal it could not owe: %ld for %ld not taken, %ld told", - (long)(pool[1].n.mem[LOST] - lost), (long)not_taken, (long)told_of); + CHECK(pool[0].n.mem[REFUSED] == refused + 8, "and sent the eight refusals it owed: node 10 was told of %ld", (long)(pool[0].n.mem[REFUSED] - refused)); + CHECK(pool[1].n.mem[LOST] == lost + not_taken + (not_taken - 8), "it counted each message it did not take, and each refusal it could not owe: %ld for %ld not taken", + (long)(pool[1].n.mem[LOST] - lost), (long)not_taken); /* the way to 12 is forgotten: what is sent there now is refused */ refused = pool[1].n.mem[REFUSED]; CHECK(is_empty(tell(11, ": P12 S\" 1 DROP\" 12 SEND ; P12")) && pool[1].n.mem[REFUSED] == refused + 1, "having forgotten the way to 12, what node 11 sends there goes toward its centre, which has no way either, and is refused"); @@ -352,11 +326,11 @@ int main(void) } /* a line from the console breaks a wait (MESH.md 7b.6) */ CHECK(is_empty(tell(10, "12 AWAIT .")) && ended == 0 && pool[0].n.mem[AWAIT_FROM] == 12 && waiting(hera), "node 10 waits for an answer from 12, which is not going to send one"); - CHECK(strcmp(tell(11, "20 22 + ."), "42 ") == 0 && ended == 1 && pool[0].n.mem[AWAIT_FROM] == 12, "text from the console for node 11 comes to node 10 on its way: node 10 passes it on, and goes on waiting: \"%s\"", printed); + CHECK(is_empty(tell(11, "20 22 + .")) && ended == 0 && pool[0].n.mem[AWAIT_FROM] == 12, "text from the console for node 11 comes to node 10 on its way: node 10 keeps it, and goes on waiting"); { const char *r = tell(10, "65 EMIT"); CHECK(strstr(r, "Interrupted\n") != NULL && pool[0].n.mem[AWAIT_FROM] == 0, "text from the console for node 10 itself ends its wait, with an error that says so: \"%s\"", r); - CHECK(strstr(r, "A") != NULL && ended == 2, "the line is then done as usual: \"%s\", %d ended", r, ended); + CHECK(strstr(r, "A") != NULL && strstr(r, "42 ") != NULL && ended == 3, "the line is then done as usual, and so is what was kept for node 11: \"%s\", %d ended", r, ended); CHECK(strstr(r, "Interrupted\n") < strstr(r, "A"), "the wait ends before the line that ended it is done"); } CHECK(strcmp(tell(10, "1 2 + ."), "3 ") == 0 && waiting(hera) && waiting(mid) && waiting(far), "and all three go on as before"); @@ -390,10 +364,9 @@ int main(void) { CHECK(is_empty(tell(11, "0 GOT !")) && is_empty(tell(11, "12 AWAIT .")) && pool[1].n.mem[AWAIT_FROM] == 12, "node 11 waits on 12 again"); CHECK(is_empty(tell(10, ": F40 40 0 DO S\" HIT\" 11 SEND LOOP ; F40")) && pool[1].n.mem[OWED_COUNT] == 0, "node 10 sends it as many messages as it has room for"); - /* node 11 does what it has kept before it pays what it owes (7b.3): forty answers come first */ - (void)tell(10, ": ONE S\" HIT\" 11 SEND 12 NO-ROUTE 12 11 GONE 41 0 DO 11 AWAIT DROP LOOP ; ONE"); + (void)tell(10, ": ONE S\" HIT\" 11 SEND 12 NO-ROUTE 12 11 GONE 11 AWAIT . ; ONE"); CHECK(strstr(printed, "Message refused") != NULL && pool[0].n.mem[AWAIT_FROM] == 0, - "it sends one more, for which there is no room, ends node 11's wait, and waits for node 11's answers: after the forty, its wait ends, the message refused: \"%s\"", printed); + "it sends one more, for which there is no room, ends node 11's wait, and waits for node 11's answer: its own wait ends, the message refused: \"%s\"", printed); CHECK(strtol(tell(11, "GOT @ ."), NULL, 10) == 40 && is_empty(tell(10, "12 3 ROUTE")) && is_empty(tell(11, "12 3 ROUTE")), "node 11 did the forty it had kept; the ways are told again"); } @@ -420,180 +393,34 @@ int main(void) CHECK(is_empty(tell(10, "66 NO-ROUTE")) && waiting(hera) && waiting(mid) && waiting(far), "the way is forgotten, and all three are at rest"); } - /* ---- what a node passes on keeps its order, however long it must be kept (MESH.md 7c) ---- */ - CHECK(is_empty(tell(11, "12 2 ROUTE")), "node 11 is told the way to node 12 is by node 10"); - CHECK(is_empty(tell(12, ": SLOW 30000 0 DO LOOP ;")), "node 12 is given a word that takes a while"); - CHECK(strcmp(tell(11, ": TRIO S\" SLOW 68 EMIT\" 12 SEND S\" 69 EMIT\" 12 SEND S\" 70 EMIT\" 12 SEND ; TRIO"), "DEF") == 0, - "it sends node 12 three texts, the first slow to do: node 10 keeps the other two until node 12 reads again, and they are done in order: \"%s\"", printed); - CHECK(is_empty(tell(11, "12 3 ROUTE")) && waiting(hera) && waiting(mid) && waiting(far) && pool[0].n.mem[MQ_COUNT] == 0, "the way is told again; all three are at rest, keeping nothing"); - - /* ---- a node that is stuck holds up no one (MESH.md 7c) ------------------- */ - v4_fabric_gone_error(&f, V4_ERROR_NO_ONE); - - /* From here a node is spinning, so the fabric never comes to rest: said() - * is tell(), answering with what was printed by the time it gave up. */ - - /* a refusal owed to a stuck node whose number is the lower */ - CHECK(is_empty(said(12, "NO-ROUTES 1 3 ROUTE 10 3 ROUTE 11 2 ROUTE")) && is_empty(said(11, "55 3 ROUTE")), "node 12 has no way for 55; node 11 is told the way to 55 is by node 12"); - CHECK(strcmp(tell(11, ": C1 S\" 1 DROP\" 55 SEND S\" 1 DROP\" 12 SEND BEGIN 0 UNTIL ; C1"), "(still running)") == 0, "node 11 sends 55 a message, and node 12 text, and is then stuck in a loop"); + /* a node that owes a refusal to a node that is stuck is not stuck itself */ + CHECK(is_empty(tell(12, "NO-ROUTES 1 3 ROUTE 10 3 ROUTE 11 2 ROUTE")) && is_empty(tell(11, "55 3 ROUTE")), "node 12 has no way for 55; node 11 is told the way to 55 is by node 12"); + CHECK(strcmp(tell(11, ": C1 S\" 1 DROP\" 55 SEND BEGIN 0 UNTIL ; C1"), "(still running)") == 0, "node 11 sends 55 a message and is then stuck in a loop"); CHECK(pool[2].n.mem[OWED_COUNT] == 1, "node 12 owes node 11 a refusal, and cannot give it: node 11 is not reading"); - CHECK(strcmp(said(12, "7 8 * ."), "56 ") == 0 && printed_from == 12, "node 12 goes on doing what it is sent: \"%s\"", printed); - { - v4_uheat_t clock = pool[2].es.anticlock; - unsigned k; - for (k = 0; k < 1000; k++) (void)v4_fabric_step(&f); - CHECK(v4_node_in_wait(&pool[2].n) && pool[2].es.anticlock == clock && pool[2].n.mem[OWED_COUNT] == 1, "between times it sleeps in the wait, still owing, and executes nothing"); - } - CHECK(strcmp(said(10, ": X12 S\" 66 EMIT\" 12 SEND ; X12"), "B") == 0 && printed_from == 12, "another node writes to it and is answered: \"%s\"", printed); + (void)tell(12, "7 8 * ."); + CHECK(strcmp(printed, "56 ") == 0 && printed_from == 12, "node 12 goes on doing what it is sent: \"%s\"", printed); - /* two refusals owed: one to the stuck node, one to a node that waits for it */ - CHECK(is_empty(said(10, "77 3 ROUTE")), "node 10 is told the way to 77 is by node 12, which has none"); - CHECK(strstr(said(10, ": W77B S\" 1 DROP\" 77 SEND 77 AWAIT . ; W77B"), "Message refused") != NULL && pool[0].n.mem[AWAIT_FROM] == 0 && pool[2].n.mem[OWED_COUNT] == 1, - "node 10 sends 77 a message and waits: node 12, which still owes the stuck node, tells node 10 it was refused: \"%s\"", printed); - CHECK(is_empty(said(10, "77 NO-ROUTE")), "the way is forgotten"); - - /* a message passed on toward the stuck node waits with the node passing it on, which goes on serving */ - { - v4_cell kept = pool[0].n.mem[MQ_COUNT]; - CHECK(is_empty(said(11, "1 DROP")) && ended == 0 && pool[0].n.mem[MQ_COUNT] > kept && v4_node_in_wait(&pool[0].n), - "text from the console for the stuck node comes to node 10, which cannot hand it on: it keeps it, offered, and sleeps"); - CHECK(strcmp(said(10, "1 2 + ."), "3 ") == 0 && strcmp(said(12, "7 8 * ."), "56 ") == 0 && printed_from == 12, - "node 10 goes on doing what it is sent, and passing on what is for others: \"%s\"", printed); - CHECK(pool[0].n.mem[MQ_COUNT] > kept && v4_node_in_wait(&pool[0].n), "still keeping what is for the stuck node"); - } - /* the stuck node's own answer: node 12 did text node 11 sent before it stuck, and cannot give it the answer */ - CHECK(pool[2].n.mem[MQ_COUNT] > 0 && v4_node_in_wait(&pool[2].n), "node 12 keeps the answer it has for node 11, offered, and sleeps"); - - /* a line that must begin a message to the stuck node waits, and the node takes in what comes */ - CHECK(is_empty(said(10, ": TO11 S\" 1 DROP\" 11 SEND 65 EMIT ; TO11")) && ended == 0 && v4_node_in_wait(&pool[0].n) && !pool[0].n.asking, - "node 10 sends the stuck node a message: its line waits in the wait, not blocked writing"); - { - v4_cell kept = pool[0].n.mem[MQ_COUNT]; - CHECK(is_empty(said(12, "7 8 * .")) && ended == 0 && pool[0].n.mem[MQ_COUNT] > kept && v4_node_in_wait(&pool[0].n), - "text from the console for node 12 comes to node 10: it keeps it, and its line goes on waiting"); - } - { - const char *r = said(10, "66 EMIT"); - CHECK(strstr(r, "Interrupted\n") != NULL && strstr(r, "A") == NULL && strstr(r, "B") != NULL && strstr(r, "Interrupted\n") < strstr(r, "B"), - "text from the console for node 10 itself ends the line that waited, with an error that says so, and is then done: \"%s\"", r); - CHECK(strstr(r, "56 ") != NULL && ended == 3, "and what it kept for node 12 is passed on and done: \"%s\", %d ended", r, ended); - } - - /* the stuck node is removed while a line waits to begin a message to it */ - CHECK(is_empty(said(10, "TO11")) && ended == 0 && v4_node_in_wait(&pool[0].n), "node 10's line waits on the stuck node again"); + /* a node that is waiting to write to a node finds out when that node is removed */ + (void)tell(12, ": TO11 S\" 1 DROP\" 11 SEND 65 EMIT ; TO11"); + CHECK(ended == 0, "node 12 sends node 11 a message: node 11 is not reading, and node 12 waits to write to it"); CHECK(v4_fabric_remove(&f, mid) == &pool[1], "node 11 is removed"); - { - unsigned k; - for (k = 0; k < 200000 && ended == 0; k++) (void)v4_fabric_step(&f); - CHECK(strstr(printed, "No one on that port") != NULL && strstr(printed, "A") == NULL && ended == 1, "the line ends in an error that says no one is there: \"%s\"", printed); - } + (void)tell(10, "1 DROP"); + CHECK(strstr(printed, "No one on that port") != NULL && strstr(printed, "A") == NULL, "node 12's line ends in an error that says no one is there: \"%s\"", printed); step_cap = 20000000; - CHECK(strcmp(said(12, "7 8 * ."), "56 ") == 0 && pool[2].n.mem[OWED_COUNT] == 0 && pool[2].n.mem[MQ_COUNT] == 0 && pool[0].n.mem[MQ_COUNT] == 0 && waiting(far) && !v4_node_in_wait(&pool[2].n) && waiting(hera), - "node 12 goes on; what it owed node 11 and kept for it, and what node 10 kept for it, are let go: they owe and keep nothing, and are at rest"); + CHECK(strcmp(tell(12, "7 8 * ."), "56 ") == 0 && pool[2].n.mem[OWED_COUNT] == 0 && waiting(far) && waiting(hera), + "it goes on, the refusal it owed node 11 let go: it owes nothing and is at rest"); - /* a refusal owed to a stuck node whose number is the higher */ + /* THE LIMIT (MESH.md 7b.7): a refusal owed to a neighbour whose number is the higher is written + * without looking, as any message to it is, so a node that is stuck holds up a neighbour that owes + * it one -- until it is removed */ step_cap = 3000000; - CHECK(is_empty(said(12, "55 3 ROUTE")), "node 12 is told the way to 55 is by node 10, which has none"); - CHECK(strcmp(tell(12, ": C2 S\" 1 DROP\" 55 SEND BEGIN 0 UNTIL ; C2"), "(still running)") == 0 && pool[0].n.mem[OWED_COUNT] == 1 && !pool[0].n.asking, - "node 12 sends 55 a message and is then stuck in a loop: node 10 owes it a refusal, and is not blocked writing it"); - step_cap = 20000000; - CHECK(strcmp(said(10, "1 2 + ."), "3 ") == 0 && pool[0].n.mem[OWED_COUNT] == 1, "node 10 goes on doing what it is sent, still owing: \"%s\"", printed); - { - v4_uheat_t clock; - unsigned k; - for (k = 0; k < 1000; k++) (void)v4_fabric_step(&f); - clock = pool[0].es.anticlock; - for (k = 0; k < 1000; k++) (void)v4_fabric_step(&f); - CHECK(v4_node_in_wait(&pool[0].n) && pool[0].es.anticlock == clock, "and sleeps in the wait between times"); - } + CHECK(is_empty(tell(12, "55 3 ROUTE")), "node 12 is told the way to 55 is by node 10, which has none"); + CHECK(strcmp(tell(12, ": C2 S\" 1 DROP\" 55 SEND BEGIN 0 UNTIL ; C2"), "(still running)") == 0 && pool[0].n.asking && pool[0].n.mem[OWED_COUNT] == 1, + "node 12 sends 55 a message and is then stuck in a loop: node 10 owes it a refusal, writes it, and is blocked"); + v4_fabric_gone_error(&f, V4_ERROR_NO_ONE); CHECK(v4_fabric_remove(&f, far) == &pool[2], "node 12 is removed"); - CHECK(strcmp(said(10, "1 2 + ."), "3 ") == 0 && pool[0].n.mem[OWED_COUNT] == 0 && waiting(hera) && !v4_node_in_wait(&pool[0].n), "node 10 lets the refusal go and is at rest: \"%s\"", printed); - - /* ==== what the review of step 6d found: each scene on a row of three, begun again ==== */ - - /* a node is removed while a message is being passed on to it */ - { - static char line[240]; - unsigned steps = 0, k; - unsigned depth; - row_again(&hera, &mid, &far); - CHECK(strcmp(tell(12, "7 8 * ."), "56 ") == 0, "three nodes in a row again"); - depth = pool[1].n.ds.depth; - memset(line, ' ', 200); memcpy(line, "1 DROP", 6); line[200] = 0; - printed_len = 0; printed[0] = 0; ended = 0; - (void)v4_message_text(&going, 12, CONSOLE_ID, V4_MSG_TEXT, line, 200); going_at = 0; - while (steps < 2000000 && !(pool[1].n.mem[MQ_COUNT] > 0 && pool[1].n.mem[MQ_COUNT] < 40 && pool[2].n.reading && pool[2].n.read_port == 2)) { (void)v4_fabric_step(&f); steps++; } - CHECK(steps < 2000000, "long text for node 12 is on its way through node 11, part written: %ld cells of it still with node 11", (long)pool[1].n.mem[MQ_COUNT]); - CHECK(v4_fabric_remove(&f, far) == &pool[2], "node 12 is removed"); - for (k = 0; k < 3000000 && v4_fabric_step(&f) != 0; k++) { } - CHECK(k < 3000000 && pool[1].n.mem[MQ_COUNT] == 0 && waiting(mid) && pool[1].n.ds.depth == depth, "node 11 comes to rest: it has let the rest go, keeps nothing, and its stack is as it was (%ld cells kept)", (long)pool[1].n.mem[MQ_COUNT]); - CHECK(strcmp(tell(11, "20 22 + ."), "42 ") == 0 && strcmp(tell(10, "1 2 + ."), "3 ") == 0, "and it and node 10 go on"); - } - - /* a node is removed while it is writing a message to its neighbour: the half that came is not kept */ - { - static char line[240]; - unsigned steps = 0, k; - row_again(&hera, &mid, &far); - memset(line, ' ', 200); memcpy(line, "1 DROP", 6); line[200] = 0; - printed_len = 0; printed[0] = 0; ended = 0; - (void)v4_message_text(&going, 12, CONSOLE_ID, V4_MSG_TEXT, line, 200); going_at = 0; - while (steps < 2000000 && !(pool[2].n.mem[MQ_COUNT] > 8 && pool[2].n.reading && pool[2].n.read_port == 2)) { (void)v4_fabric_step(&f); steps++; } - CHECK(steps < 2000000, "node 12 has taken in part of a long text from node 11: %ld cells", (long)pool[2].n.mem[MQ_COUNT]); - CHECK(v4_fabric_remove(&f, mid) == &pool[1], "node 11 is removed"); - for (k = 0; k < 3000000 && v4_fabric_step(&f) != 0; k++) { } - CHECK(k < 3000000 && pool[2].n.mem[MQ_COUNT] == 0 && waiting(far), "node 12 comes to rest, and has not kept the half: %ld cells", (long)pool[2].n.mem[MQ_COUNT]); - CHECK(v4_fabric_wire(&f, hera, 3, far, 3) && is_empty(tell(10, "12 NO-ROUTE 12 3 ROUTE")), "node 12 is wired to node 10 instead"); - (void)tell(12, "NO-ROUTES 1 3 ROUTE 10 3 ROUTE"); - CHECK(strcmp(tell(12, "7 8 * ."), "56 ") == 0, "and does what it is sent"); - } - - /* what a finished text printed, with the way to the console through a stuck node */ - row_again(&hera, &mid, &far); - CHECK(v4_fabric_wire(&f, hera, 3, far, 3) && is_empty(tell(10, "NO-ROUTES 1 1 ROUTE 11 2 ROUTE 12 3 ROUTE")) && strcmp(tell(12, "3 4 + ."), "7 ") == 0, - "node 12 is reached straight from node 10; what it prints still goes back by node 11"); - step_cap = 400000; - CHECK(strcmp(tell(11, ": ST BEGIN 0 UNTIL ; ST"), "(still running)") == 0, "node 11 is stuck in a loop"); - (void)said(10, ": P S\" 65 EMIT\" 12 SEND ; P"); - CHECK(v4_node_in_wait(&pool[2].n) && pool[2].n.mem[MQ_COUNT] > 0, "node 10 sends node 12 text that prints: what it printed, and how it ended, wait with node 12, offered, and it sleeps"); - (void)said(10, ": Q S\" 9 (REFUSED) !\" 12 SEND ; Q"); - (void)said(12, "8 (REFUSED) !"); - CHECK(pool[2].n.mem[REFUSED] == 0 && v4_node_in_wait(&pool[2].n), - "the next text from node 10, and text typed at the console for it, are kept back: what it owes each from the last has not gone (MESH.md 7c.3). It sleeps in the wait"); - { v4_cell kept = pool[2].n.mem[MQ_COUNT]; - (void)said(10, ": R S\" 7 (REFUSED) !\" 12 SEND ; R"); - CHECK(pool[2].n.mem[MQ_COUNT] > kept && v4_node_in_wait(&pool[2].n), "it is not held: it takes in what it is sent, and sleeps again"); } - CHECK(v4_fabric_remove(&f, mid) == &pool[1], "node 11 is removed"); - { unsigned k; for (k = 0; k < 3000000 && v4_fabric_step(&f) != 0; k++) { } - CHECK(k < 3000000 && pool[2].n.mem[REFUSED] == 7 && pool[2].n.mem[MQ_COUNT] == 0 && waiting(far), "what could not go is let go, the texts kept back are done in the order they came, and node 12 is at rest: %ld", (long)pool[2].n.mem[REFUSED]); } - - /* offers left standing when a node goes round to begin text */ - row_again(&hera, &mid, &far); - CHECK(v4_fabric_wire(&f, hera, 3, far, 3) && v4_fabric_wire(&f, hera, 4, far, 4), "nodes 10 and 12 are wired to each other twice over"); - (void)tell(10, "NO-ROUTES 1 1 ROUTE 11 2 ROUTE 12 3 ROUTE 77 3 ROUTE"); - (void)tell(10, ": SLOW 400000 0 DO LOOP ; : Z S\" 1 DROP\" 77 SEND SLOW ;"); - (void)tell(12, ": FWD99 S\" 67 EMIT\" 99 SEND ;"); - (void)tell(12, "NO-ROUTES 10 3 ROUTE 11 2 ROUTE 1 4 ROUTE 99 4 ROUTE"); - CHECK(strcmp(tell(12, "3 4 + ."), "7 ") == 0, "node 12's way to the console, and to 99, is by its other wire to node 10"); - step_cap = 300000; - CHECK(strcmp(tell(11, ": C S\" 1 DROP\" 12 SEND S\" FWD99\" 12 SEND BEGIN 0 UNTIL ; C"), "(still running)") == 0, "node 11 sends node 12 two texts and is stuck"); - step_cap = 30000; - (void)said(10, "Z"); - CHECK(pool[2].n.mem[OWED_COUNT] == 1, "node 10 sends 77 a message by node 12, which has no way, and is then busy: node 12 owes it a refusal, offered on its first wire"); - CHECK(v4_fabric_remove(&f, mid) == &pool[1], "node 11 is removed: node 12 lets its answer go, and begins the text it kept back"); - printed_len = 0; printed[0] = 0; printed_from = -1; - { unsigned k; for (k = 0; k < 20000000 && v4_fabric_step(&f) != 0; k++) { } - CHECK(k < 20000000, "all come to rest"); } - CHECK(strchr(printed, 'C') == NULL, "the text node 12 sent to 99 was not done by node 10: \"%s\" from %ld", printed, (long)printed_from); - CHECK(pool[0].n.mem[REFUSED] == 1 && pool[2].n.mem[OWED_COUNT] == 0, "node 10 was told its message to 77 was refused: %ld", (long)pool[0].n.mem[REFUSED]); - - /* many of a node's own messages waiting for a stuck node */ - row_again(&hera, &mid, &far); - step_cap = 300000; - CHECK(strcmp(tell(11, ": ST BEGIN 0 UNTIL ; ST"), "(still running)") == 0, "node 11 is stuck in a loop"); - { int k, good = 0; - for (k = 1; k <= 12; k++) { (void)said(10, "12 11 GONE"); if (strcmp(said(10, "1 2 + ."), "3 ") == 0) good++; } - CHECK(good == 12 && pool[0].n.mem[MQ_COUNT] == 108, "node 10 is told twelve times to tell it a node is gone, and after each still does what is typed: %d of 12, %ld cells kept", good, (long)pool[0].n.mem[MQ_COUNT]); } + step_cap = 20000000; + CHECK(strcmp(tell(10, "1 2 + ."), "3 ") == 0 && pool[0].n.mem[OWED_COUNT] == 0 && waiting(hera), "node 10 is let go, lets the refusal go, and goes on: \"%s\"", printed); printf(" %d checks, %d failures\n", checks, failures); return failures != 0; diff --git a/v4/tests/test_host_quit.c b/v4/tests/test_host_quit.c index 98fce310..2c6c7fb0 100644 --- a/v4/tests/test_host_quit.c +++ b/v4/tests/test_host_quit.c @@ -63,7 +63,7 @@ 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; /* (LINE): the word after it */ +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 @@ -213,7 +213,6 @@ static int run_line(long max) while (steps < max && idle < 64) { (void)v4_exec_step_word(&n, &es, &h); steps++; - (void)v4_node_offer_as_write(&n); /* in the wait: what it offers is a write, the kernel and the console taking at once */ 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; @@ -871,7 +870,7 @@ 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, "(LINE)"); /* the word after (IDLE) in quit.v4 */ + 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)"); @@ -1134,21 +1133,6 @@ int main(void) } printf(" %u values may wait on the stack while the next line is typed and interpreted\n", most); CHECK(most + 4u >= V4_DATA_DEPTH, "the prompt and the interpreter take no more than four cells of the stack"); - /* one more than may wait: the line ends in an error that says so, and the node goes on (found by the review of step 6d) */ - { - char line[4 * V4_DATA_DEPTH + 16]; - size_t at = 0; - unsigned m; - const char *r; - boot_bare(); - for (m = 0; m < most + 1; m++) at += (size_t)snprintf(line + at, sizeof line - at, "1 "); - snprintf(line + at, sizeof line - at, "\n"); - r = say(line); - CHECK(strstr(r, "Stack overflow") != NULL && strstr(r, " ERROR\nok> ") != NULL, "a line that leaves one value more than may wait ends in an error that says so: \"%s\"", r); - CHECK(strcmp(say("1 2 + .\n"), "3 ok\nok> ") == 0, "and the next line is done as usual"); - for (m = 0; m < 2000 && !node_idle(); m++) (void)v4_exec_step_word(&n, &es, &h); /* it finishes what it was doing when its answer went */ - CHECK(n.mem[MQ_COUNT] == 0 && node_idle(), "with nothing left among the messages waiting, and the node at its ports"); - } } /* ---- how much of the data stack a line has ---- */ diff --git a/v4/tests/test_host_unit.c b/v4/tests/test_host_unit.c index 360a23bf..5167db89 100644 --- a/v4/tests/test_host_unit.c +++ b/v4/tests/test_host_unit.c @@ -140,7 +140,7 @@ static int host_born(void *self) v4_node_fault_attach(&pool[k].n, w_fault); v4_blocks_init(&node_blocks[k], BUF0_W); if (k != 0) { /* Hera's kernel is wired where she is made */ - node_kernel[k].take = node_kernel_take; node_kernel[k].give = 0; node_kernel[k].self = &pool[k]; + node_kernel[k].take = node_kernel_take; node_kernel[k].give = 0; node_kernel[k].self = &pool[k]; node_kernel[k].pending = 0; if (!v4_fabric_wire_device(&f, (unsigned)place, 0, &node_kernel[k])) return -1; } return place; @@ -194,7 +194,7 @@ static int kernel_take(void *self, v4_cell value) /* a request */ if (value < FIRST_REQUEST || !v4_manage_serve(&manage, hera_place, (unsigned)(value - FIRST_REQUEST))) refused++; return 1; } -static const v4_device kernel = { kernel_take, kernel_give, 0 }; +static const v4_device kernel = { kernel_take, kernel_give, 0, 0 }; /* ---- the console: a device on Hera's port 1 ---- */ #define CONSOLE_ID 1 @@ -237,7 +237,8 @@ static int console_take(void *self, v4_cell value) } return 1; } -static const v4_device console = { console_take, console_give, 0 }; +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 @@ -275,8 +276,6 @@ static const char *tell(v4_cell node, const char *text) if (steps >= step_limit) { stuck(); return "(still running)"; } return printed; } -/* The same, answering with what has been printed: for when a node is spinning and the fabric never rests. */ -static const char *said(v4_cell node, const char *text) { (void)tell(node, text); return printed; } static int told(v4_cell node, const char *text) { return tell(node, text)[0] == 0 && ended == 1 && ended_how == V4_TEXT_COMPLETED; } /* POST's host, for the node numbered 10 (v4/include/v4/post.h): a line is @@ -383,6 +382,7 @@ int main(void) /* ---- Hera is born empty, and takes in the nucleus ---- */ v4_fabric_init(&f, places, PLACES); 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; @@ -506,8 +506,8 @@ int main(void) /* 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 && v4_node_in_wait(node_numbered(12)) && !node_numbered(12)->asking, - "node 12, beside it, sends it text: 14 never reads, and 12 waits to begin the message, in the wait"); + 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"); { unsigned born = born_count; const char *r = tell(10, "14 KILL 65 EMIT"); @@ -525,34 +525,21 @@ int main(void) CHECK(strstr(tell(10, ": H14 S\" 1\" 14 SEND ; H14"), "Argument out of range") != NULL, "Hera herself has no way to it"); } - /* two nodes each send a third text and are then stuck: the third has an answer for each that it - * cannot give, and goes on doing what others send it (MESH.md 7c). While a node spins the fabric - * never rests: said() answers with what was printed. */ + /* a node waits on one that is stuck and is not its neighbour; Hera kills the stuck one */ step_limit = 3000000ul; - CHECK(strcmp(tell(12, ": S11 S\" 1 DROP\" 11 SEND BEGIN 0 UNTIL ; S11"), "(still running)") == 0 && - strcmp(tell(13, ": S11 S\" 1 DROP\" 11 SEND BEGIN 0 UNTIL ; S11"), "(still running)") == 0, "nodes 12 and 13, either side of node 11, each send it text and are then stuck in a loop"); - CHECK(node_numbered(11)->mem[MQ_COUNT] == 18 && v4_node_in_wait(node_numbered(11)), "node 11 keeps its answer to each, offered, and sleeps: %ld cells", (long)node_numbered(11)->mem[MQ_COUNT]); - CHECK(strcmp(said(11, "11 100 * ."), "1100 ") == 0 && ended == 1 && node_numbered(11)->mem[MQ_COUNT] == 18, "text typed for node 11 is done all the same: \"%s\"", printed); - - /* a node waits on one that is stuck; text for the stuck one waits with Hera, who goes on; Hera kills it */ - CHECK(said(11, ": W13 13 AWAIT . ; W13")[0] == 0 && ended == 0 && node_numbered(11)->mem[AWAIT_FROM] == 13 && waiting(11), "node 11 waits for node 13's answer"); - { - v4_cell kept = node_numbered(10)->mem[MQ_COUNT]; - (void)tell(13, "1 DROP"); - CHECK(ended == 0 && v4_node_in_wait(node_numbered(10)) && !node_numbered(10)->asking && node_numbered(10)->mem[MQ_COUNT] > kept, - "text typed for the stuck node waits with Hera, offered: she sleeps in the wait, not blocked writing"); - } - CHECK(strcmp(said(10, "1 2 + ."), "3 ") == 0 && ended == 1, "and text for Hera herself is done meanwhile: \"%s\"", printed); - (void)tell(10, "13 KILL 65 EMIT"); - CHECK(node_numbered(13) == NULL && strstr(printed, "Node gone") != NULL && node_numbered(11)->mem[AWAIT_FROM] == 0, - "Hera kills node 13: node 11's wait ends, with an error that says the node is gone: \"%s\"", printed); - CHECK(strstr(printed, "A") != NULL && ended == 2 && node_numbered(10)->mem[MQ_COUNT] == 9 && v4_node_in_wait(node_numbered(10)), - "her line is not held by node 12, which is stuck and has to be told too: that message waits with her, offered, and what she kept for node 13 is let go: %ld cells", - (long)node_numbered(10)->mem[MQ_COUNT]); - CHECK(strcmp(said(11, "11 100 * ."), "1100 ") == 0 && strcmp(said(10, "1 2 + ."), "3 ") == 0, "and the others go on"); + 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(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; + 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 */ - CHECK(said(10, "12 AWAIT .")[0] == 0 && ended == 0 && node_numbered(10)->mem[AWAIT_FROM] == 12 && waiting(10), "Hera waits for node 12's answer, which will not come"); + 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"); { 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, @@ -560,14 +547,12 @@ int main(void) CHECK(node_numbered(12) == NULL && node_numbered(10)->mem[AWAIT_FROM] == 0, "the line killed the stuck node"); } step_limit = 30000000ul; - CHECK(strcmp(tell(11, "11 100 * ."), "1100 ") == 0 && strcmp(tell(10, "1 2 + ."), "3 ") == 0 && node_numbered(11)->mem[MQ_COUNT] == 0 && node_numbered(10)->mem[MQ_COUNT] == 0 && waiting(10) && waiting(11), - "the two that are left go on, and keep nothing for the nodes that are gone"); - /* Hera's line must begin a message to a node that is stuck: it waits, and a line from the console ends it (MESH.md 7c.3) */ + /* 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 && v4_node_in_wait(node_numbered(10)) && !node_numbered(10)->asking, - "Hera sends it text: it never reads, and she waits to begin the message, in the wait"); + 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"); { 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"),