/* SPDX-License-Identifier: LicenseRef-Starship-1.0 * Copyright (c) 2023-2026 Robert A. James */ /* test_stadium_words.c -- stadium_words_charge on the host (#189, plan 2026-11-13-engine-step4-phase2, * Task 4): a v4 node's words charged to the Stadium once a heartbeat tick (Captain Bob, 2026-11-13: "Per * heartbeat tick"). N calls of a word are N of stadium_word_dispatch's dispatches -- the same quantum, * the same floor left in the reservoir, the same cooling by the ticks since -- and when the words * called want more than there is, what there is is shared by their calls, not taken by whichever is * charged first. Conservation holds throughout. */ #include "harness.h" #include "starkernel/vm/stadium.h" #include "starkernel/vm/stadium_words.h" #include "starkernel/session.h" #include "starkernel/vm_uuid.h" #include "starkernel/kmalloc.h" #include "starkernel/q48_16.h" #include "starforth_config.h" #include #include #include #include void *kmalloc(size_t size) { return calloc(1, size); } void kfree(void *ptr) { free(ptr); } kmalloc_stats_t kmalloc_get_stats(void) { kmalloc_stats_t s; memset(&s, 0, sizeof s); s.free_bytes = 512u * 1024u * 1024u; return s; } void log_message(int level, const char *fmt, ...) { (void)level; (void)fmt; } void sk_hal_panic(const char *message) { printf("PANIC: %s\n", message); exit(2); } int blk_flush(uint32_t block_num) { (void)block_num; return 0; } int main(void) { VMUuid a = stadium_node_key(10); uint64_t q = (uint64_t)STADIUM_WORD_HEAT_QUANTUM, r0, pullable; uint32_t ids[3]; uint64_t calls[3]; CHECK(stadium_boot_init_for(a) == 0 && session_boot_init() == 0 && stadium_birth_node(a, "Hera") == 0, "a Stadium with the node born in it"); stadium_words_init(); /* the same as that many dispatches: word 7 charged five calls, word 8 dispatched five times */ ids[0] = 7; calls[0] = 5; r0 = stadium_reservoir_peek(a); stadium_words_charge(a, ids, calls, 1, 2); CHECK(stadium_reservoir_peek(a) == r0 - 5u * q && stadium_words_resident_heat(a) == 5u * q, "five calls of word 7 pull five quanta, and it holds them: %llu", (unsigned long long)(r0 - stadium_reservoir_peek(a))); { unsigned k; r0 = stadium_reservoir_peek(a); for (k = 0; k < 5; k++) stadium_word_dispatch(a, 8, 2); CHECK(stadium_reservoir_peek(a) == r0 - 5u * q && stadium_words_resident_heat(a) == 10u * q, "the same as five dispatches of a word"); } CHECK(stadium_conserved(a), "conservation holds"); /* a resident word cools by the ticks since, once, then takes its calls: as its dispatches would */ { uint64_t before = stadium_words_resident_heat(a); ids[0] = 7; calls[0] = 2; stadium_words_charge(a, ids, calls, 1, 12); ids[0] = 8; calls[0] = 1; stadium_word_dispatch(a, 8, 12); stadium_word_dispatch(a, 8, 12); CHECK(stadium_words_resident_heat(a) < before + 4u * q, "ten ticks on, both cooled before they took their calls"); CHECK(stadium_conserved(a), "conservation holds"); } /* no calls, nothing */ ids[0] = 9; calls[0] = 0; r0 = stadium_reservoir_peek(a); stadium_words_charge(a, ids, calls, 1, 3); CHECK(stadium_reservoir_peek(a) == r0, "a word with no calls is not charged"); /* more wanted than there is: shared by calls, the floor left */ pullable = stadium_reservoir_peek(a) - Q48_ONE / 3; ids[0] = 20; calls[0] = 3 * (pullable / q); ids[1] = 21; calls[1] = 1 * (pullable / q); stadium_words_charge(a, ids, calls, 2, 4); CHECK(stadium_reservoir_peek(a) >= Q48_ONE / 3 && stadium_reservoir_peek(a) - Q48_ONE / 3 < 4u, "the reservoir is left at its third"); CHECK(stadium_conserved(a), "conservation holds when the calls want more than there is"); return DONE(); }