[M1-SYS-02] System scheduler - #25
Merged
Merged
Conversation
The system scheduler (M1-SYS-02 scope, nothing else): turns the M1-SYS-01 registry (registration order + declared depends_on + declared component I/O) into the per-tick execution order and runs the systems in it. - SystemSchedule: the computed execution order (systemCount + dense SystemId order array). - World::scheduleSystems: the stable topological sort of the registration order plus the depends_on edges (Kahn, min-id tie-break; a system only moves later, behind its dependencies). Pre-run validation (first failure wins; every failure one rate-limited structured warn + Status, FR-12.3): unknown dependency name (system/dep_missing), dependency cycle (system/dependency_cycle, one concrete cycle reported), two systems writing the same component (system/double_writer); a declared read ordered before a declared write WARNs without failing (system/read_before_write). - World::runSystems: one sim tick's system phase — strictly one system at a time, in schedule order, on the owner thread; fresh non-owning SystemContext per system; stale (system/schedule_stale) and malformed (system/schedule_invalid) schedules rejected. - SystemDef gains dependsOn (the raw comma-separated registration-name spec); LAIGE_SYSTEM becomes variadic over the optional trailing depends_on names (stringified verbatim); kMaxSystemDependencies = 16. - detail::DepSpecParse/DepSpecError/parseDepSpec/depSpecErrorName (system.h; defined in systems.cpp): registration validates the spec (system/dep_spec_invalid); scheduling resolves the names. - Determinism (ARCH-010): pure integer/string bookkeeping — no floating point, no randomness, no addresses in the order or the warning set. No allocation at scheduling or per tick (PERF-003); no logging on the success paths (LOG-003). Docs: docs/api/scheduler.md (full contract + Performance) linked from docs/README.md; docs/api/system_registry.md updated (macro shape, dep_spec_invalid row, cross-refs); src/laige-sim/README.md status; system.h/entity.h preambles carry the M1-SYS-02 note. Tests: new SystemScheduler suite (26 tests) + CTest entry 'scheduler' (TSan property list). laige-api.json regenerated (496 symbols; api-real-tree green).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Roadmap step M1-SYS-02 (system scheduler,
roadmap/M1-heartbeat.md), scope only.What
The scheduler turns the M1-SYS-01 registry (registration order + declared
depends_on+ declared component I/O) into the per-tick execution order, validates it once, and runs the systems in that order:src/laige-sim/include/laige/sim/system.h:SystemSchedule— the computed execution order (the systemCount plus the dense SystemId order array);SystemDefgainsdependsOn— the raw depends_on spec (comma-separated registration names;nullptr/""= no dependencies);LAIGE_SYSTEM(Name, budget_ms, Dep..., ...)is now variadic — the optional trailing depends_on names are stringized verbatim into the spec (LAIGE_SYSTEM(Health, 1, Spawner)= spec"Spawner");kMaxSystemDependencies = 16— the direct-dep bound (CORE-005; a barrier is registration position, not a dependency list);detail::DepSpecParse/DepSpecError/parseDepSpec/depSpecErrorName— the spec parse (no copy, no allocation; the tokens point into the spec literal).World::scheduleSystems(SystemSchedule&) const(setup phase; pure registry read) — the stable topological sort: Kahn's algorithm with a min-id tie-break; a system only moves LATER, behind its dependencies (no dependencies = exactly the registration order). Pre-run validation (first failure wins; every failure one rate-limited structured warn +Status— FR-12.3): unknown dependency name (system/dep_missing), dependency cycle (system/dependency_cycle, one concrete cycle reported: the deterministic walk from the smallest remaining id), two systems writing the same component in one tick (system/double_writer, order-independent); a declared read ordered before a declared write of the same component is warn-only (system/read_before_write) — the schedule succeeds and the reader sees the previous tick's value.World::runSystems(const SystemSchedule&)— one sim tick's system phase: the systems run strictly one at a time, in schedule order, on the world's single owner thread (PRD §10.2); a fresh non-owningSystemContextper system; a stale schedule (system/schedule_stale) and a hand-built malformed schedule (system/schedule_invalid) are rejected.systems.cpp(no new source file; the sim CMake comment updated).Validation (normative order — first failure wins)
kMaxSystemDependencies)InvalidArgument+ warnsystem/dep_spec_invalidInvalidArgument+ warnsystem/dep_missingInvalidArgument+ warnsystem/dependency_cycleInvalidArgument+ warnsystem/double_writersystem/read_before_writeInvalidArgument+ warnsystem/schedule_staleInvalidArgument+ warnsystem/schedule_invalidEvery warn is rate-limited (LOG-004), subsystem
system, with stable identifying fields (system/missing_dep/position,cycle,component_id/first_writer/second_writer,reader/writer/component_id,scheduled_systems/current_systems,slot/id,name/error). The success paths log nothing (LOG-003).Tests
New
SystemSchedulersuite — 26 tests, CTest entryscheduler(also in the TSan property list): the order properties (no-deps = registration order, forward/backward deps, the chain, the diamond with a reversed spec list — the dependency set is orderless, the macro spec stringization, the known-answer pinscheduler-order systems=5 fnv1a=0xaef3282f393ab332), running in scheduled order with state flow (writer→reader sees the fresh value; reader→writer observes the stale value), every validation event with sink field checks (incl. therate_limitedsummary on shutdown), the spec validation (whitespace trimming legal, the 17-dep bound), the empty + moved-from world, the stale and the malformed hand-built schedules, the order bit-identical across two worlds (ARCH-010), and the zero-alloc window (100 ticks × 3 systems over 4 entities: scheduling + everyrunSystemsallocates nothing —scheduler-zeroalloc ticks=100 allocs=0; test-only operator-new counter, non-sanitizer trees; the sanitizer trees prove it leak-free).Verify (local)
ctest -R schedulergreen (26/26, incl. the zero-alloc window); full suite 42/42 onbuild.build-asan(leak-free),build-tsan,build-clang,build-release,build-shared; zero new warnings under NFR-8.10.laige-api.jsonregenerated (496 symbols, +7:kMaxSystemDependencies,SystemDef::dependsOn,SystemSchedule×3,World::scheduleSystems/runSystems);api-real-treegreen.tools/laige-include-lintOK (27 source files, 1/10 vendored deps).Docs (CORE-006)
docs/api/scheduler.md(full contract + Performance section), linked fromdocs/README.md(the API list + the per-module laige-sim list, which gains the previously missingsystem_registry.mdentry);docs/api/system_registry.mdupdated (the macro is variadic now, the validation table gains thedep_spec_invalidrow, cross-refs to scheduler.md);src/laige-sim/README.mdstatus updated;system.h/entity.hpreambles + member docs carry the M1-SYS-02 note.Notes
depends_onnames registration names, not function addresses orSystemIds (per-world runtime values): forward dependencies (registered later) are legal — the spec's shape is validated at registration, its names resolved at scheduling time.runSystemsfrom inside a system is documented misuse (it breaks the declared order contract; the one-writer-per-component invariant still prevents state corruption).