From 59727f762abb908d0feae9e419b44924c5d8202c Mon Sep 17 00:00:00 2001 From: rajames Date: Wed, 7 Oct 2026 22:18:06 -0400 Subject: [PATCH] fix(v4.0.0): step 6c mended after its review -- what a stuck or gone node can and cannot hold up The review of step 6c found it did not keep its rule. Ruled: fix them all; 16 hops. - Paying a NACK no longer waits on a lower-numbered neighbour; a node that owes one does not sleep, and goes on taking in what it is sent. - AWAIT looks first through the messages already waiting. - A node waiting to write to a removed neighbour gets error 18: the word that says who is reading also says which ports have anything on them. - A console line lets Hera go when she is blocked on the first word of a message (v4_fabric_interrupt_error, a device's `pending`). - A refused answer is told to the node that waits, not the one that answered; one owed to the node itself goes to its own queue. - A message is passed on by at most 16 nodes, then refused. - A GONE is believed only when it says it is from the node's centre. - Tests: the check that tested nothing is replaced; no room for a waiting sender; the limit that remains is tested as a limit. - MESH.md 7b's rule reworded to what holds, 7b.7 says where it falls short; README likewise. make -C v4 test, sanitize, hosted-check; three bare-metal boots. POST 538 of 538, word_count=317, dict_hash=0xc0769523a47b7dc3 on all six. Co-Authored-By: Claude Opus 5.5 --- capsules/v4/nucleus-64.f18 | Bin 54272 -> 56608 bytes docs/v4.0.0/MESH.md | 111 +++++++++- .../amd64/qemu-amd64-20261007-220949.log | 165 ++++++++++++++ .../aarch64/qemu-aarch64-20261007-221223.log | 138 ++++++++++++ .../riscv64/qemu-riscv64-20261007-221549.log | 208 ++++++++++++++++++ v4/README.md | 11 +- v4/capsule/core.v4 | 80 +++++-- v4/capsule/quit.v4 | 124 +++++++++-- v4/include/v4/fabric.h | 15 ++ v4/include/v4/image.h | 5 + v4/include/v4/node.h | 14 +- v4/src/fabric.c | 25 +++ v4/src/node.c | 11 +- v4/system/boot.c | 2 +- v4/tests/host_map.h | 8 + v4/tests/test_fabric.c | 63 +++++- v4/tests/test_host_mesh.c | 110 ++++++++- v4/tests/test_host_quit.c | 8 +- v4/tests/test_host_unit.c | 26 ++- 19 files changed, 1058 insertions(+), 66 deletions(-) create mode 100644 logs/20261007-220949/amd64/qemu-amd64-20261007-220949.log create mode 100644 logs/20261007-221223/aarch64/qemu-aarch64-20261007-221223.log create mode 100644 logs/20261007-221549/riscv64/qemu-riscv64-20261007-221549.log diff --git a/capsules/v4/nucleus-64.f18 b/capsules/v4/nucleus-64.f18 index c156aa3c30d3b2f8bfd75c0ccf3d2d6ead6075db..6f8189113aac25137fb5b6eab2a7dfb10642796a 100644 GIT binary patch literal 56608 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 literal 54272 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 : 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> HERE . +8300 ok +ok> BYE +BYE: cold restart diff --git a/logs/20261007-221223/aarch64/qemu-aarch64-20261007-221223.log b/logs/20261007-221223/aarch64/qemu-aarch64-20261007-221223.log new file mode 100644 index 00000000..0147544a --- /dev/null +++ b/logs/20261007-221223/aarch64/qemu-aarch64-20261007-221223.log @@ -0,0 +1,138 @@ +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 7 2026 22:11:57 + +UEFI BootServices: EXITED + +=== StarKernel Boot Information === +Memory map entries: 108 +Total memory: 4093 MB +Usable memory: 4054 MB +=================================== + +PMM initialized. +PMM statistics: + Total pages: 1039356 + Free pages : 1037945 + 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: 1193133] 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> HERE . +8300 ok +ok> BYE +BYE: cold restart diff --git a/logs/20261007-221549/riscv64/qemu-riscv64-20261007-221549.log b/logs/20261007-221549/riscv64/qemu-riscv64-20261007-221549.log new file mode 100644 index 00000000..4a427d96 --- /dev/null +++ b/logs/20261007-221549/riscv64/qemu-riscv64-20261007-221549.log @@ -0,0 +1,208 @@ + +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 7 2026 22:15:10 + +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: 25077003] 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> HERE . +8300 ok +ok> BYE +BYE: cold restart diff --git a/v4/README.md b/v4/README.md index b73cb6a5..d1ed0257 100644 --- a/v4/README.md +++ b/v4/README.md @@ -133,13 +133,16 @@ The two products are not yet this: they become the five nodes at steps 8 and 9. A node she births is not POSTed: POST is the kernel's, once, on Hera. -**Refusals and waits** (`MESH.md` 7b, step 6c). No message is lost without -its sender being told, and no node waits for ever. A node with no room for -a message, or no way to pass it on, sends its sender a NACK; `(REFUSED)` +**Refusals and waits** (`MESH.md` 7b, step 6c). A node is never left +waiting on a node that is gone, and a wait on a node that is alive and +stuck is ended by Hera's killing it, or from the console. A node with no +room for a message, or no way to pass it on, or that would be the +sixteenth to pass it on, sends a NACK to whoever waits on it; `(REFUSED)` counts them. `AWAIT` ends in an error if the answer is not going to come: "Message refused", "Node gone", or "Interrupted" when a line is typed at the console. `n KILL` on Hera removes node n and tells every other node it -is gone. +is gone; a node believes that only from its centre. A stuck node can hold +up its neighbours until it is killed: `MESH.md` 7b.7 says how. **Blocks** (`MESH.md` step 6). The block words are the kernel's, as they are for a v3 VM: `BLOCK`, `BUFFER`, `UPDATE`, `SAVE-BUFFERS` and diff --git a/v4/capsule/core.v4 b/v4/capsule/core.v4 index bbe82fb7..53ad645c 100644 --- a/v4/capsule/core.v4 +++ b/v4/capsule/core.v4 @@ -197,14 +197,34 @@ header CMOVE \ the messages waiting are kept for those two types, so that ordinary \ messages cannot take all the room. -\ ( to about -- ) a refusal is owed to the node `to`, about the node `about` +\ ( to about -- ) a refusal is owed to the node `to`, about the node `about`. +\ Owed to this node itself -- its own message came back to it and was let +\ go here -- it is put with the messages waiting, as if it had come by a +\ port, in the room kept for refusals. : (OWE) + over (ME) a! @ xor if SELF drop (OWED#) a! @ -8 + -if NOROOM drop (OWED#) a! @ 2* (OWED) + a! push !+ pop ! (OWED#) a! @ 1 + ! ; + SELF: drop 1 (MQ#) a! @ + -MQ-ROOM + -if NOROOM + drop push (MQ!) (ME) a! @ (MQ!) 4 (MQ!) 0 (MQ!) 0 (MQ!) 0 (MQ!) 4 (MQ!) 0 (MQ!) pop (MQ!) ; NOROOM: drop drop drop (LOST) a! @ 1 + ! ; +\ ( from to type -- ) a message of that type, from the one node for the +\ other, has been let go here. Whoever is waiting on it is told: its +\ sender; or, for a node's answer (type 3), the node the answer was for, +\ which is the one that waits. Nothing is owed for a NACK or a GONE. +: (TELL-OF) + dup -4 + if NOT -1 + if NOT drop + -3 + if ANSWER drop jump (OWE) \ to its sender, about whom it was for + ANSWER: drop SWAP jump (OWE) \ to whom it was for, about the node that answered + NOT: drop drop drop drop ; + +\ ( port -- flag ) is there anything on that port, by its address? +: (THERE) + (PORT) - -PORTS - (READERS) b! @b SWAP jump (BIT) + \ ( port to -- ) the seven words of a message being taken in, and the \ port, go into (MQ-HDR). B is at the port, by its address, and `to` is \ the message's first word, already read from it. @@ -223,7 +243,7 @@ header CMOVE LONG: drop 3 + 2/ 2/ -1 + FOR @b drop UNEXT -1 (MQ-HDR)+6 a! ! 0 COUNTED: \ ( n ) how many words of text follow dup (MQ#) a! @ + \ what it would come to - (MQ-HDR)+2 a! @ -4 + -if KEPT-FOR \ a NACK or a GONE may use the room kept for them + (MQ-HDR)+2 a! @ -4 + if KEPT-FOR -1 + if KEPT-FOR \ a NACK or a GONE may use the room kept for them drop -MQ-ORDINARY + jump ROOM KEPT-FOR: drop -MQ-ROOM + ROOM: -if FULL @@ -235,9 +255,7 @@ header CMOVE FULL: drop if READ -1 + FOR @b drop UNEXT jump LOSE READ: drop LOSE: (LOST) a! @ 1 + ! - (MQ-HDR)+2 a! @ -4 + -if NOT-OWED \ nothing is owed for a NACK or a GONE - drop (MQ-HDR)+1 a! @ (MQ-HDR) a! @ jump (OWE) \ to whoever sent it, about whom it was for - NOT-OWED: drop ; + (MQ-HDR)+1 a! @ (MQ-HDR) a! @ (MQ-HDR)+2 a! @ jump (TELL-OF) \ ( port to -- ) take in a message and keep it : (TAKE) (TAKE-HDR) jump (TAKE-KEEP) @@ -257,7 +275,10 @@ header CMOVE ASK: drop (READERS) b! @b (GATE-PORT) a! @ (PORT) - (BIT) if NOTYET drop (GATE-PORT) a! @ b! ; \ it is waiting to read - NOTYET: drop jump L + NOTYET: drop + (READERS) b! @b (GATE-PORT) a! @ (PORT) - -PORTS - (BIT) if NOONE \ is there anything on the port at all? + drop jump L + NOONE: drop NODE-ERROR b! 18 !b ; \ no: what waits here would wait for ever \ ( type -- ) words 1 to 5 of a message this node sends: from, type, and \ the three that are carried and not used yet. Its caller has put B at the @@ -266,18 +287,45 @@ header CMOVE : (HDR) (ME) a! @ !b \ from !b \ type - 0 !b 0 !b 0 !b ; \ heat and TTL, ACL tag, sequence + 16 !b 0 !b 0 !b ; \ how many nodes may pass it on (the TTL: MESH.md 7b); ACL tag; sequence -\ ( -- ) send one refusal this node owes, if it owes any: the one noted -\ last. With no way to whom it is owed, it is let go and counted. +\ PAYING WHAT IS OWED does not hold this node up. A refusal is sent if the +\ neighbour it goes by is reading at this moment. If it is not, and its +\ number is the higher, the refusal is written all the same, as any message +\ would be (MESH.md 7a): that neighbour takes it in when it next looks. If +\ its number is the lower, or it has not been told of, the refusal stays +\ owed, and this node goes on with what it has to do and tries again; it +\ does not sleep while it owes one (quit.v4, (IDLE)). One there is no way +\ for, or whose way is a port with nothing on it, is let go and counted. + +\ ( k -- ) the k-th refusal owed is done with: the last takes its place +: (PAID) + (OWED#) a! @ -1 + dup ! + 2* (OWED) + a! @+ @ + push push 2* (OWED) + a! pop !+ pop ! ; + +\ ( k -- ) send the k-th refusal owed, if it can go now +: (PAY1) + dup 2* (OWED) + a! @+ @ \ k to about + over (PORT-FOR) if NOWAY \ k to about port + dup (THERE) if GONE + drop (READERS) b! @b over (PORT) - (BIT) if BUSY + drop + WRITE: b! \ k to about B: the port + SWAP !b 4 (HDR) 4 !b !b \ k to; from, type 4; four characters; about + jump (PAID) + BUSY: drop \ it is not reading: + dup (PORT) - (NEAR) + a! @ if LEAVE \ k to about port near + (ME) a! @ - -if BLIND jump LEAVE \ its number less this node's + BLIND: drop jump WRITE \ the higher: it will take it in + LEAVE: drop drop drop drop drop ; + GONE: drop + NOWAY: drop drop drop (LOST) a! @ 1 + ! jump (PAID) + +\ ( -- ) send every refusal this node owes that can go now : (PAY) - (OWED#) a! @ if NONE - -1 + dup ! - 2* (OWED) + a! @+ @ \ to about - over (PORT-FOR) if NOWAY - (GATE) over !b \ to - 4 (HDR) 4 !b !b drop ; \ from, type 4; four characters; about - NOWAY: drop drop drop (LOST) a! @ 1 + ! ; + (OWED#) a! @ if NONE -1 + + FOR pop dup push (PAY1) NEXT ; NONE: drop ; \ ( -- ) send what has been printed, to where (PRINT-TO) says, by the port diff --git a/v4/capsule/quit.v4 b/v4/capsule/quit.v4 index 06ed2ea3..aed046d7 100644 --- a/v4/capsule/quit.v4 +++ b/v4/capsule/quit.v4 @@ -55,6 +55,7 @@ macro R-CLEAR RSTACK-DEPTH b! a !b endmacro \ something that has emptied the return stack (or by a fault, which empties \ it). (LINE-STATUS) keeps how the last text ended. : (FINISH) ( s -- ) + 1 (QUIET) a! ! \ from here an error is not this text's: see (RAISED) (LINE-STATUS) b! dup !b (FLUSH-OUT) (DONE-PORT) a! @ (GATE) (MSG)+1 a! @ !b \ to whoever sent the text @@ -78,9 +79,14 @@ macro R-CLEAR RSTACK-DEPTH b! a !b endmacro \ line was, and (FINISH) reports how it ended. A message for another node \ is passed on, (PASS-ON). : (IDLE) - L: (PAY) \ a refusal owed is sent first (core.v4) + L: 1 (QUIET) a! ! \ nothing here is any text's doing: see (RAISED) + (PAY) \ what refusals can be sent now, are (core.v4) (MQ#) a! @ if WAIT drop jump HAVE - WAIT: drop (PORT)+8 b! @b (PORT)+9 b! @b (PORT) + dup b! \ ( to port ) B: the port it came on + WAIT: drop (OWED#) a! @ if BLOCK \ with one still owed it does not sleep: + drop (WRITERS) b! @b if L0 \ it takes in whatever is being written to it, + (LOW) (PORT) + dup b! @b (TAKE) jump L + L0: drop jump L \ and tries again + BLOCK: drop (PORT)+8 b! @b (PORT)+9 b! @b (PORT) + dup b! \ ( to port ) B: the port it came on SWAP (TAKE) jump L HAVE: (MQ@) (MSG) a! ! (MQ@) (MSG)+1 a! ! (MQ@) (MSG)+2 a! ! (MQ@) (MSG)+3 a! ! @@ -100,8 +106,9 @@ macro R-CLEAR RSTACK-DEPTH b! a !b endmacro (MSG)+2 a! @ -1 + if TEXT \ for this node, and -3 + if NACK -1 + if GONE drop jump (IDLE) \ neither text nor these: let go NACK: drop (REFUSED) a! @ 1 + ! jump (IDLE) \ a message of this node's was refused: counted - GONE: drop (REPLY) a! @ (ROUTE-DEFAULT) a! @ xor if CENTRE \ a node is gone, if its centre says so: - drop jump (IDLE) + GONE: drop (MSG)+6 a! @ -4 + if G4 drop jump (IDLE) \ a node is gone, if its centre says so: + G4: drop (CENTRE) if NOT (MSG)+1 a! @ xor if CENTRE drop jump (IDLE) + NOT: drop jump (IDLE) CENTRE: drop TIB 2/ 2/ a! @ NO-ROUTE jump (IDLE) \ the way to it is forgotten TEXT: drop (MSG)+1 a! @ (PORT-FOR) if D0 jump D1 \ the way back to whoever sent it: @@ -119,22 +126,27 @@ macro R-CLEAR RSTACK-DEPTH b! a !b endmacro \ of it, as it came, to the port that leads toward that node. With no way \ known it is let go, and (LOST) counts it. : (PASS-ON) + (MSG)+3 a! @ if FRESH jump COUNT \ how many more nodes may pass it on; + FRESH: drop 16 \ from a device, which set none: sixteen + COUNT: -1 + if SPENT (MSG)+3 a! ! (MSG) a! @ (PORT-FOR) if NOWAY + dup (THERE) if NOONE drop (GATE) (MSG) a! 6 FOR @+ !b UNEXT \ its seven words (MSG)+6 a! @ 3 + 2/ 2/ if SENT -1 + TIB 2/ 2/ a! FOR @+ !b UNEXT \ and its text jump (IDLE) SENT: drop jump (IDLE) + NOONE: drop \ its way is a port with nothing on it + SPENT: \ it has been passed on as often as it may be NOWAY: drop (LOST) a! @ 1 + ! - (MSG)+2 a! @ -4 + -if NOT-OWED \ its sender is told, unless it is a NACK or a GONE - drop (MSG)+1 a! @ (MSG) a! @ (OWE) jump (IDLE) - NOT-OWED: drop jump (IDLE) + (MSG)+1 a! @ (MSG) a! @ (MSG)+2 a! @ (TELL-OF) jump (IDLE) \ whoever waits on it is told (core.v4) \ ( -- ) THE TEXT, INTERPRETED. It is in TIB with a zero after it and its \ length in SPAN, and the return stack is empty. It is interpreted with one \ return entry under it, this word's call of INTERPRET; whatever is on the \ data stack stays there. : (LINE) + 0 (QUIET) a! ! \ an error from here on is this text's 0 BLK a! ! \ the terminal, whatever was being loaded TIB (SRC) a! ! 0 >IN a! ! 0 NODE-ERROR b! !b @@ -232,6 +244,70 @@ header SEND-ON POS: -PORTS + -if BAD drop (PORT) + jump (SEND-ON) BAD: drop drop drop drop drop NODE-ERROR b! 12 !b ; +\ ( -- n ) the number of this node's centre: the node on the port that +\ leads to everything it has no other way to. 0: it has none, or has not +\ been told who is there (NEIGHBOUR). +: (CENTRE) + (ROUTE-DEFAULT) a! @ if NONE (PORT) - (NEAR) + a! @ ; + NONE: ; + +\ ( -- n ) the number of this node's console: the one it was told of, or +\ with none told of, whoever sent the text it is doing +: (A-CONSOLE) + (CONSOLE) a! @ if C0 ; + C0: drop (MSG)+1 a! @ ; + +\ THE MESSAGES WAITING, LOOKED THROUGH for what would end a wait. Each in +\ turn is taken from the front: its eight cells into (MQ-HDR), (A-HDR). If +\ it is the answer waited for, or a NACK or a believed GONE for this node, +\ it is used up, as it would be had it come during the wait; otherwise it +\ goes to the back, (A-BACK) and (A-TEXT), so that when all have been +\ looked at they are in the order they were in. (A-LEFT) is how many +\ cells are still to be looked at. (A-FOUND) is what was found first: 0 +\ nothing, 1 the answer, its word in (A-WORD); 2 a NACK about the node +\ waited for; 3 a GONE about it; 4 text from the console, which stays +\ where it is to be done next. +: (A-HDR) (MQ-HDR) a! 7 FOR a push (MQ@) pop a! !+ NEXT ; +: (A-BACK) (MQ-HDR) a! 7 FOR @+ a push (MQ!) pop a! NEXT ; +: (A-TEXT) if NONE -1 + FOR (MQ@) (MQ!) NEXT ; NONE: drop ; +: (A-QUEUE) + 0 (A-FOUND) a! ! + (MQ#) a! @ (A-LEFT) a! ! + L: (A-LEFT) a! @ if END drop + (A-HDR) + (MQ-HDR)+6 a! @ -if SIZED drop 0 jump N + SIZED: 3 + 2/ 2/ + N: (A-LEFT) a! @ over 8 + - ! \ n: its words of text + (A-FOUND) a! @ if LOOK drop jump KEEP \ something is found: the rest only go round + LOOK: drop + (MQ-HDR) a! @ (ME) a! @ xor if MINE drop jump KEEP + MINE: drop + (MQ-HDR)+2 a! @ -1 + if TEXT -2 + if ANS -1 + if NACK -1 + if GONE drop jump KEEP + TEXT: drop (A-CONSOLE) (MQ-HDR)+1 a! @ xor if BRK drop jump KEEP + BRK: drop 4 (A-FOUND) a! ! jump KEEP + ANS: drop (MQ-HDR)+1 a! @ (AWAIT-FROM) a! @ xor if A1 drop jump KEEP + A1: drop dup -1 + if A2 drop jump KEEP + A2: drop drop (MQ@) (A-WORD) a! ! 1 (A-FOUND) a! ! jump L + NACK: drop dup -1 + if N2 drop jump KEEP + N2: drop drop (MQ@) (AWAIT-FROM) a! @ xor if N3 drop (REFUSED) a! @ 1 + ! jump L + N3: drop 2 (A-FOUND) a! ! jump L + GONE: drop dup -1 + if G2 drop jump KEEP + G2: drop (CENTRE) if NOC (MQ-HDR)+1 a! @ xor if G3 drop jump KEEP + NOC: drop jump KEEP + G3: drop drop (MQ@) dup NO-ROUTE (AWAIT-FROM) a! @ xor if G4 drop jump L + G4: drop 3 (A-FOUND) a! ! jump L + KEEP: (A-BACK) (A-TEXT) jump L + END: drop ; + +\ ( what -- how | ) the wait is over, by what (A-FOUND) says: the answer's +\ word is left; anything else is an error +: (A-END) + 0 (AWAIT-FROM) a! ! + -1 + if ANS -1 + if REF -1 + if LEFT drop NODE-ERROR b! 21 !b ; + ANS: drop (A-WORD) a! @ ; + REF: drop NODE-ERROR b! 19 !b ; + LEFT: drop NODE-ERROR b! 20 !b ; + \ ( node -- how ) WAIT FOR THE NODE'S WORD OF HOW TEXT ENDED: 0 QUIT, \ 1 completed, 2 an error. This node is blocked reading its ports until a \ message of type 3 comes for it from that node; every other message that @@ -241,7 +317,11 @@ header SEND-ON \ (MESH.md section 7b): a NACK about that node, error 19, Message refused; \ or a GONE about it from this node's centre, error 20, Node gone. A NACK \ about another node is counted, and a GONE about another has its way -\ forgotten, as they would be if this node were not waiting. +\ forgotten, as they would be if this node were not waiting. A GONE is +\ believed only if this node's centre sent it: (CENTRE). +\ WHAT HAS COME ALREADY is looked through first, (A-QUEUE): the answer, a +\ NACK or a GONE may have been taken in before the wait began, while this +\ node was waiting to write, and is then with the messages waiting. \ AND A LINE FROM THE CONSOLE BREAKS IT (7b.6): text for this node from its \ console is kept, as any message is, the wait ends in error 21, \ Interrupted, and the text is then done as usual. It is the one way to @@ -250,28 +330,30 @@ header SEND-ON header AWAIT : AWAIT (AWAIT-FROM) a! ! + (A-QUEUE) + (A-FOUND) a! @ if WAIT jump (A-END) + WAIT: drop L: (PORT)+8 b! @b (PORT)+9 b! @b (PORT) + dup b! SWAP (TAKE-HDR) (MQ-HDR) a! @ (ME) a! @ xor if A1 drop jump KEEP A1: drop (MQ-HDR)+2 a! @ \ for this node: its type -1 + if TEXT -2 + if ANSWER -1 + if NACK -1 + if GONE drop jump KEEP - TEXT: drop (CONSOLE) a! @ if C0 jump C1 \ text: from the console? - C0: drop (MSG)+1 a! @ \ with none told of, whoever sent the text being done - C1: (MQ-HDR)+1 a! @ xor if BREAK drop jump KEEP - BREAK: drop (TAKE-KEEP) 0 (AWAIT-FROM) a! ! NODE-ERROR b! 21 !b ; \ kept, to be done next; and the wait is over + TEXT: drop (A-CONSOLE) (MQ-HDR)+1 a! @ xor if BREAK drop jump KEEP \ text: from the console? + BREAK: drop (TAKE-KEEP) 4 jump (A-END) \ kept, to be done next; and the wait is over ANSWER: drop (MQ-HDR)+1 a! @ (AWAIT-FROM) a! @ xor if A3 drop jump KEEP A3: drop (MQ-HDR)+6 a! @ -4 + if A4 drop jump KEEP A4: drop 0 (AWAIT-FROM) a! ! @b ; \ its one word NACK: drop (MQ-HDR)+6 a! @ -4 + if N4 drop jump KEEP N4: drop @b (AWAIT-FROM) a! @ xor if REFUSED \ about the node waited for? drop (REFUSED) a! @ 1 + ! jump L \ another: counted - REFUSED: drop 0 (AWAIT-FROM) a! ! NODE-ERROR b! 19 !b ; + REFUSED: drop 2 jump (A-END) GONE: drop (MQ-HDR)+6 a! @ -4 + if G4 drop jump KEEP G4: drop @b \ the node that is gone - (MQ-HDR)+7 a! @ (ROUTE-DEFAULT) a! @ xor if CENTRE \ only its centre is believed + (CENTRE) if NOC (MQ-HDR)+1 a! @ xor if CENTRE \ only its centre is believed drop drop jump L + NOC: drop drop jump L CENTRE: drop dup NO-ROUTE (AWAIT-FROM) a! @ xor if LEFT drop jump L - LEFT: drop 0 (AWAIT-FROM) a! ! NODE-ERROR b! 20 !b ; + LEFT: drop 3 jump (A-END) KEEP: (TAKE-KEEP) jump L \ ( w port -- ) write a cell to the port, by its number. It waits until @@ -343,7 +425,16 @@ header ABORT \ in core.v4). Print its message -- a negative code means the word printed \ its own -- and end the line with ERROR. The return stack has been \ emptied; the data stack is as the word left it. +\ (QUIET) is 1 while the node is doing something that is no text's doing: +\ paying a refusal, passing a message on, or sending word of how a text +\ ended. An error then has nobody to be told to -- the usual one is that +\ no one is on the port (18) -- and trying to tell it would raise it again +\ without end. So it is not told: what was being sent is let go and +\ counted, and the node goes back to waiting. : (RAISED) + (QUIET) a! @ if SPEAK + drop (OUT) (OUT^) a! ! (LOST) a! @ 1 + ! jump (IDLE) + SPEAK: drop NODE-ERROR b! @b -if POS drop 2 jump (DONE) POS: -1 + if M1 -1 + if M2 -1 + if M3 -1 + if M4 @@ -369,7 +460,8 @@ header ABORT M15: drop $65666544 (EMIT4) $64657272 (EMIT4) $726F7720 (EMIT4) $6F6E2064 (EMIT4) $65732074 (EMIT4) $74 (EMIT4) CR 2 jump (DONE) M16: drop $20746F4E (EMIT4) $6F772061 (EMIT4) $6472 (EMIT4) CR 2 jump (DONE) M17: drop $726F7453 (EMIT4) $20656761 (EMIT4) $75666572 (EMIT4) $646573 (EMIT4) CR 2 jump (DONE) - M18: drop $6F206F4E (EMIT4) $6F20656E (EMIT4) $6874206E (EMIT4) $70207461 (EMIT4) $74726F (EMIT4) CR 2 jump (DONE) + M18: drop (OUT) (OUT^) a! ! \ what was printed and not sent cannot be sent: it is let go + $6F206F4E (EMIT4) $6F20656E (EMIT4) $6874206E (EMIT4) $70207461 (EMIT4) $74726F (EMIT4) CR 2 jump (DONE) M19: drop $7373654D (EMIT4) $20656761 (EMIT4) $75666572 (EMIT4) $646573 (EMIT4) CR 2 jump (DONE) M20: drop $65646F4E (EMIT4) $6E6F6720 (EMIT4) $65 (EMIT4) CR 2 jump (DONE) M21: drop $65746E49 (EMIT4) $70757272 (EMIT4) $646574 (EMIT4) CR 2 jump (DONE) diff --git a/v4/include/v4/fabric.h b/v4/include/v4/fabric.h index e6a94b09..1d0898d1 100644 --- a/v4/include/v4/fabric.h +++ b/v4/include/v4/fabric.h @@ -26,6 +26,7 @@ typedef struct { int (*take)(void *self, v4_cell value); /* the node wrote `value`: 1 if it is taken, 0 if not yet */ int (*give)(void *self, v4_cell *value); /* the node is reading: 1 with a word in *value, 0 if not yet */ void *self; + int (*pending)(void *self); /* 1 if it has a word to give that has not been asked for; may be 0: it never says */ } v4_device; /* One end of a wire. */ @@ -55,6 +56,7 @@ typedef struct { v4_place *place; unsigned capacity; v4_cell gone_error; /* see v4_fabric_gone_error; 0: none */ + v4_cell interrupt_error; /* see v4_fabric_interrupt_error; 0: none */ } v4_fabric; /* An empty fabric with room for `capacity` nodes, in `places`. */ @@ -71,6 +73,19 @@ void v4_fabric_init(v4_fabric *f, v4_place *places, unsigned capacity); * is how a fabric begins, nothing is raised. */ void v4_fabric_gone_error(v4_fabric *f, v4_cell code); +/* A NODE BLOCKED WRITING TO A NODE THAT DOES NOT READ. Of two neighbours + * one may write to the other without looking (docs/v4.0.0/MESH.md 7a), and + * is then blocked until the other reads; if the other never does, it is + * blocked for ever, and if it is the node the console is on, nothing can be + * typed to it again. So: when a node that can take an error is blocked + * writing to another node, on the first word of a message (node.h, + * v4_node_words_since_look: nothing of the message has gone), and a device + * on one of its own ports says it has something pending for it, the error + * `code` is raised on the node: it gives up that message and goes on, to + * read what the device has. Only a device with a `pending` function can + * do this. With `code` 0, which is how a fabric begins, it never happens. */ +void v4_fabric_interrupt_error(v4_fabric *f, v4_cell code); + /* More room: `places`, of `capacity` no less than before, takes over; every * node keeps its number and its wiring. */ void v4_fabric_grow(v4_fabric *f, v4_place *places, unsigned capacity); diff --git a/v4/include/v4/image.h b/v4/include/v4/image.h index 03c628db..377f8b8d 100644 --- a/v4/include/v4/image.h +++ b/v4/include/v4/image.h @@ -65,6 +65,11 @@ typedef struct { * port". */ #define V4_ERROR_NO_ONE 18 +/* The nucleus's error for a node let go of a message it was blocked on the + * first word of, because its console has a line for it (fabric.h, + * v4_fabric_interrupt_error): "Interrupted". */ +#define V4_ERROR_INTERRUPTED 21 + /* The description of the nucleus built from v4/capsule (the generated file * defines it). */ extern const v4_image v4_capsule_image; diff --git a/v4/include/v4/node.h b/v4/include/v4/node.h index cc2cd116..6d4108b7 100644 --- a/v4/include/v4/node.h +++ b/v4/include/v4/node.h @@ -111,6 +111,7 @@ typedef struct { unsigned last_from; /* the port the last read from "any" took its word from */ unsigned writers; /* bit k: the neighbour on port k is blocked writing to this node */ unsigned readers; /* bit k: the neighbour on port k is blocked reading from this node */ + unsigned since_look; /* words taken from this node since it last fetched either of those */ /* DSTACK-DEPTH and RSTACK-DEPTH (D-16). See v4_node_stack_regs_attach. */ v4_cell dstack_reg; /* its word address, or -1 */ @@ -181,7 +182,11 @@ void v4_node_console_attach(v4_node *n, v4_cell addr); * base + V4_PORTS + 1 which port the last read from "any" came from * base + V4_PORTS + 2 which ports have a neighbour waiting to write to this * node: bit k for port k - * base + V4_PORTS + 3 which ports have a neighbour waiting to read from it + * base + V4_PORTS + 3 which ports have a neighbour waiting to read from it, + * bit k for port k; and, V4_PORTS bits higher, which + * ports have anything on them at all: bit V4_PORTS + k + * for port k. A port with nothing on it can never + * be read from or written to. * The last two are how a node looks before it writes (MESH.md 7a), as the * F18's io register lets it. They are what whoever connects the ports last * said, v4_node_port_status; fetching them waits for nothing and changes @@ -214,6 +219,13 @@ void v4_node_port_attach(v4_node *n, v4_cell base); /* The neighbour has taken what the node wrote. */ void v4_node_port_served(v4_node *n); +/* How many words the node has had taken since it last looked at its + * neighbours (a fetch of either of the two status addresses). A node that + * looks before it begins a message (MESH.md 7a) and is blocked writing with + * this at 0 is blocked on the first word of the message: nothing of it has + * gone. */ +unsigned v4_node_words_since_look(const v4_node *n); + /* THERE IS NO ONE ON THE PORT the node is blocked at -- writing to it, or * reading from that one port -- and there will not be. A node that can * take an error (it has an error register and a fault table: node.h, diff --git a/v4/src/fabric.c b/v4/src/fabric.c index 04e8664f..cb269156 100644 --- a/v4/src/fabric.c +++ b/v4/src/fabric.c @@ -18,6 +18,7 @@ void v4_fabric_init(v4_fabric *f, v4_place *places, unsigned capacity) f->place = 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,11 @@ 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) { unsigned i; @@ -171,6 +177,7 @@ static void look_about(v4_fabric *f, unsigned a) for (k = 0; k < V4_PORTS; k++) { const v4_wire *w = &f->place[a].wire[k]; + if (w->kind != V4_WIRE_NONE) readers |= 1u << (V4_PORTS + k); /* there is something on this port */ if (w->kind == V4_WIRE_DEVICE) { if (w->device->take) readers |= 1u << k; } else if (w->kind == V4_WIRE_NODE && awake(f, w->node)) { @@ -202,6 +209,24 @@ 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); + /* 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) for (i = 0; i < f->capacity; i++) { diff --git a/v4/src/node.c b/v4/src/node.c index a7f788ed..665310b6 100644 --- a/v4/src/node.c +++ b/v4/src/node.c @@ -120,8 +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_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; @@ -179,6 +179,7 @@ void v4_node_port_attach(v4_node *n, v4_cell base) n->last_from = 0; n->writers = 0; n->readers = 0; + n->since_look = 0; } void v4_node_port_status(v4_node *n, unsigned writers, unsigned readers) @@ -221,6 +222,12 @@ void v4_node_born(v4_node *n, v4_cell base) void v4_node_port_served(v4_node *n) { n->asking = 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) diff --git a/v4/system/boot.c b/v4/system/boot.c index 5388caaf..715a5bf6 100644 --- a/v4/system/boot.c +++ b/v4/system/boot.c @@ -354,7 +354,7 @@ int v4_boot_run(const v4_boot *b) /* what is on its two ports, the kernel and the console, takes a word * when it is written: the node need never wait before writing to them * (node.h, v4_node_port_status) */ - v4_node_port_status(b->n, 0, 1u << KERNEL_PORT | 1u << CONSOLE_PORT); + v4_node_port_status(b->n, 0, (1u << KERNEL_PORT | 1u << CONSOLE_PORT) * (1u + (1u << V4_PORTS))); /* each of them there, and taking */ v4_blocks_init(&boot_blocks, b->im->block_window); if (!load_nucleus(b)) { say(b, "PARITY:FAIL\nPOST: FAILED\n"); return 0; } diff --git a/v4/tests/host_map.h b/v4/tests/host_map.h index ca5b3e84..98e3cca2 100644 --- a/v4/tests/host_map.h +++ b/v4/tests/host_map.h @@ -64,6 +64,10 @@ #define BLK (BVARS + 7) #define SRC (BVARS + 8) #define SRC_HOOK (BVARS + 9) +#define QUIET (BUF0_W - 10) /* 1 while the node does something that is no text's doing: an error then is not told (quit.v4, (RAISED)) */ +#define A_WORD (BUF0_W - 113) /* AWAIT's look through the messages waiting: the answer's word, */ +#define A_LEFT (BUF0_W - 114) /* how many cells are still to be looked at, */ +#define A_FOUND (BUF0_W - 115) /* and what was found */ #define REFUSED (BUF0_W - 7) /* how many of this node's messages it has been told were refused */ #define OWED_COUNT (BUF0_W - 8) /* how many refusals this node owes and has not sent yet, at most 8 */ #define OWED (BUF0_W - 112) /* those: for each, the node it is owed to and the node the refused message was for */ @@ -196,6 +200,10 @@ static int host_load(v4_text *tx, v4_node *n, const char *const *files, unsigned v4_text_constant(tx, "(MQ)", MQ_W); v4_text_constant(tx, "(MQ-END)", MQ_W + MQ_CELLS); v4_text_constant(tx, "-MQ-ORDINARY", -(v4_cell)(MQ_CELLS - 7 - 36)); /* the same for a message that is not a refusal or a GONE: 36 cells are kept for those */ + v4_text_constant(tx, "(QUIET)", QUIET); + v4_text_constant(tx, "(A-WORD)", A_WORD); + v4_text_constant(tx, "(A-LEFT)", A_LEFT); + v4_text_constant(tx, "(A-FOUND)", A_FOUND); v4_text_constant(tx, "(REFUSED)", REFUSED); v4_text_constant(tx, "(OWED#)", OWED_COUNT); v4_text_constant(tx, "(OWED)", OWED); diff --git a/v4/tests/test_fabric.c b/v4/tests/test_fabric.c index d6000a45..7af08fe6 100644 --- a/v4/tests/test_fabric.c +++ b/v4/tests/test_fabric.c @@ -45,6 +45,10 @@ 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 */ +/* 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) { @@ -358,10 +362,67 @@ int main(void) } for (i = 0; i < 20; i++) (void)v4_fabric_step(&f); CHECK(ND(b)->n.mem[OUT] == (1 << 2), "it sees which of its ports has a neighbour waiting to write to it: %ld", (long)ND(b)->n.mem[OUT]); - CHECK(ND(b)->n.mem[OUT + 1] == ((1 << 4) | (1 << 6)), "and which have one waiting to read from it, or a device: %ld", (long)ND(b)->n.mem[OUT + 1]); + CHECK((ND(b)->n.mem[OUT + 1] & ((1 << V4_PORTS) - 1)) == ((1 << 4) | (1 << 6)), "and which have one waiting to read from it, or a device: %ld", (long)(ND(b)->n.mem[OUT + 1] & ((1 << V4_PORTS) - 1))); + CHECK((ND(b)->n.mem[OUT + 1] >> V4_PORTS) == ((1 << 2) | (1 << 4) | (1 << 6)), "and, in the bits above those, which of its ports have anything on them at all: %ld", (long)(ND(b)->n.mem[OUT + 1] >> V4_PORTS)); 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"); + /* ---- 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 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(!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"); + + /* 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); + 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); return failures != 0; } diff --git a/v4/tests/test_host_mesh.c b/v4/tests/test_host_mesh.c index 871e2b96..cde5eaa7 100644 --- a/v4/tests/test_host_mesh.c +++ b/v4/tests/test_host_mesh.c @@ -10,6 +10,7 @@ * tells it. The console is a device on a port and speaks messages * (v4/include/v4/message.h). */ +#include "v4/image.h" #include "v4/fabric.h" #include "v4/message.h" #include "v4/text.h" @@ -70,10 +71,12 @@ 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; } +static unsigned step_cap = 20000000; /* how long a line may take before it is given up on */ /* Send text to a node and let everything that follows from it happen. */ static const char *tell(v4_cell node, const char *text) { @@ -82,8 +85,16 @@ static const char *tell(v4_cell node, const char *text) ended = 0; ended_from = -1; ended_how = -1; if (!v4_message_text(&going, node, CONSOLE_ID, V4_MSG_TEXT, text, (unsigned)strlen(text))) return "(too long)"; going_at = 0; - while (steps < 20000000 && v4_fabric_step(&f) != 0) steps++; - if (steps >= 20000000) return "(still running)"; + while (steps < step_cap && v4_fabric_step(&f) != 0) steps++; + if (steps >= step_cap) { + if (getenv("V4_MESH_STUCK")) { + unsigned k; + for (k = 0; k < 3; k++) if (v4_fabric_node_at(&f, k)) { const v4_node *n = &pool[k].n; + printf(" stuck after \"%s\": node %ld p=%ld asking=%d port=%u reading=%d rport=%u mq=%ld owed=%ld lost=%ld fault=%u@%ld\n", text, (long)n->mem[ME], (long)n->p, n->asking, n->ask_port, n->reading, n->read_port, + (long)n->mem[MQ_COUNT], (long)n->mem[OWED_COUNT], (long)n->mem[LOST], n->fault_kind, (long)n->fault_addr); } + } + return "(still running)"; + } return printed; } @@ -298,8 +309,6 @@ int main(void) 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"); - /* a GONE that does not come from its centre is not acted on */ - CHECK(is_empty(tell(12, "12 11 GONE")) && pool[1].n.mem[AWAIT_FROM] == 12, "node 12 tells it that 12 is gone: that is not its centre speaking, and it goes on waiting"); /* one from its centre ends the wait */ CHECK(is_empty(tell(10, "12 NO-ROUTE")), "node 10 forgets the way to 12"); CHECK(strstr(tell(10, "12 11 GONE"), "Node gone") != NULL && printed_from == 11 && ended_from != -1, "node 10, its centre, tells it 12 is gone: its wait ends with an error that says so: \"%s\"", printed); @@ -308,10 +317,8 @@ int main(void) not_taken = 49 - got; CHECK(got >= 30 && not_taken > 8, "it then did every message it had kept: %ld of the 49", (long)got); 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)); - /* two more than node 10's: node 12's word of how "12 11 GONE" ended came this way for the console while - * node 11 was full, and was not taken; nor could the refusal for it be owed */ - CHECK(pool[1].n.mem[LOST] == lost + (not_taken + 1) + (not_taken + 1 - 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 + 1)); + 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"); @@ -330,6 +337,91 @@ int main(void) CHECK(strstr(tell(10, "5 NO-ROUTE"), "ERROR") == NULL && strcmp(tell(11, "20 22 + ."), "42 ") == 0, "forgetting the way to a node there was no way to changes nothing"); + + /* ---- what the review of step 6c found (MESH.md step 6c) ------------------- */ + step_cap = 3000000; + v4_cell ways11 = 0; + + /* a GONE is believed only from the node's centre: not by another port, and not from another node by the centre's port */ + CHECK(is_empty(tell(10, "12 NO-ROUTE 12 3 ROUTE 12 3 NEIGHBOUR")) && is_empty(tell(12, "NO-ROUTES 3 DEFAULT-ROUTE 11 2 ROUTE 10 3 NEIGHBOUR")), + "node 12 is reached straight from node 10 now, and has its own wire to 11"); + CHECK(is_empty(tell(11, "12 AWAIT .")) && ended == 0 && pool[1].n.mem[AWAIT_FROM] == 12, "node 11 waits for node 12's answer"); + ways11 = pool[1].n.mem[ROUTE_COUNT]; + CHECK(is_empty(tell(12, "12 11 GONE")) && ended == 1 && ended_from == 12 && pool[1].n.mem[AWAIT_FROM] == 12 && pool[1].n.mem[ROUTE_COUNT] == ways11, + "node 12 tells it, by the wire between them, that 12 is gone: that is not by its centre's port, and it goes on waiting"); + CHECK(is_empty(tell(12, "11 NO-ROUTE 12 11 GONE")) && ended == 1 && ended_from == 12 && pool[1].n.mem[AWAIT_FROM] == 12 && pool[1].n.mem[ROUTE_COUNT] == ways11, + "node 12 says it again by way of node 10: it comes by the centre's port but is not from the centre, and node 11 goes on waiting, its ways as they were"); + CHECK(strstr(tell(10, "12 11 GONE"), "Node gone") != NULL && pool[1].n.mem[AWAIT_FROM] == 0, "node 10, its centre, says it: the wait ends: \"%s\"", printed); + CHECK(is_empty(tell(11, "12 3 ROUTE")) && is_empty(tell(12, "11 2 ROUTE")), "the ways are told again"); + + /* an answer that came before the wait began is in the messages waiting, and is found there */ + CHECK(strstr(tell(12, ": QF S\" 1 DROP\" 77 SEND 3000 0 DO LOOP S\" 1 DROP\" 10 SEND 77 AWAIT . ; QF"), "Message refused") != NULL && ended_how == V4_TEXT_ERROR + && pool[2].n.mem[AWAIT_FROM] == 0 && waiting(far), + "node 12 sends to 77, is busy a while, sends another message -- taking in the refusal as it does -- and then waits for 77's answer: the wait ends at once: \"%s\"", printed); + CHECK(strcmp(tell(12, "7 8 * ."), "56 ") == 0, "and it goes on"); + + /* a waiting sender whose message found no room */ + { + 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"); + (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 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"); + } + + /* an answer that is refused on its way back is told to the node that waits for it, not to the node that answered */ + { + v4_cell r12 = pool[2].n.mem[REFUSED]; + CHECK(is_empty(tell(12, "10 2 ROUTE")), "node 12 is told the way to node 10 is by node 11"); + CHECK(is_empty(tell(11, "0 GOT !")) && is_empty(tell(11, "55 AWAIT .")) && pool[1].n.mem[AWAIT_FROM] == 55, "node 11 waits on a node there is not"); + CHECK(is_empty(tell(10, "F40")) && pool[1].n.mem[OWED_COUNT] == 0, "node 10 sends it as many messages as it has room for"); + (void)tell(10, ": RA S\" 1 DROP\" 12 SEND 3000 0 DO LOOP 55 11 GONE 12 AWAIT . ; RA"); + CHECK(strstr(printed, "Message refused") != NULL && pool[0].n.mem[AWAIT_FROM] == 0, + "node 10 sends node 12 text; its answer comes back by node 11, which has no room for it; node 10 then ends node 11's wait and waits for the answer: it is told the message was refused: \"%s\"", printed); + CHECK(pool[2].n.mem[REFUSED] == r12, "node 12, which answered, is told nothing"); + CHECK(strtol(tell(11, "GOT @ ."), NULL, 10) == 40 && is_empty(tell(12, "10 3 ROUTE")) && strcmp(tell(12, "7 8 * ."), "56 ") == 0, "node 11 did the forty it had kept; the way is told again"); + } + + /* a message that would go round for ever is refused after 16 nodes have passed it on */ + { + v4_cell refused = pool[0].n.mem[REFUSED], l10 = pool[0].n.mem[LOST], l11 = pool[1].n.mem[LOST]; + CHECK(is_empty(tell(10, "66 2 ROUTE")), "node 10 is told the way to 66 is by node 11, whose way for everything it has not been told of is back by node 10"); + CHECK(is_empty(tell(10, ": H66 S\" 1 DROP\" 66 SEND ; H66")) && ended == 1 && ended_how == V4_TEXT_COMPLETED, "node 10 sends 66 a message: it comes to rest"); + CHECK(pool[0].n.mem[LOST] + pool[1].n.mem[LOST] == l10 + l11 + 1 && pool[0].n.mem[REFUSED] == refused + 1, + "one of the two let it go when it had been passed on 16 times, and node 10 was told it was refused"); + CHECK(is_empty(tell(10, "66 NO-ROUTE")) && waiting(hera) && waiting(mid) && waiting(far), "the way is forgotten, and all three are at rest"); + } + + /* 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"); + (void)tell(12, "7 8 * ."); + CHECK(strcmp(printed, "56 ") == 0 && printed_from == 12, "node 12 goes on doing what it is sent: \"%s\"", printed); + + /* 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"); + (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(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"); + + /* 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(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"); + 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 55d480e9..2c6c7fb0 100644 --- a/v4/tests/test_host_quit.c +++ b/v4/tests/test_host_quit.c @@ -63,6 +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; /* (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 @@ -199,7 +200,7 @@ static int text_ended; /* how the text ended, when it has */ /* 1 if the node is where it waits for a message, with nothing given it. */ static int node_idle(void) { - return n.reading && !n.given && n.read_port == V4_PORT_ANY && n.p > w_idle && n.p <= w_idle + 8; /* in the first words of (IDLE), where it reads */ + return n.reading && !n.given && n.read_port == V4_PORT_ANY && n.p > w_idle && n.p <= w_idle_end; /* in (IDLE), where it reads */ } /* Run until the text ends, or until it has been inside KEY with nothing to @@ -278,7 +279,7 @@ static void boot_with(unsigned depth) for (i = 0; i < depth; i++) v4_dstack_push(&n.ds, (v4_cell)(0x5A000000 + i)); v4_dstack_push(&n.ds, CANARY); v4_node_port_attach(&n, PORT); - v4_node_port_status(&n, 0, 1u | 1u << CONSOLE_PORT); /* the kernel and the console take what is written when it is written */ + v4_node_port_status(&n, 0, (1u | 1u << CONSOLE_PORT) * (1u + (1u << V4_PORTS))); /* the kernel and the console take what is written when it is written */ n.mem[WORD_DEFINED] = 0; /* no one is told of entries, until a test says so */ n.mem[WORD_FORGOTTEN] = 0; known_n = 0; @@ -292,7 +293,7 @@ static void boot_with(unsigned depth) line_open = 0; to_node.count = 0; to_sent = 0; from_node.count = 0; shown_len = 0; shown_dropped = 0; - for (i = 0; i < 8 && !n.reading; i++) (void)v4_exec_step_word(&n, &es, &h); /* no message waits: it reads its ports, and is blocked */ + for (i = 0; i < 64 && !n.reading; i++) (void)v4_exec_step_word(&n, &es, &h); /* no message waits: it reads its ports, and is blocked */ } static void boot(void) { boot_with(0); } /* The same with nothing at all on the data stack. */ @@ -869,6 +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, "(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)"); diff --git a/v4/tests/test_host_unit.c b/v4/tests/test_host_unit.c index 8f0f5d84..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 @@ -381,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; @@ -546,14 +548,28 @@ int main(void) } step_limit = 30000000ul; + /* 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 && 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"), + "a line typed at the console ends the line that was blocked, with an error that says so, and is then done: \"%s\"", r); + CHECK(node_numbered(11) == NULL && !node_numbered(10)->asking && waiting(10), "the line killed the stuck node, and Hera is back at her ports"); + } + CHECK(strcmp(tell(10, "1 2 + ."), "3 ") == 0, "and she goes on"); + step_limit = 30000000ul; + /* what is not a node she has had born */ { unsigned born = born_count; CHECK(strstr(tell(10, "99 KILL"), "KILL: no such node") != NULL && ended_how == V4_TEXT_ERROR, "KILL of a node there never was is an error that says so: \"%s\"", printed); CHECK(strstr(tell(10, "10 KILL"), "KILL: no such node") != NULL && ended_how == V4_TEXT_ERROR && node_numbered(10) != NULL, "so is KILL of Hera herself"); CHECK(strstr(tell(10, "14 KILL"), "KILL: no such node") != NULL && ended_how == V4_TEXT_ERROR, "and of a node she has killed already"); - CHECK(born_count == born && node_numbered(11) != NULL && strcmp(tell(11, "11 100 * ."), "1100 ") == 0 && strcmp(tell(10, "1 2 + ."), "3 ") == 0, - "nothing was removed, and the two that are left go on"); + CHECK(born_count == born && strcmp(tell(10, "1 2 + ."), "3 ") == 0, + "nothing was removed, and Hera goes on"); } printf(" %d checks, %d failures\n", checks, failures);