/* SPDX-License-Identifier: LicenseRef-Starship-1.0 * Copyright (c) 2023-2026 Robert A. James */ /* test_stadium_ops.c -- kernel/src/vm/stadium_ops.c on the host (#189, plan * 2026-11-13-engine-step4-phase1, Task 3): HEAT-OP into the Stadium (Captain Bob, 2026-11-12: "Add * HEAT-OP ... Both"; 2026-11-13: "Per heartbeat tick"). A node's 32 opcodes are patrons; at a charge, * each opcode's retirements since the last are charged as that many of the word layer's dispatches -- * the same quantum, the same floor on the reservoir -- and a resident one cools by the ticks since, * as a word does. Conservation holds throughout. */ #include "harness.h" #include "starkernel/vm/stadium.h" #include "starkernel/vm/stadium_ops.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; /* room for more than one quota, as on the boot (50 VM slots) */ 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 key = stadium_node_key(10); uint64_t counts[32], before, h20, r, q = (uint64_t)STADIUM_WORD_HEAT_QUANTUM; unsigned k, hot = 0; CHECK(stadium_boot_init_for(key) == 0 && session_boot_init() == 0 && stadium_birth_node(key, "Hera") == 0, "a Stadium with the node born in it"); stadium_ops_init(); memset(counts, 0, sizeof counts); stadium_ops_charge(key, counts, 1); for (k = 0; k < 32; k++) if (stadium_ops_heat(key, k) != 0) hot++; CHECK(hot == 0 && stadium_reservoir_peek(key) == Q48_ONE, "no retirements, no heat: the reservoir holds 1.0"); counts[20] = 5; /* + retired five times */ before = stadium_reservoir_peek(key); stadium_ops_charge(key, counts, 2); h20 = stadium_ops_heat(key, 20); CHECK(h20 == 5u * q && stadium_reservoir_peek(key) == before - 5u * q, "five retirements of + are five dispatches' heat, pulled from the reservoir (%llu)", (unsigned long long)h20); CHECK(stadium_ops_heat(key, 21) == 0, "and no other opcode is heated"); CHECK(stadium_conserved(key), "conservation holds"); stadium_ops_charge(key, counts, 12); /* ten ticks later, nothing retired */ CHECK(stadium_ops_heat(key, 20) < h20 && stadium_reservoir_peek(key) > before - 5u * q, "with nothing retired, + cools over ten ticks, back to the reservoir"); CHECK(stadium_conserved(key), "conservation holds"); counts[24] = (uint64_t)1 << 40; /* dup, very many times */ stadium_ops_charge(key, counts, 13); r = stadium_reservoir_peek(key); CHECK(r >= Q48_ONE / 3, "a flood of retirements leaves the reservoir its floor, a third (%llu)", (unsigned long long)r); CHECK(stadium_ops_heat(key, 24) > 0 && stadium_conserved(key), "dup is heated, and conservation holds"); CHECK(stadium_ops_heat(vm_uuid_hera(), 20) == 0, "a key with no quota has no opcode heat"); /* more wanted than the reservoir has: shared by retirements, not by the order the opcodes are * charged in -- a low-numbered opcode retired a little does not take it all */ { VMUuid k2 = stadium_node_key(12); uint64_t c2[32], h0, h5; CHECK(stadium_grant_quota(k2, key) == 0, "a second node, its own quota"); memset(c2, 0, sizeof c2); c2[0] = 1000000; /* ; retired a million times */ c2[5] = 3000000; /* next three million */ stadium_ops_charge(k2, c2, 20); h0 = stadium_ops_heat(k2, 0); h5 = stadium_ops_heat(k2, 5); CHECK(h0 > 0 && h5 > 0, "both opcodes are heated (; %llu, next %llu)", (unsigned long long)h0, (unsigned long long)h5); CHECK(h5 > 2u * h0 && h5 < 4u * h0, "in proportion to their retirements, about 3 to 1"); CHECK(stadium_reservoir_peek(k2) >= Q48_ONE / 3 && stadium_conserved(k2), "the floor kept, conservation held"); } return DONE(); }