From 487e7b94190ad7de19a90679eb0f77e0c505f919 Mon Sep 17 00:00:00 2001 From: Rongsong Shen Date: Fri, 18 Sep 2026 04:46:49 +0000 Subject: [PATCH] fix(scheduler-sim-diagnostics): bootstrap limit plugin in sim mode (bug-1825) info limit was correctly implemented and tested but inert against a real replay: register_limit()'s only call site was normal-mode sched_register_ plugins(); sim mode's own plugin bootstrap (sim_bootstrap_picker()) never registered the limit plugin. The snapshot format already carries the original session's limit config (sim->core.config, a borrowed reference set by xce_sched_core_load_snapshot()) -- nothing needed to change in the recording/replay format. New sim_bootstrap_limit_plugin() (sim_limit.c, mirroring sim_picker.c's structure) registers the plugin via plugin_register_core() and seeds it via a new limit_seed_config(plugin, xce_sched_core_t* core, config) entry point, wired into sim_loop.c alongside the existing sim_bootstrap_picker() call. limit_new_plugin() extracted from register_limit() as a factory, mirroring builtin_fcfs_new_plugin()'s established pattern; register_limit() itself is behavior-preserving. First attempt set plugin->sched to a disguised xce_sched_sim_driver_t*, relying on it sharing xce_scheduler_t's first-member layout to reuse the existing config_hook path -- rejected on review: this repeats the exact cross-cast pattern that already corrupted memory once in this codebase (see core.h's xce_core_cache_clear_cb_t history). Fixed by making limit_seed_config() take xce_sched_core_t* as an honest parameter instead, with limit_update_config() (normal mode) and the new sim path both delegating to a shared limit_seed_config_impl() -- no cast, no aliasing risk, independently verified by two review rounds reading the actual struct layouts and call chains. Also added sim_limit.c to sched_sim_isolation_lib so Req 5 AC 17's controller-I/O-isolation check covers it (an initial gap, closed in the same remediation round). Two ripple waves of the same established link-stub pattern applied to the ~24 white-box test files that #include sim_loop.c. info limit now shows real configured-limit data against a genuine replay. Live usage will still correctly show "no active usage instance yet" until a separate, larger fix (logged as bug-1826) wires limit *matching* into the sim decision path -- deliberately out of this task's scope. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01RLSNJhNoULvVcUhXEFafQC --- .../specs/scheduler-sim-diagnostics/tasks.md | 5 +- .wolf/buglog.json | 25 ++- src/scheduler/include/plugin.h | 34 ++++ src/scheduler/meson.build | 36 ++-- src/scheduler/src/plugins/limit.c | 106 +++++++++- src/scheduler/src/sim_limit.c | 143 ++++++++++++++ src/scheduler/src/sim_loop.c | 11 ++ .../test_maxtasks_wiring_sim_driver_parity.c | 36 ++++ .../test_repl_breakpoints_inspection.c | 36 ++++ .../testsuite/test_repl_equivalence.c | 36 ++++ .../testsuite/test_repl_event_echo.c | 36 ++++ .../testsuite/test_repl_execution_control.c | 36 ++++ src/scheduler/testsuite/test_repl_help.c | 36 ++++ .../testsuite/test_repl_limit_table.c | 181 ++++++++++++++++++ .../testsuite/test_repl_prompt_context.c | 36 ++++ .../testsuite/test_repl_resource_table.c | 36 ++++ .../testsuite/test_repl_round_rollup.c | 36 ++++ .../testsuite/test_repl_snapshot_params.c | 36 ++++ .../testsuite/test_repl_step_verbosity.c | 36 ++++ src/scheduler/testsuite/test_sim_alt_events.c | 36 ++++ .../testsuite/test_sim_contention_parity.c | 36 ++++ .../test_sim_e2e_original_motivation.c | 36 ++++ .../testsuite/test_sim_equivalence.c | 36 ++++ src/scheduler/testsuite/test_sim_event_loop.c | 36 ++++ .../testsuite/test_sim_print_decision_gate.c | 36 ++++ .../test_sim_reconcile_unaccounted.c | 36 ++++ .../testsuite/test_sim_record_output.c | 36 ++++ .../testsuite/test_sim_resource_contention.c | 36 ++++ .../test_sim_round_boundary_reconciliation.c | 36 ++++ .../testsuite/test_sim_session_ordering.c | 36 ++++ .../test_sim_snapshot_reconcile_gap.c | 36 ++++ src/scheduler/testsuite/test_sim_verify.c | 36 ++++ 32 files changed, 1377 insertions(+), 28 deletions(-) create mode 100644 src/scheduler/src/sim_limit.c diff --git a/.kiro/specs/scheduler-sim-diagnostics/tasks.md b/.kiro/specs/scheduler-sim-diagnostics/tasks.md index a7b2b231..cb25c2ed 100644 --- a/.kiro/specs/scheduler-sim-diagnostics/tasks.md +++ b/.kiro/specs/scheduler-sim-diagnostics/tasks.md @@ -8,7 +8,7 @@ - _Requirements: 1.1, 1.2, 1.4, 2.1, 3.1_ - _Boundary: Limit Plugin Accessor_ -- [ ] 6. Bootstrap the limit plugin in sim mode so `info limit` shows real data against a replay (fixes `bug-1825`) +- [x] 6. Bootstrap the limit plugin in sim mode so `info limit` shows real data against a replay (fixes `bug-1825`) - Extract `limit_new_plugin()` factory out of `register_limit()` (`plugins/limit.c`), mirroring `builtin_fcfs_new_plugin()`/`register_builtin_fcfs()`'s existing factory-extraction pattern exactly; `register_limit()` itself must be behaviorally unchanged (pure refactor) - New `sim_bootstrap_limit_plugin(xce_sched_sim_driver_t* sim)` (new file `sim_limit.c`, mirroring `sim_picker.c`'s structure/header-comment convention): `limit_new_plugin()` → `plugin_register_core(&(sim->core), plugin)` → if `sim->core.config != NULL`, call `plugin->hooks.config_hook(plugin, sim->core.config)` directly (NOT the type-unsafe `plugin_config_update()`, which casts its `void*` arg to `xce_scheduler_t*` and would misread `xce_sched_sim_driver_t`'s embedded `core` field) - Wire the call into `sim_loop.c` right alongside the existing `sim_bootstrap_picker(sim)` call site — by that point `sim->core.config` already holds the snapshot's borrowed-reference config (set during `sched_sim_driver_start()`'s snapshot load, which runs first) @@ -76,4 +76,5 @@ No `(P)` markers are used anywhere in this plan. Tasks 2.1–2.3 and 3.1–3.2 a - **Task 2.1**: Making `repl_info.c` genuinely call `limit_get_configured_limits()` broke the link for ~24 pre-existing white-box test files that `#include "../src/repl_info.c"` without also linking `plugins/limit.c` — this codebase's white-box test convention means any new cross-file call from an included production file ripples to every file that includes the caller but not the callee. Fixed with an identical 5-line link-satisfying stub (`return NULL`) added to each, matching the established "duplicate, don't share" stub idiom already used throughout this file family; verified behavior-preserving since none of those 24 fixtures register the limit plugin (the real function would also return `NULL` there). **Task 3 (which calls `limit_get_usage_instances()`) will very likely hit the identical ripple across an overlapping or identical file set — expect it, and apply the same fix pattern rather than treating it as a surprise.** - **Task 3.1**: found and fixed a real latent bounds-check imprecision (negative/stale `idx` not safely rejected by a `.count`-based pre-check) while writing the new GLOBAL/SHARED usage-read code — logged as `bug-1824` in `.wolf/buglog.json` rather than fixed in the pre-existing `repl_info_resource_one()` (out of this task's boundary); the new code uses the corrected pattern instead. Task 3.2 confirmed it did NOT repeat the same mistake in its own per-host reads. - **Task 3.2**: reviewer approved with one non-blocking suggestion — no test directly exercises Requirement 3.1's "per-user and per-host together" combined-dimension case end-to-end (structurally supported by the code, per independent review inspection, just not covered by a dedicated fixture). Worth adding if this area gets touched again; not required before shipping. -- **Task 5 — significant follow-up needed (logged as `bug-1825`)**: all 5 tasks are correctly implemented, thoroughly tested, and genuinely read-only/deterministic — but `info limit` is currently **inert in real production usage**. `register_limit()` has exactly one call site in the whole codebase (`scheduler.c`'s normal-mode `sched_register_plugins()`); sim mode's own plugin bootstrap (`sim_bootstrap_picker()`, `sim_picker.c`) only registers the picker plugin, never the limit plugin. So a real operator running `xce_scheduler -i`/`-s ` will always see "no limits configured"/"no such limit" from `info limit`, regardless of what the original live recording had configured — every positive-display test in this spec exercises the code via a test-only plugin-registration helper with no production equivalent in sim mode. This is a pre-existing sim-mode driver gap (same class as `scheduler-driver-equivalence.md`'s other documented normal-vs-sim parity gaps), not a defect in this spec's own work, and was out of task 5's test-only boundary to fix. **Recommended as the next piece of work for this open-ended spec** (or a small companion fix): add a sim-mode limit-plugin bootstrap analogous to `sim_bootstrap_picker()`, and confirm `.replay` files actually carry the original limit configuration for such a bootstrap to register from (not yet verified — check this first). +- **Task 5 — significant follow-up needed (logged as `bug-1825`)**: all 5 tasks are correctly implemented, thoroughly tested, and genuinely read-only/deterministic — but `info limit` is currently **inert in real production usage**. `register_limit()` has exactly one call site in the whole codebase (`scheduler.c`'s normal-mode `sched_register_plugins()`); sim mode's own plugin bootstrap (`sim_bootstrap_picker()`, `sim_picker.c`) only registers the picker plugin, never the limit plugin. So a real operator running `xce_scheduler -i`/`-s ` will always see "no limits configured"/"no such limit" from `info limit`, regardless of what the original live recording had configured — every positive-display test in this spec exercises the code via a test-only plugin-registration helper with no production equivalent in sim mode. This is a pre-existing sim-mode driver gap (same class as `scheduler-driver-equivalence.md`'s other documented normal-vs-sim parity gaps), not a defect in this spec's own work, and was out of task 5's test-only boundary to fix. Fixed by task 6. +- **Task 6 — a real memory-safety lesson, caught by review before it shipped**: the first implementation attempt set `plugin->sched = (void*)sim` (a `xce_sched_sim_driver_t*`, disguised) before invoking the limit plugin's `config_hook`, relying on both `xce_scheduler_t` and `xce_sched_sim_driver_t` happening to have `xce_sched_core_t core` as their first struct member. An independent reviewer rejected this: `core.h`'s own `xce_core_cache_clear_cb_t` doc comment documents that this EXACT cross-cast pattern already corrupted memory once before in this codebase (`cache_clear()` reinterpreting `xce_sched_sim_driver_t`'s `alt_events_file` as a `kvtab_t*`), and there was no compiler/runtime guard against a future edit to the callee reading past `.core`. Fixed by adding `limit_seed_config(plugin, xce_sched_core_t* core, config)` — an honestly-typed entry point taking `core` as a real field access, no cast — with `limit_update_config()` (normal mode) and `sim_bootstrap_limit_plugin()` (sim mode) both delegating to a shared `_impl` function. **Lesson for future work in this codebase**: when a plugin hook or helper is invoked from BOTH a normal-mode (`xce_scheduler_t`) and a sim-mode (`xce_sched_sim_driver_t`) context, never derive one struct type from the other via a first-member cast, even when it "happens to work" today — take `xce_sched_core_t*` as an honest parameter instead. Two independent reviewers (one per round) verified this by reading the actual struct definitions and the full call chain, not by trusting the report. diff --git a/.wolf/buglog.json b/.wolf/buglog.json index d1fdbce6..b42361f6 100644 --- a/.wolf/buglog.json +++ b/.wolf/buglog.json @@ -22428,7 +22428,7 @@ "error_message": "NOT A CRASH -- significant, verified architectural gap discovered while closing out scheduler-sim-diagnostics task 5 (final validation task for the new `info limit` REPL command). The limit plugin is never registered in ANY simulation-mode driver path, so `info limit ` will ALWAYS report \"no limits configured\"/\"no such limit\" for a real operator running `xce_scheduler -i`/`-s ` against a genuine recording -- even one whose original live run had limits configured and actively enforced. The entire feature built across scheduler-sim-diagnostics tasks 1-5 is correctly implemented, thoroughly tested, and read-only/deterministic as designed, but is currently INERT in real production usage: every positive-display test path (tasks 2-3, showing a limit's configured setting and live usage) only exercises the code via a test-only helper that hand-registers the plugin into core->plugins directly, which has no production equivalent in sim mode.", "file": "src/scheduler/src/sim_picker.c", "root_cause": "register_limit() (plugins/limit.c:609) has exactly one call site in the entire codebase: scheduler.c:605, inside sched_register_plugins(), which is itself called only from scheduler.c:827 -- the normal-mode (controller-connected, live) driver's sched_init() path. Sim mode's own plugin-bootstrap mechanism, sim_bootstrap_picker() (sim_picker.c), exists specifically to register a picker plugin (builtin_fcfs, via plugin_register_core()) for simulation/replay mode, since sim mode deliberately does not run the full normal-mode init sequence -- but it only ever registers the PICKER plugin category. It has no equivalent step for the limit plugin (or, by the same reasoning, potentially other plugin categories registered by sched_register_plugins() that were never audited for this gap). Confirmed via full-codebase grep: register_limit()/sched_register_plugins() are never referenced anywhere under sim.c/sim_loop.c/sim_picker.c/driver.c/repl*.c, and no write into core->plugins occurs anywhere in the sim-mode driver files. This is the same class of gap scheduler-driver-equivalence.md already documents for OTHER driver-parity properties (e.g. bug-1613's sched_account_jobs() gap) -- a sim-mode driver feature silently missing something the normal-mode driver does, discovered only when a REPL feature tried to read state that assumes it.", - "fix": "NOT FIXED -- discovered and characterized during scheduler-sim-diagnostics task 5 (final validation task), explicitly out of that task's test-only boundary per its own instructions (a production-code fix found during validation must be reported, not silently patched). Recommended fix: add a sim-mode limit-plugin bootstrap analogous to sim_bootstrap_picker() -- either a new sim_bootstrap_limit_plugin() called from the same place sim_bootstrap_picker() is invoked (sim_loop.c, per that function's own doc comment), or generalize sched_register_plugins() itself to be safely callable from sim mode (auditing whether builtin_fcfs's own registration would double-register if sim_bootstrap_picker() and sched_register_plugins() both ran). Needs a decision on whether the sim driver should register ALL production plugin categories (matching normal mode exactly, closing this gap generally) or add limit-specific bootstrap only (narrower, faster, but leaves the general problem for the next plugin type that needs sim-mode REPL visibility). Also needs the actual config source: sim mode's replay files must carry the original limit configuration if there is to be anything for a bootstrap step to register from -- confirm whether `.replay` files / ReplaySession already embed limit config, or whether that itself is a second, sequential gap. This second question was not investigated in this session -- flagged as the first thing a fix attempt needs to check.", + "fix": "FIXED (2026-09-17, scheduler-sim-diagnostics task 6). Added limit_new_plugin() factory (extracted from register_limit(), mirroring builtin_fcfs_new_plugin()'s established pattern) and a new sim_bootstrap_limit_plugin() (new file src/scheduler/src/sim_limit.c, mirroring sim_picker.c's structure exactly): registers the limit plugin via plugin_register_core() and seeds it with sim->core.config (the snapshot's own borrowed-reference config, which already carries the original session's limit configuration -- no recording/replay format change needed) via a new limit_seed_config(plugin, xce_sched_core_t* core, config) entry point. Wired into sim_loop.c alongside the existing sim_bootstrap_picker(sim) call.\n\nFirst attempt (round 1) set plugin->sched = (void*)sim (a disguised xce_sched_sim_driver_t*) to reuse the existing config_hook path -- REJECTED on independent review: this repeats the exact pointer-to-first-member cross-cast pattern that already corrupted memory once in this codebase (see this bug's own root_cause note and core.h's xce_core_cache_clear_cb_t doc comment for that prior incident). Round 2 eliminated the cast entirely: limit_seed_config() takes xce_sched_core_t* core as an honest parameter (a real field both xce_scheduler_t and xce_sched_sim_driver_t genuinely have), delegating to a shared limit_seed_config_impl() also used by normal-mode limit_update_config(). No cast, no aliasing risk -- confirmed structurally sound by two independent reviewers, each reading the actual struct definitions and full call chain rather than trusting the report.\n\nAlso closed: sim_limit.c added to sched_sim_isolation_lib's source list (meson.build) so Req 5 AC 17's mechanical controller-I/O-isolation check covers it (round 1 omitted this; round 2 fixed it, sched_sim_isolation_test independently re-verified passing with the new file included).\n\nRipple: two waves of an identical, minimal link-satisfying stub pattern across the ~24 white-box test files that #include \"../src/sim_loop.c\" (one for limit_new_plugin(), one for limit_seed_config()) -- same established 'duplicate, don't share' idiom this file family already used for the earlier scheduler-sim-diagnostics tasks' repl_info.c ripple.\n\nTDD: RED->GREEN verified for both the config-seed test and the remediation itself (a temporary limit_seed_config() no-op stub reproduced the exact expected single-test failure, reverted cleanly). Full scheduler suite: 78/78 Ok, 0 Fail; sched_repl_limit_table_test 26/26; sched_limit_plugin_test 5/5 (zero regression to normal-mode registration); sched_sim_isolation_test 5/5 (genuinely covering the new file).\n\nDeliberately NOT fixed (separate, larger, tracked as a distinct follow-up item in .kiro/specs/scheduler-sim-diagnostics/tasks.md, not this bug): limit *matching* (limit_job_election()) is still never invoked during replay -- it's only reachable via plugin_job_election(), which is normal-mode-only (one caller, scheduler.c) and would itself need a core-safe variant before it could be wired into sim_loop.c's decision path. So 'info limit' now shows real CONFIGURED settings against a real replay, but live USAGE will still correctly show 'no active usage instance yet' until that separate, larger fix lands.", "tags": [ "scheduler", "sim-mode", @@ -22442,6 +22442,29 @@ ], "occurrences": 1, "last_seen": "2026-09-17T07:10:00Z" + }, + { + "id": "bug-1826", + "timestamp": "2026-09-17T21:50:00Z", + "error_message": "NOT A CRASH -- known, deliberately out-of-scope follow-up discovered while fixing bug-1825 (sim-mode limit plugin bootstrap, scheduler-sim-diagnostics task 6). Bug-1825's fix makes the limit plugin's CONFIGURED SETTING visible in sim mode (info limit now shows a real replay's actual limit configuration). But limit *matching* -- the mechanism that actually populates live usage instances (xce_vres_t) as jobs get evaluated -- is still never invoked during replay. So info limit's live-usage display will correctly show 'no active usage instance yet' for every limit against any real replay, even one whose original live session actively enforced and charged against that limit.", + "file": "src/scheduler/src/plugin.c", + "root_cause": "limit_job_election() (plugins/limit.c, the function that calls match_job_global_limit_()/match_job_host_limit_() and populates xce_vres_t usage instances) is only ever invoked via plugin_job_election() (plugin.c:554), which -- like plugin_config_update()/plugin_prepare_allocation() -- takes a `void* arg` cast unconditionally to `xce_scheduler_t*` and reads `sched->core.plugins`. plugin_job_election() has exactly ONE call site in the entire codebase: scheduler.c (normal-mode only). Nothing in sim_loop.c's replay decision path calls plugin_job_election() or any core-safe equivalent, so no limit ever gets matched against any job during simulation/replay, regardless of whether the limit plugin is registered (bug-1825 fixes registration+config, not this).", + "fix": "NOT FIXED -- deliberately deferred, tracked in `.kiro/specs/scheduler-sim-diagnostics/tasks.md`'s Implementation Notes as a known non-goal of task 6, not bundled into that task's scope. Fixing this properly needs: (1) a core-safe variant of plugin_job_election() (mirroring the plugin_register()/plugin_register_core() split bug-1825 already established for registration, and the limit_update_config()/limit_seed_config() split for config-seeding) that takes xce_sched_core_t* honestly instead of casting a void* to xce_scheduler_t*; (2) wiring a call to it into sim_loop.c's replay decision path, at whatever point a job becomes eligible for dispatch -- this touches the scheduler's most invariant-sensitive area (see .kiro/steering/scheduler-driver-equivalence.md's documented state-equivalence contract and its required resource-contention-fixture testing discipline, plus the known related gaps bug-1611/bug-1613 already track for other normal-vs-sim parity issues in this same decision path). This is a materially larger, higher-risk change than bug-1825's registration+config fix and should go through this project's full TDD + contention-fixture validation discipline, not be rushed in as a drive-by addition.", + "tags": [ + "scheduler", + "sim-mode", + "repl", + "limit-plugin", + "limit-matching", + "plugin-job-election", + "architectural-gap", + "unfixed", + "discovered-during-scheduler-sim-diagnostics", + "driver-parity", + "follow-up-to-bug-1825" + ], + "occurrences": 1, + "last_seen": "2026-09-17T21:50:00Z" } ] } diff --git a/src/scheduler/include/plugin.h b/src/scheduler/include/plugin.h index dcd9c615..ef4c78c2 100644 --- a/src/scheduler/include/plugin.h +++ b/src/scheduler/include/plugin.h @@ -115,6 +115,40 @@ extern int register_fcfs(void*); extern int register_builtin_fcfs(void*); extern int register_limit(void*); +/* + * limit_new_plugin - factory half of register_limit()'s template-build + * step (plugins/limit.c), extracted so a caller with no xce_scheduler_t* + * (e.g. the simulation driver, via plugin_register_core() below) can + * obtain a freshly calloc'd, fully populated `xce_sched_plugin_t` for the + * limit plugin -- same hooks/init_fn/fini_fn template register_limit() + * itself uses, just not yet registered anywhere. Mirrors + * builtin_fcfs_new_plugin()'s identical extraction (below) exactly + * (scheduler-sim-diagnostics spec, task 6; bug-1825). Returns NULL on + * allocation failure. Caller owns the returned plugin (frees it only on a + * subsequent registration failure -- once registered, the plugin registry + * owns it, same lifetime rule every other plugin category in this + * codebase already follows). + */ +extern xce_sched_plugin_t* limit_new_plugin(void); + +/* + * limit_seed_config - bug-1825 remediation round 2 (scheduler-sim- + * diagnostics spec, task 6): honestly-typed config-seed entry point for a + * caller that holds a real xce_sched_core_t* but no real xce_scheduler_t* + * -- the simulation driver (sim_limit.c's sim_bootstrap_limit_plugin()), + * whose xce_sched_sim_driver_t (sim.h) has no xce_scheduler_t to point + * plugin->sched at. Takes `core` directly with no cast and never touches + * plugin->sched -- see the function's own doc comment (plugins/limit.c) + * for why a plugin->sched type-pun is unsafe here (core.h's + * xce_core_cache_clear_cb_t doc comment documents the real production bug + * this avoids repeating). Delegates to the same seed logic + * limit_update_config() (the normal-mode config_hook) itself uses, so the + * two entry points cannot silently diverge in behavior. + */ +extern int limit_seed_config(xce_sched_plugin_t* plugin, + xce_sched_core_t* core, + xce_conf4sched_t* config); + /* limit_get_configured_limits/limit_get_usage_instances - scheduler-sim- * diagnostics spec, Requirement Set 1 (Limit Plugin Accessor), task 1. * Read-only crossing of the plugin-private-state boundary: each looks up diff --git a/src/scheduler/meson.build b/src/scheduler/meson.build index 3b48ce09..7b8dc66e 100644 --- a/src/scheduler/meson.build +++ b/src/scheduler/meson.build @@ -17,6 +17,7 @@ scheduler_srcs = [ 'src/sim.c', 'src/sim_loop.c', 'src/sim_picker.c', + 'src/sim_limit.c', 'src/sim_verify.c', 'src/sim_record.c', 'src/repl.c', @@ -100,32 +101,37 @@ sched_core_isolation_lib = static_library( # the simulation driver's own files, consumed by # testsuite/test_sim_isolation.c to prove at build time that # sim.c/sim_loop.c/sim_record.c/repl.c/repl_fmt.c/repl_break.c/repl_info.c/ -# repl_params.c/sim_picker.c reference zero controller-facing symbols -# (client/netio/xceapi/delay/idgen). All files are compiled together into -# ONE archive -- task 11.2 added sim_loop.c as a sibling file to task -# 11.1's sim.c, task 11.5 added sim_record.c as a third, task 12.1 added -# repl.c as a fourth, task 12.2 added repl_break.c/repl_info.c as a -# fifth/sixth (task 12.2's own 800-line-cap split of repl.c's +# repl_params.c/sim_picker.c/sim_limit.c reference zero controller-facing +# symbols (client/netio/xceapi/delay/idgen). All files are compiled +# together into ONE archive -- task 11.2 added sim_loop.c as a sibling +# file to task 11.1's sim.c, task 11.5 added sim_record.c as a third, task +# 12.1 added repl.c as a fourth, task 12.2 added repl_break.c/repl_info.c +# as a fifth/sixth (task 12.2's own 800-line-cap split of repl.c's # breakpoint/state-inspection logic -- repl_internal.h's own "Why a split # at all" section has the full rationale), task 12.3 added repl_params.c # as a seventh (the same split, extended again for `snapshot`/`set`), # bug-1567's fix added sim_picker.c as an eighth (sim_loop.c's own # "zero line-budget headroom" -- same reason its siblings above exist -- # so the fix's picker-registration + post-load picker-rebuild-walk logic -# lives here instead of growing sim_loop.c past its cap), and the +# lives here instead of growing sim_loop.c past its cap), the # scheduler-sim-repl-verbosity spec's task 7.2 remediation added repl_fmt.c # as a ninth (repl.c's own 800-line-cap split of its shared, dependency-free # formatting helpers -- repl_fmt.c's own header comment has the full -# rationale); covering only a subset of them would repeat core.c's own -# siblings' (core_events.c/core_session.c/core_clock.c) -# never-mechanically-covered gap for the sim driver. Same "compiling to an -# object is enough, no real linking needed" rationale as -# sched_core_isolation_lib above. +# rationale), and the scheduler-sim-diagnostics spec's task 6 (bug-1825 +# fix, remediation round 2) added sim_limit.c as a tenth (sim_loop.c-cap +# sibling housing sim_bootstrap_limit_plugin(), mirroring sim_picker.c's +# own bootstrap-function role exactly) -- covering only a subset of them +# would repeat core.c's own siblings' (core_events.c/core_session.c/ +# core_clock.c) never-mechanically-covered gap for the sim driver, which is +# exactly the omission a prior round of this same task left in place by +# adding sim_limit.c to scheduler_srcs above without also adding it here. +# Same "compiling to an object is enough, no real linking needed" rationale +# as sched_core_isolation_lib above. sched_sim_isolation_lib = static_library( 'sched_sim_isolation', - ['src/sim.c', 'src/sim_loop.c', 'src/sim_picker.c', 'src/sim_record.c', - 'src/repl.c', 'src/repl_fmt.c', 'src/repl_break.c', 'src/repl_info.c', - 'src/repl_params.c', 'src/repl_rollup.c'], + ['src/sim.c', 'src/sim_loop.c', 'src/sim_picker.c', 'src/sim_limit.c', + 'src/sim_record.c', 'src/repl.c', 'src/repl_fmt.c', 'src/repl_break.c', + 'src/repl_info.c', 'src/repl_params.c', 'src/repl_rollup.c'], include_directories: incdir, dependencies: scheduler_deps, install: false) diff --git a/src/scheduler/src/plugins/limit.c b/src/scheduler/src/plugins/limit.c index 0c48b97a..a283495a 100644 --- a/src/scheduler/src/plugins/limit.c +++ b/src/scheduler/src/plugins/limit.c @@ -46,24 +46,34 @@ static int limit_priv_data_init(limit_plugin_data_t* data) return 0; } -static int limit_update_config(xce_sched_plugin_t* plugin, - xce_conf4sched_t* config) +/* + * limit_seed_config_impl - shared seed-loop body behind both + * limit_update_config() (normal-mode config_hook, below) and + * limit_seed_config() (bug-1825 remediation round 2, near + * limit_new_plugin() below): builds and registers one xce_limit_t per + * config->limits[] entry against `core`'s real group managers. + * Parameterized on an honestly-typed xce_sched_core_t* -- neither caller + * derives `core` from a plugin->sched cast inside this function; each + * caller resolves its own `core` argument in whatever way is safe for its + * own context (see each caller's doc comment). + */ +static int limit_seed_config_impl(xce_sched_plugin_t* plugin, + xce_sched_core_t* core, + xce_conf4sched_t* config) { limit_plugin_data_t* privdata = NULL; - xce_scheduler_t* sched = (xce_scheduler_t*)plugin->sched; int i; xce_limit_config_t** limits = config->limits; int nlimits = config->nlimits; - TRACE("%s ...", __func__); privdata = (limit_plugin_data_t*)plugin->data; for (i = 0; i < nlimits; i++) { int rc; xce_limit_t* limit; - rc = build_limit(&limit, (sched->core.ugmgr), (sched->core.qgmgr), - (sched->core.pgmgr), (sched->core.hgmgr), limits[i]); + rc = build_limit(&limit, (core->ugmgr), (core->qgmgr), + (core->pgmgr), (core->hgmgr), limits[i]); if (rc < 0) { return rc; } @@ -114,6 +124,16 @@ static int limit_update_config(xce_sched_plugin_t* plugin, return 0; } +static int limit_update_config(xce_sched_plugin_t* plugin, + xce_conf4sched_t* config) +{ + xce_scheduler_t* sched = (xce_scheduler_t*)plugin->sched; + + TRACE("%s ...", __func__); + + return limit_seed_config_impl(plugin, &(sched->core), config); +} + typedef struct { xce_sched_plugin_t* plugin; xce_sched_ctx_t* ctx; @@ -606,9 +626,23 @@ var limit_get_usage_instances(xce_sched_core_t* core) return privdata->instances; } -int register_limit(void* sched) +/* + * limit_new_plugin - factory half of register_limit()'s template-build + * step, extracted so a caller with no xce_scheduler_t* (e.g. the + * simulation driver, via plugin_register_core() -- see sim_limit.c) can + * obtain a freshly calloc'd, fully populated `xce_sched_plugin_t` for the + * limit plugin -- same hooks/init_fn/fini_fn template register_limit() + * itself uses, just not yet registered anywhere. Mirrors + * builtin_fcfs_new_plugin()'s (plugins/builtin_fcfs.c) identical + * extraction exactly (scheduler-sim-diagnostics spec, task 6; bug-1825). + * Returns NULL on allocation failure. Caller owns the returned plugin + * (frees it only on a subsequent registration failure -- once registered, + * the plugin registry owns it, same lifetime rule every other plugin + * category in this codebase already follows). + */ +xce_sched_plugin_t* limit_new_plugin(void) { - static xce_sched_plugin_t limit_plugin = { + static xce_sched_plugin_t limit_plugin_template = { .name = "xce_sched_limit", .hooks = { .job_election_hook = (plugin_job_election_hook_t)limit_job_election, @@ -622,11 +656,63 @@ int register_limit(void* sched) xce_sched_plugin_t* plugin = (xce_sched_plugin_t*)calloc(1, sizeof(*plugin)); + + if (plugin == NULL) { + return NULL; + } + + memcpy(plugin, &limit_plugin_template, sizeof(*plugin)); + + return plugin; +} + +/* + * limit_seed_config - bug-1825 remediation round 2: honestly-typed + * config-seed entry point for a caller that holds a real xce_sched_core_t* + * but NOT a real xce_scheduler_t* -- the simulation driver + * (sim_limit.c's sim_bootstrap_limit_plugin()), whose + * xce_sched_sim_driver_t (sim.h) embeds a bare xce_sched_core_t core as + * its FIRST field, not a full xce_scheduler_t. + * + * This exists instead of routing the simulation driver through + * limit_update_config()/plugin->hooks.config_hook via a + * `plugin->sched = (void*)sim` type-pun (the round-1 approach, since + * removed) because that pun reproduces a cross-cast bug this codebase has + * already made once, for real: core.h's own xce_core_cache_clear_cb_t doc + * comment documents alloc_cache.c's cache_clear(xce_scheduler_t*) once + * being invoked unconditionally against BOTH drivers via this identical + * pointer-to-first-member idiom, and in sim mode silently reinterpreting + * xce_sched_sim_driver_t's `alt_events_file` (a live FILE*) as + * `unsatisfiable_cache` (a kvtab_t*) -- corrupting it. That bug's own fix + * was a structurally-typed per-core hook defaulting to NULL in sim mode, + * not a narrower comment scoping a still-present cast. This function + * applies the same discipline: `core` is a REAL field both + * xce_scheduler_t and xce_sched_sim_driver_t genuinely have (no cast + * needed to obtain it), so nothing this function or limit_seed_config_impl() + * reads can ever reach past xce_sched_core_t's own bounds into whatever + * xce_sched_sim_driver_t field happens to follow `core` in memory -- + * unlike a plugin->sched cast, which compiles and "works" regardless of + * the target's true layout until a future edit reads one field too many. + * + * plugin->sched is never read or written by this function. Delegates to + * the same limit_seed_config_impl() helper limit_update_config() itself + * uses, so the two entry points can never silently diverge in behavior. + */ +int limit_seed_config(xce_sched_plugin_t* plugin, xce_sched_core_t* core, + xce_conf4sched_t* config) +{ + TRACE("%s ...", __func__); + + return limit_seed_config_impl(plugin, core, config); +} + +int register_limit(void* sched) +{ + xce_sched_plugin_t* plugin = limit_new_plugin(); + if (plugin == NULL) { return -ENOMEM; } - memcpy(plugin, &limit_plugin, sizeof(*plugin)); - return plugin_register(sched, plugin); } diff --git a/src/scheduler/src/sim_limit.c b/src/scheduler/src/sim_limit.c new file mode 100644 index 00000000..b8a44c78 --- /dev/null +++ b/src/scheduler/src/sim_limit.c @@ -0,0 +1,143 @@ +#include + +#include "plugin.h" +#include "sim.h" +#include "utils.h" + +/* + * sim_limit.c - simulation driver limit-plugin bootstrap + * (scheduler-sim-diagnostics spec, task 6; bug-1825 fix). Split out of + * sim_loop.c rather than folded into it, for the exact same reason its + * sibling sim_picker.c already is a separate file: sim_loop.c's own file + * header comment states it has "zero line-budget headroom" (this + * project's 800-line-per-file convention, src/scheduler/CLAUDE.md's + * "Files stay under the repo's 800-line cap") -- so new bootstrap logic + * goes in its own sibling file rather than growing sim_loop.c past its + * cap, the same "new sibling file when the owning file hits its cap" + * precedent this family has already established repeatedly (sim_loop.c + * itself, sim_record.c, repl.c, repl_break.c/repl_info.c, repl_params.c, + * and sim_picker.c). + * + * bug-1825: the limit plugin's normal-mode registration entry point, + * register_limit() (plugins/limit.c), has exactly one call site in the + * whole codebase -- normal-mode-only sched_register_plugins() + * (scheduler.c). The simulation driver's own plugin bootstrap + * (sim_bootstrap_picker(), sim_picker.c) only ever registers the picker + * plugin, so the limit plugin is never registered in sim mode at all -- + * the REPL's `info limit` command (repl_info.c) is fully correct but + * permanently inert against a real replay, since + * limit_get_configured_limits() (plugins/limit.c) finds no + * "xce_sched_limit" entry in core->plugins to return anything from. + * + * sim_locate_and_load_snapshot() (sim.c) already loads the ORIGINAL live + * session's full config into sim->core.config (a borrowed reference -- + * xce_sched_core_load_snapshot()'s own documented choice, referenced in + * sim.c's comment right after that call) before this function's caller + * (sim_loop.c) ever runs -- so whatever limit configuration + * (config->limits/config->nlimits) the original session had is already + * sitting there, just never handed to a limit plugin because none is ever + * registered. sim_bootstrap_limit_plugin() below fixes both halves: + * register the plugin against the simulation core (mirroring + * sim_bootstrap_picker()'s own plugin_register_core() call exactly), then + * -- only if a config is actually present -- seed it directly via the + * plugin's own config_hook, deliberately bypassing plugin_config_update() + * (plugin.c): that function casts its void* arg to xce_scheduler_t* and + * reads sched->core.plugins, which would misinterpret + * xce_sched_sim_driver_t's memory layout (it embeds a bare + * xce_sched_core_t core as its FIRST field, not a full xce_scheduler_t) -- + * calling it here would be a type-unsafe read of whatever field happens + * to immediately follow `core` in xce_sched_sim_driver_t (sim.h). + * + * Explicit non-goal (tracked as its own, separate, larger follow-up bug): + * this does NOT wire limit *matching* (limit_job_election(), + * plugins/limit.c) into the sim/replay decision path -- only the + * configured-setting display becomes real here. Live usage display still + * correctly shows "no active usage instance yet" against a real replay + * even after this fix, since no job is ever matched against the limit in + * sim mode. + * + * plugin->sched wrinkle, and why this does NOT type-pun around it (bug-1825 + * remediation round 2 -- found via a real ASAN SEGV while TDD-ing the + * original fix, then rejected on independent review): limit_update_config() + * -- the limit plugin's config_hook, unlike builtin_fcfs's picker hooks -- + * internally casts plugin->sched to xce_scheduler_t* and dereferences + * sched->core.ugmgr/qgmgr/pgmgr/hgmgr (plugins/limit.c) to resolve each + * configured limit's consumer groups (build_limit()). plugin_register_core() + * deliberately leaves plugin->sched NULL (plugin.h's own doc comment on + * that function), because the picker category it was built for + * (sim_bootstrap_picker(), sim_picker.c) never dereferences plugin->sched + * in any hook -- the limit plugin's config_hook does, so calling it here + * via plugin->hooks.config_hook with plugin->sched still NULL segfaults on + * sched->core.ugmgr. + * + * The round-1 fix pointed plugin->sched at `sim` itself before invoking the + * hook, via the "pointer-to-first-member idiom" (xce_scheduler_t's own + * first field is also `xce_sched_core_t core`, at the same offset + * xce_sched_sim_driver_t's `core` sits at). Independent review rejected + * this: it is the EXACT SAME cross-cast idiom that already caused a real, + * documented production bug in this codebase -- core.h's own + * xce_core_cache_clear_cb_t doc comment describes alloc_cache.c's + * cache_clear(xce_scheduler_t*) once being called unconditionally from + * both drivers via this identical pointer-to-first-member trick, and in + * sim mode silently reinterpreting xce_sched_sim_driver_t's + * `alt_events_file` (a live FILE*) as `unsatisfiable_cache` (a kvtab_t*), + * corrupting it. That bug's own accepted fix was a structural, + * per-core-instance function-pointer hook defaulting to NULL in sim mode + * (xce_core_set_cache_clear_cb()) -- not "confine the cast by manual + * code-reading and a comment," because nothing stops a future edit to + * limit_update_config() from reading plugin->sched past ->core (e.g. + * ->apictx) and silently corrupting memory beyond the real + * xce_sched_sim_driver_t allocation, exactly as the cache_clear() bug did. + * + * Fix here follows that same precedent instead of repeating the type-pun: + * plugins/limit.c exposes limit_seed_config(xce_sched_plugin_t*, + * xce_sched_core_t* core, xce_conf4sched_t*) -- an honestly-typed entry + * point parameterized directly on `core`, the REAL field both + * xce_scheduler_t and xce_sched_sim_driver_t genuinely have (no cast + * needed), sharing its seed logic with limit_update_config() via a common + * static helper so the two paths cannot silently diverge. Called below + * with &(sim->core) -- plugin->sched is never written or read anywhere in + * this file. + */ + +/* Consumed by sim_loop.c via a plain `extern` declared there -- same + * "extern declared where consumed" idiom this file family already uses + * throughout (sim_picker.c's own file header comment). */ +void sim_bootstrap_limit_plugin(xce_sched_sim_driver_t* sim) +{ + xce_sched_plugin_t* plugin; + + plugin = limit_new_plugin(); + if (plugin == NULL) { + ERROR("%s out of memory building the limit plugin instance", __func__); + return; + } + + if (plugin_register_core(&(sim->core), plugin) < 0) { + ERROR("%s failed to register the limit plugin against the " + "simulation core", + __func__); + free(plugin); + return; + } + + /* bug-1825: seed the freshly registered plugin with whatever limit + * configuration the replay's snapshot already carries in + * sim->core.config (set earlier by sched_sim_driver_start()'s + * snapshot load) -- via limit_seed_config() (plugins/limit.c), NOT + * plugin_config_update() (see this file's header comment for why that + * function is unsafe to call here) and NOT plugin->hooks.config_hook + * via a plugin->sched type-pun (see this file's header comment, + * "plugin->sched wrinkle", for why that round-1 approach was rejected + * on review and replaced with this honestly-typed call). Skipped + * silently when no config is present: the normal case for a replay + * whose snapshot carries no config, or for a test fixture that + * doesn't set one up -- not an error. */ + if (sim->core.config != NULL) { + /* &(sim->core) is a REAL xce_sched_core_t* -- no cast, and + * plugin->sched is never touched. See limit_seed_config()'s own + * doc comment (plugins/limit.c) for why this is safe where the + * type-punned alternative was not. */ + limit_seed_config(plugin, &(sim->core), sim->core.config); + } +} diff --git a/src/scheduler/src/sim_loop.c b/src/scheduler/src/sim_loop.c index 13874d76..efe665d4 100644 --- a/src/scheduler/src/sim_loop.c +++ b/src/scheduler/src/sim_loop.c @@ -407,6 +407,16 @@ static int sim_process_replay_event_message(xce_sched_sim_driver_t* sim, * trace. */ extern void sim_bootstrap_picker(xce_sched_sim_driver_t* sim); +/* sim_bootstrap_limit_plugin - scheduler-sim-diagnostics spec, task 6 + * (bug-1825 fix): registers the limit plugin against the simulation core + * and seeds it from sim->core.config, if present, so `info limit` shows + * real data against a replay. Moved to sim_limit.c (this file's own "zero + * line-budget headroom", see this file's header comment) -- extern'd here + * per this file family's established "declared where consumed" idiom, + * same as sim_bootstrap_picker() immediately above. See sim_limit.c's own + * file header comment for the full mechanism/bug trace. */ +extern void sim_bootstrap_limit_plugin(xce_sched_sim_driver_t* sim); + /* * sim_print_decision - Req 5 AC 19's default: "If --sim-output is not * specified, decisions are printed to stdout only." This task (11.2) owns @@ -940,6 +950,7 @@ int sched_sim_driver_run(xce_sched_sim_driver_t* sim, xce_repl_pre_replay_pause(sim); sim_bootstrap_picker(sim); + sim_bootstrap_limit_plugin(sim); /* Req 5 AC 19 (task 11.5): the first two --sim-output messages, written * exactly once, before the event loop below runs -- see sim_record.c's diff --git a/src/scheduler/testsuite/test_maxtasks_wiring_sim_driver_parity.c b/src/scheduler/testsuite/test_maxtasks_wiring_sim_driver_parity.c index 03e01c44..6dfa77cb 100644 --- a/src/scheduler/testsuite/test_maxtasks_wiring_sim_driver_parity.c +++ b/src/scheduler/testsuite/test_maxtasks_wiring_sim_driver_parity.c @@ -148,6 +148,7 @@ #include "../src/sim.c" #include "../src/sim_loop.c" #include "../src/sim_picker.c" +#include "../src/sim_limit.c" /* bug-1825: sched_sim_driver_run() now also calls sim_bootstrap_limit_plugin() (sim_loop.c-cap sibling, sim_picker.c's own bug-1567 precedent) as an extern -- undefined reference at link time otherwise */ #include "../src/sim_record.c" #include "../src/sim_verify.c" #include "../src/snapshot.c" @@ -193,6 +194,41 @@ var limit_get_usage_instances(xce_sched_core_t* core) return NULL; } +/* scheduler-sim-diagnostics spec, task 6 (bug-1825): sim_loop.c's + * sched_sim_driver_run() now also calls sim_bootstrap_limit_plugin() + * (sim_limit.c, pulled into this TU via the #include above), which in + * turn calls limit_new_plugin() (plugins/limit.c, not part of this TU) -- + * identical link-satisfying stub rationale as limit_get_configured_ + * limits()/limit_get_usage_instances() immediately above. Returning NULL + * here is also the behaviorally correct answer, not just a link + * placeholder: sim_bootstrap_limit_plugin() already handles a NULL + * limit_new_plugin() return gracefully (logs an ERROR, registers nothing, + * returns) -- the exact same "out of memory" path sim_bootstrap_picker() + * (sim_picker.c) takes for a NULL builtin_fcfs_new_plugin() -- so this + * file's fixtures simply end up with no limit plugin registered, same + * observable state as before this task. */ +xce_sched_plugin_t* limit_new_plugin(void) +{ + return NULL; +} + +/* limit_seed_config() (plugins/limit.c, not part of this TU) -- bug-1825 + * remediation round 2's new honestly-typed config-seed entry point. + * sim_bootstrap_limit_plugin() (sim_limit.c) only calls it after + * plugin_register_core() has already succeeded against a real, non-NULL + * limit_new_plugin() plugin -- unreachable in this file since the + * limit_new_plugin() stub immediately above always returns NULL, but the + * symbol must still resolve at link time. Same link-satisfying-stub + * rationale as limit_new_plugin() immediately above. */ +int limit_seed_config(xce_sched_plugin_t* plugin, xce_sched_core_t* core, + xce_conf4sched_t* config) +{ + (void)plugin; + (void)core; + (void)config; + return 0; +} + int sched_reconcile_job_resource_usage(xce_sched_core_t* c, xce_job_account_t* a, xce_job_rusages_t* r) diff --git a/src/scheduler/testsuite/test_repl_breakpoints_inspection.c b/src/scheduler/testsuite/test_repl_breakpoints_inspection.c index 70ec4750..48908615 100644 --- a/src/scheduler/testsuite/test_repl_breakpoints_inspection.c +++ b/src/scheduler/testsuite/test_repl_breakpoints_inspection.c @@ -88,6 +88,7 @@ #include "../src/sim.c" #include "../src/sim_loop.c" #include "../src/sim_picker.c" /* bug-1567: sched_sim_driver_run() now calls sim_bootstrap_picker() (sim_loop.c-cap sibling) as an extern -- undefined reference at link time otherwise */ +#include "../src/sim_limit.c" /* bug-1825: sched_sim_driver_run() now also calls sim_bootstrap_limit_plugin() (sim_loop.c-cap sibling, sim_picker.c's own bug-1567 precedent) as an extern -- undefined reference at link time otherwise */ #include "../src/sim_record.c" #include "../src/sim_verify.c" #include "../src/snapshot.c" @@ -129,6 +130,41 @@ var limit_get_usage_instances(xce_sched_core_t* core) return NULL; } +/* scheduler-sim-diagnostics spec, task 6 (bug-1825): sim_loop.c's + * sched_sim_driver_run() now also calls sim_bootstrap_limit_plugin() + * (sim_limit.c, pulled into this TU via the #include above), which in + * turn calls limit_new_plugin() (plugins/limit.c, not part of this TU) -- + * identical link-satisfying stub rationale as limit_get_configured_ + * limits()/limit_get_usage_instances() immediately above. Returning NULL + * here is also the behaviorally correct answer, not just a link + * placeholder: sim_bootstrap_limit_plugin() already handles a NULL + * limit_new_plugin() return gracefully (logs an ERROR, registers nothing, + * returns) -- the exact same "out of memory" path sim_bootstrap_picker() + * (sim_picker.c) takes for a NULL builtin_fcfs_new_plugin() -- so this + * file's fixtures simply end up with no limit plugin registered, same + * observable state as before this task. */ +xce_sched_plugin_t* limit_new_plugin(void) +{ + return NULL; +} + +/* limit_seed_config() (plugins/limit.c, not part of this TU) -- bug-1825 + * remediation round 2's new honestly-typed config-seed entry point. + * sim_bootstrap_limit_plugin() (sim_limit.c) only calls it after + * plugin_register_core() has already succeeded against a real, non-NULL + * limit_new_plugin() plugin -- unreachable in this file since the + * limit_new_plugin() stub immediately above always returns NULL, but the + * symbol must still resolve at link time. Same link-satisfying-stub + * rationale as limit_new_plugin() immediately above. */ +int limit_seed_config(xce_sched_plugin_t* plugin, xce_sched_core_t* core, + xce_conf4sched_t* config) +{ + (void)plugin; + (void)core; + (void)config; + return 0; +} + /* resource-accounting.md's reconciliation (resource.c, not linked here -- * this file only #includes core_events.c). core_events.c's * XCE_CORE_EVENT_JOB_RUSAGE handler calls this whenever job->accounts != diff --git a/src/scheduler/testsuite/test_repl_equivalence.c b/src/scheduler/testsuite/test_repl_equivalence.c index 0f390f9e..4167cbbd 100644 --- a/src/scheduler/testsuite/test_repl_equivalence.c +++ b/src/scheduler/testsuite/test_repl_equivalence.c @@ -137,6 +137,7 @@ #include "../src/sim.c" #include "../src/sim_loop.c" #include "../src/sim_picker.c" /* bug-1567: sched_sim_driver_run() now calls sim_bootstrap_picker() (sim_loop.c-cap sibling) as an extern -- undefined reference at link time otherwise */ +#include "../src/sim_limit.c" /* bug-1825: sched_sim_driver_run() now also calls sim_bootstrap_limit_plugin() (sim_loop.c-cap sibling, sim_picker.c's own bug-1567 precedent) as an extern -- undefined reference at link time otherwise */ #include "../src/sim_record.c" #include "../src/sim_verify.c" #include "../src/snapshot.c" @@ -178,6 +179,41 @@ var limit_get_usage_instances(xce_sched_core_t* core) return NULL; } +/* scheduler-sim-diagnostics spec, task 6 (bug-1825): sim_loop.c's + * sched_sim_driver_run() now also calls sim_bootstrap_limit_plugin() + * (sim_limit.c, pulled into this TU via the #include above), which in + * turn calls limit_new_plugin() (plugins/limit.c, not part of this TU) -- + * identical link-satisfying stub rationale as limit_get_configured_ + * limits()/limit_get_usage_instances() immediately above. Returning NULL + * here is also the behaviorally correct answer, not just a link + * placeholder: sim_bootstrap_limit_plugin() already handles a NULL + * limit_new_plugin() return gracefully (logs an ERROR, registers nothing, + * returns) -- the exact same "out of memory" path sim_bootstrap_picker() + * (sim_picker.c) takes for a NULL builtin_fcfs_new_plugin() -- so this + * file's fixtures simply end up with no limit plugin registered, same + * observable state as before this task. */ +xce_sched_plugin_t* limit_new_plugin(void) +{ + return NULL; +} + +/* limit_seed_config() (plugins/limit.c, not part of this TU) -- bug-1825 + * remediation round 2's new honestly-typed config-seed entry point. + * sim_bootstrap_limit_plugin() (sim_limit.c) only calls it after + * plugin_register_core() has already succeeded against a real, non-NULL + * limit_new_plugin() plugin -- unreachable in this file since the + * limit_new_plugin() stub immediately above always returns NULL, but the + * symbol must still resolve at link time. Same link-satisfying-stub + * rationale as limit_new_plugin() immediately above. */ +int limit_seed_config(xce_sched_plugin_t* plugin, xce_sched_core_t* core, + xce_conf4sched_t* config) +{ + (void)plugin; + (void)core; + (void)config; + return 0; +} + /* resource-accounting.md's reconciliation (resource.c, not linked here -- * this file only #includes core_events.c). core_events.c's * XCE_CORE_EVENT_JOB_RUSAGE handler calls this whenever job->accounts != diff --git a/src/scheduler/testsuite/test_repl_event_echo.c b/src/scheduler/testsuite/test_repl_event_echo.c index f36bf997..cb005693 100644 --- a/src/scheduler/testsuite/test_repl_event_echo.c +++ b/src/scheduler/testsuite/test_repl_event_echo.c @@ -105,6 +105,7 @@ #include "../src/sim.c" #include "../src/sim_loop.c" #include "../src/sim_picker.c" /* bug-1567: sched_sim_driver_run() now calls sim_bootstrap_picker() (sim_loop.c-cap sibling) as an extern -- undefined reference at link time otherwise */ +#include "../src/sim_limit.c" /* bug-1825: sched_sim_driver_run() now also calls sim_bootstrap_limit_plugin() (sim_loop.c-cap sibling, sim_picker.c's own bug-1567 precedent) as an extern -- undefined reference at link time otherwise */ #include "../src/sim_record.c" #include "../src/sim_verify.c" #include "../src/snapshot.c" @@ -146,6 +147,41 @@ var limit_get_usage_instances(xce_sched_core_t* core) return NULL; } +/* scheduler-sim-diagnostics spec, task 6 (bug-1825): sim_loop.c's + * sched_sim_driver_run() now also calls sim_bootstrap_limit_plugin() + * (sim_limit.c, pulled into this TU via the #include above), which in + * turn calls limit_new_plugin() (plugins/limit.c, not part of this TU) -- + * identical link-satisfying stub rationale as limit_get_configured_ + * limits()/limit_get_usage_instances() immediately above. Returning NULL + * here is also the behaviorally correct answer, not just a link + * placeholder: sim_bootstrap_limit_plugin() already handles a NULL + * limit_new_plugin() return gracefully (logs an ERROR, registers nothing, + * returns) -- the exact same "out of memory" path sim_bootstrap_picker() + * (sim_picker.c) takes for a NULL builtin_fcfs_new_plugin() -- so this + * file's fixtures simply end up with no limit plugin registered, same + * observable state as before this task. */ +xce_sched_plugin_t* limit_new_plugin(void) +{ + return NULL; +} + +/* limit_seed_config() (plugins/limit.c, not part of this TU) -- bug-1825 + * remediation round 2's new honestly-typed config-seed entry point. + * sim_bootstrap_limit_plugin() (sim_limit.c) only calls it after + * plugin_register_core() has already succeeded against a real, non-NULL + * limit_new_plugin() plugin -- unreachable in this file since the + * limit_new_plugin() stub immediately above always returns NULL, but the + * symbol must still resolve at link time. Same link-satisfying-stub + * rationale as limit_new_plugin() immediately above. */ +int limit_seed_config(xce_sched_plugin_t* plugin, xce_sched_core_t* core, + xce_conf4sched_t* config) +{ + (void)plugin; + (void)core; + (void)config; + return 0; +} + /* resource-accounting.md's reconciliation (resource.c, not linked here -- * this file only #includes core_events.c). core_events.c's * XCE_CORE_EVENT_JOB_RUSAGE handler calls this whenever job->accounts != diff --git a/src/scheduler/testsuite/test_repl_execution_control.c b/src/scheduler/testsuite/test_repl_execution_control.c index 659d73a9..cc198868 100644 --- a/src/scheduler/testsuite/test_repl_execution_control.c +++ b/src/scheduler/testsuite/test_repl_execution_control.c @@ -102,6 +102,7 @@ #include "../src/sim.c" #include "../src/sim_loop.c" #include "../src/sim_picker.c" /* bug-1567: sched_sim_driver_run() now calls sim_bootstrap_picker() (sim_loop.c-cap sibling) as an extern -- undefined reference at link time otherwise */ +#include "../src/sim_limit.c" /* bug-1825: sched_sim_driver_run() now also calls sim_bootstrap_limit_plugin() (sim_loop.c-cap sibling, sim_picker.c's own bug-1567 precedent) as an extern -- undefined reference at link time otherwise */ #include "../src/sim_record.c" #include "../src/sim_verify.c" #include "../src/snapshot.c" @@ -143,6 +144,41 @@ var limit_get_usage_instances(xce_sched_core_t* core) return NULL; } +/* scheduler-sim-diagnostics spec, task 6 (bug-1825): sim_loop.c's + * sched_sim_driver_run() now also calls sim_bootstrap_limit_plugin() + * (sim_limit.c, pulled into this TU via the #include above), which in + * turn calls limit_new_plugin() (plugins/limit.c, not part of this TU) -- + * identical link-satisfying stub rationale as limit_get_configured_ + * limits()/limit_get_usage_instances() immediately above. Returning NULL + * here is also the behaviorally correct answer, not just a link + * placeholder: sim_bootstrap_limit_plugin() already handles a NULL + * limit_new_plugin() return gracefully (logs an ERROR, registers nothing, + * returns) -- the exact same "out of memory" path sim_bootstrap_picker() + * (sim_picker.c) takes for a NULL builtin_fcfs_new_plugin() -- so this + * file's fixtures simply end up with no limit plugin registered, same + * observable state as before this task. */ +xce_sched_plugin_t* limit_new_plugin(void) +{ + return NULL; +} + +/* limit_seed_config() (plugins/limit.c, not part of this TU) -- bug-1825 + * remediation round 2's new honestly-typed config-seed entry point. + * sim_bootstrap_limit_plugin() (sim_limit.c) only calls it after + * plugin_register_core() has already succeeded against a real, non-NULL + * limit_new_plugin() plugin -- unreachable in this file since the + * limit_new_plugin() stub immediately above always returns NULL, but the + * symbol must still resolve at link time. Same link-satisfying-stub + * rationale as limit_new_plugin() immediately above. */ +int limit_seed_config(xce_sched_plugin_t* plugin, xce_sched_core_t* core, + xce_conf4sched_t* config) +{ + (void)plugin; + (void)core; + (void)config; + return 0; +} + /* resource-accounting.md's reconciliation (resource.c, not linked here -- * this file only #includes core_events.c). core_events.c's * XCE_CORE_EVENT_JOB_RUSAGE handler calls this whenever job->accounts != diff --git a/src/scheduler/testsuite/test_repl_help.c b/src/scheduler/testsuite/test_repl_help.c index 13f29ae3..c997aa61 100644 --- a/src/scheduler/testsuite/test_repl_help.c +++ b/src/scheduler/testsuite/test_repl_help.c @@ -97,6 +97,7 @@ #include "../src/sim.c" #include "../src/sim_loop.c" #include "../src/sim_picker.c" /* bug-1567: sched_sim_driver_run() now calls sim_bootstrap_picker() (sim_loop.c-cap sibling) as an extern -- undefined reference at link time otherwise */ +#include "../src/sim_limit.c" /* bug-1825: sched_sim_driver_run() now also calls sim_bootstrap_limit_plugin() (sim_loop.c-cap sibling, sim_picker.c's own bug-1567 precedent) as an extern -- undefined reference at link time otherwise */ #include "../src/sim_record.c" #include "../src/sim_verify.c" #include "../src/snapshot.c" @@ -138,6 +139,41 @@ var limit_get_usage_instances(xce_sched_core_t* core) return NULL; } +/* scheduler-sim-diagnostics spec, task 6 (bug-1825): sim_loop.c's + * sched_sim_driver_run() now also calls sim_bootstrap_limit_plugin() + * (sim_limit.c, pulled into this TU via the #include above), which in + * turn calls limit_new_plugin() (plugins/limit.c, not part of this TU) -- + * identical link-satisfying stub rationale as limit_get_configured_ + * limits()/limit_get_usage_instances() immediately above. Returning NULL + * here is also the behaviorally correct answer, not just a link + * placeholder: sim_bootstrap_limit_plugin() already handles a NULL + * limit_new_plugin() return gracefully (logs an ERROR, registers nothing, + * returns) -- the exact same "out of memory" path sim_bootstrap_picker() + * (sim_picker.c) takes for a NULL builtin_fcfs_new_plugin() -- so this + * file's fixtures simply end up with no limit plugin registered, same + * observable state as before this task. */ +xce_sched_plugin_t* limit_new_plugin(void) +{ + return NULL; +} + +/* limit_seed_config() (plugins/limit.c, not part of this TU) -- bug-1825 + * remediation round 2's new honestly-typed config-seed entry point. + * sim_bootstrap_limit_plugin() (sim_limit.c) only calls it after + * plugin_register_core() has already succeeded against a real, non-NULL + * limit_new_plugin() plugin -- unreachable in this file since the + * limit_new_plugin() stub immediately above always returns NULL, but the + * symbol must still resolve at link time. Same link-satisfying-stub + * rationale as limit_new_plugin() immediately above. */ +int limit_seed_config(xce_sched_plugin_t* plugin, xce_sched_core_t* core, + xce_conf4sched_t* config) +{ + (void)plugin; + (void)core; + (void)config; + return 0; +} + /* resource-accounting.md's reconciliation (resource.c, not linked here -- * this file only #includes core_events.c). core_events.c's * XCE_CORE_EVENT_JOB_RUSAGE handler calls this whenever job->accounts != diff --git a/src/scheduler/testsuite/test_repl_limit_table.c b/src/scheduler/testsuite/test_repl_limit_table.c index 14c22b80..b120c428 100644 --- a/src/scheduler/testsuite/test_repl_limit_table.c +++ b/src/scheduler/testsuite/test_repl_limit_table.c @@ -90,6 +90,7 @@ #include "../src/sim.c" #include "../src/sim_loop.c" #include "../src/sim_picker.c" /* bug-1567: sched_sim_driver_run() now calls sim_bootstrap_picker() (sim_loop.c-cap sibling) as an extern -- undefined reference at link time otherwise */ +#include "../src/sim_limit.c" /* bug-1825: sched_sim_driver_run() now calls sim_bootstrap_limit_plugin() (sim_loop.c-cap sibling) as an extern -- undefined reference at link time otherwise */ #include "../src/sim_record.c" #include "../src/sim_verify.c" #include "../src/snapshot.c" @@ -2395,6 +2396,180 @@ test_repl_step_continue_with_info_limit_interleaved_matches_baseline_decision_se "baseline_decision_sequence: PASS\n"); } +/* ---------------------------------------------------------------------- */ +/* scheduler-sim-diagnostics spec, task 6 (bug-1825 fix): sim mode never */ +/* registers the limit plugin at all, so `info limit`'s otherwise-correct */ +/* display logic (tested above via the hand-rolled register_limit_plugin() */ +/* fixture) is permanently inert against a REAL replay. These tests drive */ +/* the actual production bootstrap function, sim_bootstrap_limit_plugin() */ +/* (new src/sim_limit.c, sim_loop.c's sim_bootstrap_picker()-call-site */ +/* sibling), against a real xce_sched_core_init()-initialized core -- not */ +/* the register_limit_plugin()/fixture_register_plugin_in_core() shortcut */ +/* the rest of this file's tests use, since the whole point here is */ +/* proving the real bootstrap path (registration + config-seed) works, */ +/* not re-proving repl_info_limit()'s own display logic again. */ +/* ---------------------------------------------------------------------- */ + +/* + * (1) Registration: before sim_bootstrap_limit_plugin() runs, the limit + * plugin is not present in sim->core.plugins at all (limit_get_configured_ + * limits()'s own documented "plugin absent" NULL return). After it runs, + * the accessor resolves a real, iterable handle -- proving the function + * actually registered "xce_sched_limit" against the simulation core via + * plugin_register_core(), mirroring sim_bootstrap_picker()'s own + * registration call exactly. + */ +static void +test_sim_bootstrap_limit_plugin_registers_plugin_against_real_core(void) +{ + xce_sched_sim_driver_t sim; + var limits_handle; + int rc; + + memset(&sim, 0, sizeof(sim)); + rc = xce_sched_core_init(&(sim.core)); + TEST_ASSERT_EQUAL_INT(0, rc); + + limits_handle = limit_get_configured_limits(&(sim.core)); + TEST_ASSERT_NULL_MESSAGE( + limits_handle, + "the limit plugin must not be registered before " + "sim_bootstrap_limit_plugin() runs"); + + sim_bootstrap_limit_plugin(&sim); + + limits_handle = limit_get_configured_limits(&(sim.core)); + TEST_ASSERT_NOT_NULL_MESSAGE( + limits_handle, + "sim_bootstrap_limit_plugin() must register the limit plugin " + "against sim->core, matching sim_bootstrap_picker()'s own " + "plugin_register_core() pattern"); + + printf( + "test_sim_bootstrap_limit_plugin_registers_plugin_against_real_" + "core: PASS\n"); +} + +/* + * (2) THE observable criterion (task 6's own stated Observable): a real + * xce_sched_sim_driver_t whose core.config already carries a real + * xce_limit_config_t (as sim_locate_and_load_snapshot()/ + * xce_sched_core_load_snapshot() would have set it up from a replay's own + * snapshot, before sim_bootstrap_limit_plugin() ever runs -- reproduced + * here via direct construction per this task's own stated test-level + * guidance, no capnp/.replay file needed) ends up with + * limit_get_configured_limits() iterating to that SAME real limit by name + * once sim_bootstrap_limit_plugin() runs. This is the test that actually + * proves the config-seed step (limit_seed_config(), plugins/limit.c, + * called directly with a real xce_sched_core_t* -- bug-1825 remediation + * round 2 replaced the original plugin->sched-type-punned + * plugin->hooks.config_hook() call with this honestly-typed entry point; + * see sim_limit.c's own header comment for why), not just registration. + */ +static void +test_sim_bootstrap_limit_plugin_seeds_real_config_limit_when_config_present( + void) +{ + xce_sched_sim_driver_t sim; + xce_limit_config_t* limits[1]; + xce_conf4sched_t config; + var limits_handle; + iterator_t it; + xce_limit_t* found = NULL; + int rc; + + memset(&sim, 0, sizeof(sim)); + rc = xce_sched_core_init(&(sim.core)); + TEST_ASSERT_EQUAL_INT(0, rc); + + limits[0] = make_limit_config("sim_replay_limit"); + + memset(&config, 0, sizeof(config)); + config.nlimits = 1; + config.limits = limits; + + /* Mirrors sim_locate_and_load_snapshot()'s (sim.c) borrowed-reference + * assignment of sim->core.config from the replay's own snapshot -- + * this test only needs the END STATE that call produces, not the + * capnp/file machinery that gets there. */ + sim.core.config = &config; + + sim_bootstrap_limit_plugin(&sim); + + limits_handle = limit_get_configured_limits(&(sim.core)); + TEST_ASSERT_NOT_NULL(limits_handle); + + rc = iter_make(&it, limits_handle); + TEST_ASSERT_TRUE(rc >= 0); + + rc = iter_next(&it, (void**)&found); + TEST_ASSERT_TRUE(rc >= 0); + TEST_ASSERT_NOT_EQUAL(RTTI_ITER_NO_DATA, rc); + TEST_ASSERT_NOT_NULL(found); + TEST_ASSERT_EQUAL_STRING_MESSAGE( + "sim_replay_limit", found->name, + "sim_bootstrap_limit_plugin() must seed the plugin with the REAL " + "limit already sitting in sim->core.config, not merely register " + "an empty plugin"); + + rc = iter_next(&it, (void**)&found); + TEST_ASSERT_EQUAL_INT(RTTI_ITER_NO_DATA, rc); + + iter_destroy(&it); + + free_limit_config(limits[0]); + + printf( + "test_sim_bootstrap_limit_plugin_seeds_real_config_limit_when_" + "config_present: PASS\n"); +} + +/* + * (3) Graceful no-config case (task 6's explicit design constraint): a + * replay whose snapshot carries no config -- or a test fixture that never + * sets one up -- leaves sim->core.config NULL. sim_bootstrap_limit_plugin() + * must still register the plugin successfully (non-NULL, empty-iterating + * accessor handle) and must NOT crash attempting to seed a config that + * does not exist; this is the normal case, not an error. + */ +static void +test_sim_bootstrap_limit_plugin_registers_without_crash_when_config_null( + void) +{ + xce_sched_sim_driver_t sim; + var limits_handle; + iterator_t it; + void* item = NULL; + int rc; + + memset(&sim, 0, sizeof(sim)); + rc = xce_sched_core_init(&(sim.core)); + TEST_ASSERT_EQUAL_INT(0, rc); + + TEST_ASSERT_NULL(sim.core.config); + + sim_bootstrap_limit_plugin(&sim); + + limits_handle = limit_get_configured_limits(&(sim.core)); + TEST_ASSERT_NOT_NULL_MESSAGE( + limits_handle, + "the plugin must still register successfully when " + "sim->core.config is NULL -- the config-seed step must be " + "skipped silently, not treated as a registration failure"); + + rc = iter_make(&it, limits_handle); + TEST_ASSERT_TRUE(rc >= 0); + + rc = iter_next(&it, &item); + TEST_ASSERT_EQUAL_INT(RTTI_ITER_NO_DATA, rc); + + iter_destroy(&it); + + printf( + "test_sim_bootstrap_limit_plugin_registers_without_crash_when_" + "config_null: PASS\n"); +} + int main(void) { UNITY_BEGIN(); @@ -2443,5 +2618,11 @@ int main(void) test_info_limit_on_empty_registry_is_repeatable_and_never_registers_plugin); RUN_TEST( test_repl_step_continue_with_info_limit_interleaved_matches_baseline_decision_sequence); + RUN_TEST( + test_sim_bootstrap_limit_plugin_registers_plugin_against_real_core); + RUN_TEST( + test_sim_bootstrap_limit_plugin_seeds_real_config_limit_when_config_present); + RUN_TEST( + test_sim_bootstrap_limit_plugin_registers_without_crash_when_config_null); return UNITY_END(); } diff --git a/src/scheduler/testsuite/test_repl_prompt_context.c b/src/scheduler/testsuite/test_repl_prompt_context.c index b826ec7b..10a597a5 100644 --- a/src/scheduler/testsuite/test_repl_prompt_context.c +++ b/src/scheduler/testsuite/test_repl_prompt_context.c @@ -101,6 +101,7 @@ #include "../src/sim.c" #include "../src/sim_loop.c" #include "../src/sim_picker.c" +#include "../src/sim_limit.c" /* bug-1825: sched_sim_driver_run() now also calls sim_bootstrap_limit_plugin() (sim_loop.c-cap sibling, sim_picker.c's own bug-1567 precedent) as an extern -- undefined reference at link time otherwise */ #include "../src/sim_record.c" #include "../src/sim_verify.c" #include "../src/snapshot.c" @@ -142,6 +143,41 @@ var limit_get_usage_instances(xce_sched_core_t* core) return NULL; } +/* scheduler-sim-diagnostics spec, task 6 (bug-1825): sim_loop.c's + * sched_sim_driver_run() now also calls sim_bootstrap_limit_plugin() + * (sim_limit.c, pulled into this TU via the #include above), which in + * turn calls limit_new_plugin() (plugins/limit.c, not part of this TU) -- + * identical link-satisfying stub rationale as limit_get_configured_ + * limits()/limit_get_usage_instances() immediately above. Returning NULL + * here is also the behaviorally correct answer, not just a link + * placeholder: sim_bootstrap_limit_plugin() already handles a NULL + * limit_new_plugin() return gracefully (logs an ERROR, registers nothing, + * returns) -- the exact same "out of memory" path sim_bootstrap_picker() + * (sim_picker.c) takes for a NULL builtin_fcfs_new_plugin() -- so this + * file's fixtures simply end up with no limit plugin registered, same + * observable state as before this task. */ +xce_sched_plugin_t* limit_new_plugin(void) +{ + return NULL; +} + +/* limit_seed_config() (plugins/limit.c, not part of this TU) -- bug-1825 + * remediation round 2's new honestly-typed config-seed entry point. + * sim_bootstrap_limit_plugin() (sim_limit.c) only calls it after + * plugin_register_core() has already succeeded against a real, non-NULL + * limit_new_plugin() plugin -- unreachable in this file since the + * limit_new_plugin() stub immediately above always returns NULL, but the + * symbol must still resolve at link time. Same link-satisfying-stub + * rationale as limit_new_plugin() immediately above. */ +int limit_seed_config(xce_sched_plugin_t* plugin, xce_sched_core_t* core, + xce_conf4sched_t* config) +{ + (void)plugin; + (void)core; + (void)config; + return 0; +} + /* Link-satisfying stubs -- same rationale as every sibling in this file * family (see test_repl_execution_control.c's own copies for the full * per-stub doc comments). */ diff --git a/src/scheduler/testsuite/test_repl_resource_table.c b/src/scheduler/testsuite/test_repl_resource_table.c index d795a0d6..69a73852 100644 --- a/src/scheduler/testsuite/test_repl_resource_table.c +++ b/src/scheduler/testsuite/test_repl_resource_table.c @@ -68,6 +68,7 @@ #include "../src/sim.c" #include "../src/sim_loop.c" #include "../src/sim_picker.c" /* bug-1567: sched_sim_driver_run() now calls sim_bootstrap_picker() (sim_loop.c-cap sibling) as an extern -- undefined reference at link time otherwise */ +#include "../src/sim_limit.c" /* bug-1825: sched_sim_driver_run() now also calls sim_bootstrap_limit_plugin() (sim_loop.c-cap sibling, sim_picker.c's own bug-1567 precedent) as an extern -- undefined reference at link time otherwise */ #include "../src/sim_record.c" #include "../src/sim_verify.c" #include "../src/snapshot.c" @@ -109,6 +110,41 @@ var limit_get_usage_instances(xce_sched_core_t* core) return NULL; } +/* scheduler-sim-diagnostics spec, task 6 (bug-1825): sim_loop.c's + * sched_sim_driver_run() now also calls sim_bootstrap_limit_plugin() + * (sim_limit.c, pulled into this TU via the #include above), which in + * turn calls limit_new_plugin() (plugins/limit.c, not part of this TU) -- + * identical link-satisfying stub rationale as limit_get_configured_ + * limits()/limit_get_usage_instances() immediately above. Returning NULL + * here is also the behaviorally correct answer, not just a link + * placeholder: sim_bootstrap_limit_plugin() already handles a NULL + * limit_new_plugin() return gracefully (logs an ERROR, registers nothing, + * returns) -- the exact same "out of memory" path sim_bootstrap_picker() + * (sim_picker.c) takes for a NULL builtin_fcfs_new_plugin() -- so this + * file's fixtures simply end up with no limit plugin registered, same + * observable state as before this task. */ +xce_sched_plugin_t* limit_new_plugin(void) +{ + return NULL; +} + +/* limit_seed_config() (plugins/limit.c, not part of this TU) -- bug-1825 + * remediation round 2's new honestly-typed config-seed entry point. + * sim_bootstrap_limit_plugin() (sim_limit.c) only calls it after + * plugin_register_core() has already succeeded against a real, non-NULL + * limit_new_plugin() plugin -- unreachable in this file since the + * limit_new_plugin() stub immediately above always returns NULL, but the + * symbol must still resolve at link time. Same link-satisfying-stub + * rationale as limit_new_plugin() immediately above. */ +int limit_seed_config(xce_sched_plugin_t* plugin, xce_sched_core_t* core, + xce_conf4sched_t* config) +{ + (void)plugin; + (void)core; + (void)config; + return 0; +} + /* resource-accounting.md's reconciliation (resource.c, not linked here -- * this file only #includes core_events.c). core_events.c's * XCE_CORE_EVENT_JOB_RUSAGE handler calls this whenever job->accounts != diff --git a/src/scheduler/testsuite/test_repl_round_rollup.c b/src/scheduler/testsuite/test_repl_round_rollup.c index a00b7f61..48234b3b 100644 --- a/src/scheduler/testsuite/test_repl_round_rollup.c +++ b/src/scheduler/testsuite/test_repl_round_rollup.c @@ -64,6 +64,7 @@ #include "../src/sim.c" #include "../src/sim_loop.c" #include "../src/sim_picker.c" /* bug-1567: sched_sim_driver_run() now calls sim_bootstrap_picker() (sim_loop.c-cap sibling) as an extern -- undefined reference at link time otherwise */ +#include "../src/sim_limit.c" /* bug-1825: sched_sim_driver_run() now also calls sim_bootstrap_limit_plugin() (sim_loop.c-cap sibling, sim_picker.c's own bug-1567 precedent) as an extern -- undefined reference at link time otherwise */ #include "../src/sim_record.c" #include "../src/sim_verify.c" #include "../src/snapshot.c" @@ -105,6 +106,41 @@ var limit_get_usage_instances(xce_sched_core_t* core) return NULL; } +/* scheduler-sim-diagnostics spec, task 6 (bug-1825): sim_loop.c's + * sched_sim_driver_run() now also calls sim_bootstrap_limit_plugin() + * (sim_limit.c, pulled into this TU via the #include above), which in + * turn calls limit_new_plugin() (plugins/limit.c, not part of this TU) -- + * identical link-satisfying stub rationale as limit_get_configured_ + * limits()/limit_get_usage_instances() immediately above. Returning NULL + * here is also the behaviorally correct answer, not just a link + * placeholder: sim_bootstrap_limit_plugin() already handles a NULL + * limit_new_plugin() return gracefully (logs an ERROR, registers nothing, + * returns) -- the exact same "out of memory" path sim_bootstrap_picker() + * (sim_picker.c) takes for a NULL builtin_fcfs_new_plugin() -- so this + * file's fixtures simply end up with no limit plugin registered, same + * observable state as before this task. */ +xce_sched_plugin_t* limit_new_plugin(void) +{ + return NULL; +} + +/* limit_seed_config() (plugins/limit.c, not part of this TU) -- bug-1825 + * remediation round 2's new honestly-typed config-seed entry point. + * sim_bootstrap_limit_plugin() (sim_limit.c) only calls it after + * plugin_register_core() has already succeeded against a real, non-NULL + * limit_new_plugin() plugin -- unreachable in this file since the + * limit_new_plugin() stub immediately above always returns NULL, but the + * symbol must still resolve at link time. Same link-satisfying-stub + * rationale as limit_new_plugin() immediately above. */ +int limit_seed_config(xce_sched_plugin_t* plugin, xce_sched_core_t* core, + xce_conf4sched_t* config) +{ + (void)plugin; + (void)core; + (void)config; + return 0; +} + /* resource-accounting.md's reconciliation (resource.c, not linked here -- * this file only #includes core_events.c). core_events.c's * XCE_CORE_EVENT_JOB_RUSAGE handler calls this whenever job->accounts != diff --git a/src/scheduler/testsuite/test_repl_snapshot_params.c b/src/scheduler/testsuite/test_repl_snapshot_params.c index cde6b978..d44c512a 100644 --- a/src/scheduler/testsuite/test_repl_snapshot_params.c +++ b/src/scheduler/testsuite/test_repl_snapshot_params.c @@ -77,6 +77,7 @@ #include "../src/sim.c" #include "../src/sim_loop.c" #include "../src/sim_picker.c" /* bug-1567: sched_sim_driver_run() now calls sim_bootstrap_picker() (sim_loop.c-cap sibling) as an extern -- undefined reference at link time otherwise */ +#include "../src/sim_limit.c" /* bug-1825: sched_sim_driver_run() now also calls sim_bootstrap_limit_plugin() (sim_loop.c-cap sibling, sim_picker.c's own bug-1567 precedent) as an extern -- undefined reference at link time otherwise */ #include "../src/sim_record.c" #include "../src/sim_verify.c" #include "../src/snapshot.c" @@ -118,6 +119,41 @@ var limit_get_usage_instances(xce_sched_core_t* core) return NULL; } +/* scheduler-sim-diagnostics spec, task 6 (bug-1825): sim_loop.c's + * sched_sim_driver_run() now also calls sim_bootstrap_limit_plugin() + * (sim_limit.c, pulled into this TU via the #include above), which in + * turn calls limit_new_plugin() (plugins/limit.c, not part of this TU) -- + * identical link-satisfying stub rationale as limit_get_configured_ + * limits()/limit_get_usage_instances() immediately above. Returning NULL + * here is also the behaviorally correct answer, not just a link + * placeholder: sim_bootstrap_limit_plugin() already handles a NULL + * limit_new_plugin() return gracefully (logs an ERROR, registers nothing, + * returns) -- the exact same "out of memory" path sim_bootstrap_picker() + * (sim_picker.c) takes for a NULL builtin_fcfs_new_plugin() -- so this + * file's fixtures simply end up with no limit plugin registered, same + * observable state as before this task. */ +xce_sched_plugin_t* limit_new_plugin(void) +{ + return NULL; +} + +/* limit_seed_config() (plugins/limit.c, not part of this TU) -- bug-1825 + * remediation round 2's new honestly-typed config-seed entry point. + * sim_bootstrap_limit_plugin() (sim_limit.c) only calls it after + * plugin_register_core() has already succeeded against a real, non-NULL + * limit_new_plugin() plugin -- unreachable in this file since the + * limit_new_plugin() stub immediately above always returns NULL, but the + * symbol must still resolve at link time. Same link-satisfying-stub + * rationale as limit_new_plugin() immediately above. */ +int limit_seed_config(xce_sched_plugin_t* plugin, xce_sched_core_t* core, + xce_conf4sched_t* config) +{ + (void)plugin; + (void)core; + (void)config; + return 0; +} + /* resource-accounting.md's reconciliation (resource.c, not linked here -- * this file only #includes core_events.c). core_events.c's * XCE_CORE_EVENT_JOB_RUSAGE handler calls this whenever job->accounts != diff --git a/src/scheduler/testsuite/test_repl_step_verbosity.c b/src/scheduler/testsuite/test_repl_step_verbosity.c index e7bfed7c..8ade7f80 100644 --- a/src/scheduler/testsuite/test_repl_step_verbosity.c +++ b/src/scheduler/testsuite/test_repl_step_verbosity.c @@ -118,6 +118,7 @@ #include "../src/sim.c" #include "../src/sim_loop.c" #include "../src/sim_picker.c" /* bug-1567: sched_sim_driver_run() now calls sim_bootstrap_picker() (sim_loop.c-cap sibling) as an extern -- undefined reference at link time otherwise */ +#include "../src/sim_limit.c" /* bug-1825: sched_sim_driver_run() now also calls sim_bootstrap_limit_plugin() (sim_loop.c-cap sibling, sim_picker.c's own bug-1567 precedent) as an extern -- undefined reference at link time otherwise */ #include "../src/sim_record.c" #include "../src/sim_verify.c" #include "../src/snapshot.c" @@ -159,6 +160,41 @@ var limit_get_usage_instances(xce_sched_core_t* core) return NULL; } +/* scheduler-sim-diagnostics spec, task 6 (bug-1825): sim_loop.c's + * sched_sim_driver_run() now also calls sim_bootstrap_limit_plugin() + * (sim_limit.c, pulled into this TU via the #include above), which in + * turn calls limit_new_plugin() (plugins/limit.c, not part of this TU) -- + * identical link-satisfying stub rationale as limit_get_configured_ + * limits()/limit_get_usage_instances() immediately above. Returning NULL + * here is also the behaviorally correct answer, not just a link + * placeholder: sim_bootstrap_limit_plugin() already handles a NULL + * limit_new_plugin() return gracefully (logs an ERROR, registers nothing, + * returns) -- the exact same "out of memory" path sim_bootstrap_picker() + * (sim_picker.c) takes for a NULL builtin_fcfs_new_plugin() -- so this + * file's fixtures simply end up with no limit plugin registered, same + * observable state as before this task. */ +xce_sched_plugin_t* limit_new_plugin(void) +{ + return NULL; +} + +/* limit_seed_config() (plugins/limit.c, not part of this TU) -- bug-1825 + * remediation round 2's new honestly-typed config-seed entry point. + * sim_bootstrap_limit_plugin() (sim_limit.c) only calls it after + * plugin_register_core() has already succeeded against a real, non-NULL + * limit_new_plugin() plugin -- unreachable in this file since the + * limit_new_plugin() stub immediately above always returns NULL, but the + * symbol must still resolve at link time. Same link-satisfying-stub + * rationale as limit_new_plugin() immediately above. */ +int limit_seed_config(xce_sched_plugin_t* plugin, xce_sched_core_t* core, + xce_conf4sched_t* config) +{ + (void)plugin; + (void)core; + (void)config; + return 0; +} + /* resource-accounting.md's reconciliation (resource.c, not linked here -- * this file only #includes core_events.c). core_events.c's * XCE_CORE_EVENT_JOB_RUSAGE handler calls this whenever job->accounts != diff --git a/src/scheduler/testsuite/test_sim_alt_events.c b/src/scheduler/testsuite/test_sim_alt_events.c index 9bdb3458..41c59506 100644 --- a/src/scheduler/testsuite/test_sim_alt_events.c +++ b/src/scheduler/testsuite/test_sim_alt_events.c @@ -95,6 +95,7 @@ #include "../src/sim.c" #include "../src/sim_loop.c" #include "../src/sim_picker.c" /* bug-1567: sched_sim_driver_run() now calls sim_bootstrap_picker() (sim_loop.c-cap sibling) as an extern -- undefined reference at link time otherwise */ +#include "../src/sim_limit.c" /* bug-1825: sched_sim_driver_run() now also calls sim_bootstrap_limit_plugin() (sim_loop.c-cap sibling, sim_picker.c's own bug-1567 precedent) as an extern -- undefined reference at link time otherwise */ #include "../src/sim_record.c" #include "../src/sim_verify.c" #include "../src/snapshot.c" @@ -136,6 +137,41 @@ var limit_get_usage_instances(xce_sched_core_t* core) return NULL; } +/* scheduler-sim-diagnostics spec, task 6 (bug-1825): sim_loop.c's + * sched_sim_driver_run() now also calls sim_bootstrap_limit_plugin() + * (sim_limit.c, pulled into this TU via the #include above), which in + * turn calls limit_new_plugin() (plugins/limit.c, not part of this TU) -- + * identical link-satisfying stub rationale as limit_get_configured_ + * limits()/limit_get_usage_instances() immediately above. Returning NULL + * here is also the behaviorally correct answer, not just a link + * placeholder: sim_bootstrap_limit_plugin() already handles a NULL + * limit_new_plugin() return gracefully (logs an ERROR, registers nothing, + * returns) -- the exact same "out of memory" path sim_bootstrap_picker() + * (sim_picker.c) takes for a NULL builtin_fcfs_new_plugin() -- so this + * file's fixtures simply end up with no limit plugin registered, same + * observable state as before this task. */ +xce_sched_plugin_t* limit_new_plugin(void) +{ + return NULL; +} + +/* limit_seed_config() (plugins/limit.c, not part of this TU) -- bug-1825 + * remediation round 2's new honestly-typed config-seed entry point. + * sim_bootstrap_limit_plugin() (sim_limit.c) only calls it after + * plugin_register_core() has already succeeded against a real, non-NULL + * limit_new_plugin() plugin -- unreachable in this file since the + * limit_new_plugin() stub immediately above always returns NULL, but the + * symbol must still resolve at link time. Same link-satisfying-stub + * rationale as limit_new_plugin() immediately above. */ +int limit_seed_config(xce_sched_plugin_t* plugin, xce_sched_core_t* core, + xce_conf4sched_t* config) +{ + (void)plugin; + (void)core; + (void)config; + return 0; +} + void sched_worker_release_host_ctx(xce_sched_worker_t* w) { (void)w; } /* resource-accounting.md's reconciliation (resource.c, not linked here -- diff --git a/src/scheduler/testsuite/test_sim_contention_parity.c b/src/scheduler/testsuite/test_sim_contention_parity.c index 1f3f26a7..09e5577a 100644 --- a/src/scheduler/testsuite/test_sim_contention_parity.c +++ b/src/scheduler/testsuite/test_sim_contention_parity.c @@ -180,6 +180,7 @@ #include "../src/sim.c" #include "../src/sim_loop.c" #include "../src/sim_picker.c" /* bug-1567: sched_sim_driver_run() calls sim_bootstrap_picker() (sim_loop.c-cap sibling) as an extern -- undefined reference at link time otherwise */ +#include "../src/sim_limit.c" /* bug-1825: sched_sim_driver_run() now also calls sim_bootstrap_limit_plugin() (sim_loop.c-cap sibling, sim_picker.c's own bug-1567 precedent) as an extern -- undefined reference at link time otherwise */ #include "../src/sim_record.c" #include "../src/sim_verify.c" #include "../src/snapshot.c" @@ -221,6 +222,41 @@ var limit_get_usage_instances(xce_sched_core_t* core) return NULL; } +/* scheduler-sim-diagnostics spec, task 6 (bug-1825): sim_loop.c's + * sched_sim_driver_run() now also calls sim_bootstrap_limit_plugin() + * (sim_limit.c, pulled into this TU via the #include above), which in + * turn calls limit_new_plugin() (plugins/limit.c, not part of this TU) -- + * identical link-satisfying stub rationale as limit_get_configured_ + * limits()/limit_get_usage_instances() immediately above. Returning NULL + * here is also the behaviorally correct answer, not just a link + * placeholder: sim_bootstrap_limit_plugin() already handles a NULL + * limit_new_plugin() return gracefully (logs an ERROR, registers nothing, + * returns) -- the exact same "out of memory" path sim_bootstrap_picker() + * (sim_picker.c) takes for a NULL builtin_fcfs_new_plugin() -- so this + * file's fixtures simply end up with no limit plugin registered, same + * observable state as before this task. */ +xce_sched_plugin_t* limit_new_plugin(void) +{ + return NULL; +} + +/* limit_seed_config() (plugins/limit.c, not part of this TU) -- bug-1825 + * remediation round 2's new honestly-typed config-seed entry point. + * sim_bootstrap_limit_plugin() (sim_limit.c) only calls it after + * plugin_register_core() has already succeeded against a real, non-NULL + * limit_new_plugin() plugin -- unreachable in this file since the + * limit_new_plugin() stub immediately above always returns NULL, but the + * symbol must still resolve at link time. Same link-satisfying-stub + * rationale as limit_new_plugin() immediately above. */ +int limit_seed_config(xce_sched_plugin_t* plugin, xce_sched_core_t* core, + xce_conf4sched_t* config) +{ + (void)plugin; + (void)core; + (void)config; + return 0; +} + /* xce_sched_driver_on_decision()'s (driver.c) publish-side linker * dependencies -- never actually invoked: this file never registers a * decision callback. Same rationale test_sim_resource_contention.c's own diff --git a/src/scheduler/testsuite/test_sim_e2e_original_motivation.c b/src/scheduler/testsuite/test_sim_e2e_original_motivation.c index 8d31afcc..e2da78c0 100644 --- a/src/scheduler/testsuite/test_sim_e2e_original_motivation.c +++ b/src/scheduler/testsuite/test_sim_e2e_original_motivation.c @@ -203,6 +203,7 @@ #include "../src/sim.c" #include "../src/sim_loop.c" #include "../src/sim_picker.c" /* bug-1567: sched_sim_driver_run() calls sim_bootstrap_picker() (sim_loop.c-cap sibling) as an extern -- undefined reference at link time otherwise */ +#include "../src/sim_limit.c" /* bug-1825: sched_sim_driver_run() now also calls sim_bootstrap_limit_plugin() (sim_loop.c-cap sibling, sim_picker.c's own bug-1567 precedent) as an extern -- undefined reference at link time otherwise */ #include "../src/sim_record.c" #include "../src/sim_verify.c" #include "../src/snapshot.c" @@ -244,6 +245,41 @@ var limit_get_usage_instances(xce_sched_core_t* core) return NULL; } +/* scheduler-sim-diagnostics spec, task 6 (bug-1825): sim_loop.c's + * sched_sim_driver_run() now also calls sim_bootstrap_limit_plugin() + * (sim_limit.c, pulled into this TU via the #include above), which in + * turn calls limit_new_plugin() (plugins/limit.c, not part of this TU) -- + * identical link-satisfying stub rationale as limit_get_configured_ + * limits()/limit_get_usage_instances() immediately above. Returning NULL + * here is also the behaviorally correct answer, not just a link + * placeholder: sim_bootstrap_limit_plugin() already handles a NULL + * limit_new_plugin() return gracefully (logs an ERROR, registers nothing, + * returns) -- the exact same "out of memory" path sim_bootstrap_picker() + * (sim_picker.c) takes for a NULL builtin_fcfs_new_plugin() -- so this + * file's fixtures simply end up with no limit plugin registered, same + * observable state as before this task. */ +xce_sched_plugin_t* limit_new_plugin(void) +{ + return NULL; +} + +/* limit_seed_config() (plugins/limit.c, not part of this TU) -- bug-1825 + * remediation round 2's new honestly-typed config-seed entry point. + * sim_bootstrap_limit_plugin() (sim_limit.c) only calls it after + * plugin_register_core() has already succeeded against a real, non-NULL + * limit_new_plugin() plugin -- unreachable in this file since the + * limit_new_plugin() stub immediately above always returns NULL, but the + * symbol must still resolve at link time. Same link-satisfying-stub + * rationale as limit_new_plugin() immediately above. */ +int limit_seed_config(xce_sched_plugin_t* plugin, xce_sched_core_t* core, + xce_conf4sched_t* config) +{ + (void)plugin; + (void)core; + (void)config; + return 0; +} + /* resource.c's sched_try_alloc_on_host_()'s own link-time dependency -- * never actually invoked (this file calls sched_check_host_satisfy()/ * sched_charge_account()/sched_commit_job_alloc() directly). Same stub diff --git a/src/scheduler/testsuite/test_sim_equivalence.c b/src/scheduler/testsuite/test_sim_equivalence.c index c5031ee6..b9b3ea8d 100644 --- a/src/scheduler/testsuite/test_sim_equivalence.c +++ b/src/scheduler/testsuite/test_sim_equivalence.c @@ -164,6 +164,7 @@ #include "../src/sim.c" #include "../src/sim_loop.c" #include "../src/sim_picker.c" /* bug-1567: sched_sim_driver_run() now calls sim_bootstrap_picker() (sim_loop.c-cap sibling) as an extern -- undefined reference at link time otherwise */ +#include "../src/sim_limit.c" /* bug-1825: sched_sim_driver_run() now also calls sim_bootstrap_limit_plugin() (sim_loop.c-cap sibling, sim_picker.c's own bug-1567 precedent) as an extern -- undefined reference at link time otherwise */ #include "../src/sim_record.c" #include "../src/sim_verify.c" #include "../src/snapshot.c" @@ -205,6 +206,41 @@ var limit_get_usage_instances(xce_sched_core_t* core) return NULL; } +/* scheduler-sim-diagnostics spec, task 6 (bug-1825): sim_loop.c's + * sched_sim_driver_run() now also calls sim_bootstrap_limit_plugin() + * (sim_limit.c, pulled into this TU via the #include above), which in + * turn calls limit_new_plugin() (plugins/limit.c, not part of this TU) -- + * identical link-satisfying stub rationale as limit_get_configured_ + * limits()/limit_get_usage_instances() immediately above. Returning NULL + * here is also the behaviorally correct answer, not just a link + * placeholder: sim_bootstrap_limit_plugin() already handles a NULL + * limit_new_plugin() return gracefully (logs an ERROR, registers nothing, + * returns) -- the exact same "out of memory" path sim_bootstrap_picker() + * (sim_picker.c) takes for a NULL builtin_fcfs_new_plugin() -- so this + * file's fixtures simply end up with no limit plugin registered, same + * observable state as before this task. */ +xce_sched_plugin_t* limit_new_plugin(void) +{ + return NULL; +} + +/* limit_seed_config() (plugins/limit.c, not part of this TU) -- bug-1825 + * remediation round 2's new honestly-typed config-seed entry point. + * sim_bootstrap_limit_plugin() (sim_limit.c) only calls it after + * plugin_register_core() has already succeeded against a real, non-NULL + * limit_new_plugin() plugin -- unreachable in this file since the + * limit_new_plugin() stub immediately above always returns NULL, but the + * symbol must still resolve at link time. Same link-satisfying-stub + * rationale as limit_new_plugin() immediately above. */ +int limit_seed_config(xce_sched_plugin_t* plugin, xce_sched_core_t* core, + xce_conf4sched_t* config) +{ + (void)plugin; + (void)core; + (void)config; + return 0; +} + /* resource-accounting.md's reconciliation (resource.c, not linked here -- * this file only #includes core_events.c). core_events.c's * XCE_CORE_EVENT_JOB_RUSAGE handler calls this whenever job->accounts != diff --git a/src/scheduler/testsuite/test_sim_event_loop.c b/src/scheduler/testsuite/test_sim_event_loop.c index 2c8ada31..f53578f0 100644 --- a/src/scheduler/testsuite/test_sim_event_loop.c +++ b/src/scheduler/testsuite/test_sim_event_loop.c @@ -111,6 +111,7 @@ #include "../src/sim.c" #include "../src/sim_loop.c" #include "../src/sim_picker.c" /* bug-1567: sched_sim_driver_run() now calls sim_bootstrap_picker() (sim_loop.c-cap sibling) as an extern -- undefined reference at link time otherwise */ +#include "../src/sim_limit.c" /* bug-1825: sched_sim_driver_run() now also calls sim_bootstrap_limit_plugin() (sim_loop.c-cap sibling, sim_picker.c's own bug-1567 precedent) as an extern -- undefined reference at link time otherwise */ #include "../src/sim_record.c" #include "../src/sim_verify.c" #include "../src/snapshot.c" @@ -152,6 +153,41 @@ var limit_get_usage_instances(xce_sched_core_t* core) return NULL; } +/* scheduler-sim-diagnostics spec, task 6 (bug-1825): sim_loop.c's + * sched_sim_driver_run() now also calls sim_bootstrap_limit_plugin() + * (sim_limit.c, pulled into this TU via the #include above), which in + * turn calls limit_new_plugin() (plugins/limit.c, not part of this TU) -- + * identical link-satisfying stub rationale as limit_get_configured_ + * limits()/limit_get_usage_instances() immediately above. Returning NULL + * here is also the behaviorally correct answer, not just a link + * placeholder: sim_bootstrap_limit_plugin() already handles a NULL + * limit_new_plugin() return gracefully (logs an ERROR, registers nothing, + * returns) -- the exact same "out of memory" path sim_bootstrap_picker() + * (sim_picker.c) takes for a NULL builtin_fcfs_new_plugin() -- so this + * file's fixtures simply end up with no limit plugin registered, same + * observable state as before this task. */ +xce_sched_plugin_t* limit_new_plugin(void) +{ + return NULL; +} + +/* limit_seed_config() (plugins/limit.c, not part of this TU) -- bug-1825 + * remediation round 2's new honestly-typed config-seed entry point. + * sim_bootstrap_limit_plugin() (sim_limit.c) only calls it after + * plugin_register_core() has already succeeded against a real, non-NULL + * limit_new_plugin() plugin -- unreachable in this file since the + * limit_new_plugin() stub immediately above always returns NULL, but the + * symbol must still resolve at link time. Same link-satisfying-stub + * rationale as limit_new_plugin() immediately above. */ +int limit_seed_config(xce_sched_plugin_t* plugin, xce_sched_core_t* core, + xce_conf4sched_t* config) +{ + (void)plugin; + (void)core; + (void)config; + return 0; +} + /* resource-accounting.md's reconciliation (resource.c, not linked here -- * this file only #includes core_events.c). core_events.c's * XCE_CORE_EVENT_JOB_RUSAGE handler calls this whenever job->accounts != diff --git a/src/scheduler/testsuite/test_sim_print_decision_gate.c b/src/scheduler/testsuite/test_sim_print_decision_gate.c index ef7439c4..a361350d 100644 --- a/src/scheduler/testsuite/test_sim_print_decision_gate.c +++ b/src/scheduler/testsuite/test_sim_print_decision_gate.c @@ -123,6 +123,7 @@ #include "../src/sim.c" #include "../src/sim_loop.c" #include "../src/sim_picker.c" /* bug-1567: sched_sim_driver_run() now calls sim_bootstrap_picker() (sim_loop.c-cap sibling) as an extern -- undefined reference at link time otherwise */ +#include "../src/sim_limit.c" /* bug-1825: sched_sim_driver_run() now also calls sim_bootstrap_limit_plugin() (sim_loop.c-cap sibling, sim_picker.c's own bug-1567 precedent) as an extern -- undefined reference at link time otherwise */ #include "../src/sim_record.c" #include "../src/sim_verify.c" #include "../src/snapshot.c" @@ -164,6 +165,41 @@ var limit_get_usage_instances(xce_sched_core_t* core) return NULL; } +/* scheduler-sim-diagnostics spec, task 6 (bug-1825): sim_loop.c's + * sched_sim_driver_run() now also calls sim_bootstrap_limit_plugin() + * (sim_limit.c, pulled into this TU via the #include above), which in + * turn calls limit_new_plugin() (plugins/limit.c, not part of this TU) -- + * identical link-satisfying stub rationale as limit_get_configured_ + * limits()/limit_get_usage_instances() immediately above. Returning NULL + * here is also the behaviorally correct answer, not just a link + * placeholder: sim_bootstrap_limit_plugin() already handles a NULL + * limit_new_plugin() return gracefully (logs an ERROR, registers nothing, + * returns) -- the exact same "out of memory" path sim_bootstrap_picker() + * (sim_picker.c) takes for a NULL builtin_fcfs_new_plugin() -- so this + * file's fixtures simply end up with no limit plugin registered, same + * observable state as before this task. */ +xce_sched_plugin_t* limit_new_plugin(void) +{ + return NULL; +} + +/* limit_seed_config() (plugins/limit.c, not part of this TU) -- bug-1825 + * remediation round 2's new honestly-typed config-seed entry point. + * sim_bootstrap_limit_plugin() (sim_limit.c) only calls it after + * plugin_register_core() has already succeeded against a real, non-NULL + * limit_new_plugin() plugin -- unreachable in this file since the + * limit_new_plugin() stub immediately above always returns NULL, but the + * symbol must still resolve at link time. Same link-satisfying-stub + * rationale as limit_new_plugin() immediately above. */ +int limit_seed_config(xce_sched_plugin_t* plugin, xce_sched_core_t* core, + xce_conf4sched_t* config) +{ + (void)plugin; + (void)core; + (void)config; + return 0; +} + /* resource-accounting.md's reconciliation (resource.c, not linked here -- * this file only #includes core_events.c). core_events.c's * XCE_CORE_EVENT_JOB_RUSAGE handler calls this whenever job->accounts != diff --git a/src/scheduler/testsuite/test_sim_reconcile_unaccounted.c b/src/scheduler/testsuite/test_sim_reconcile_unaccounted.c index a3b802ac..c8914a27 100644 --- a/src/scheduler/testsuite/test_sim_reconcile_unaccounted.c +++ b/src/scheduler/testsuite/test_sim_reconcile_unaccounted.c @@ -126,6 +126,7 @@ #include "../src/sim.c" #include "../src/sim_loop.c" #include "../src/sim_picker.c" /* bug-1567: sim_loop.c calls sim_bootstrap_picker() (sim_loop.c-cap sibling) as an extern -- undefined reference at link time otherwise */ +#include "../src/sim_limit.c" /* bug-1825: sched_sim_driver_run() now also calls sim_bootstrap_limit_plugin() (sim_loop.c-cap sibling, sim_picker.c's own bug-1567 precedent) as an extern -- undefined reference at link time otherwise */ #include "../src/sim_record.c" #include "../src/sim_verify.c" #include "../src/snapshot.c" @@ -167,6 +168,41 @@ var limit_get_usage_instances(xce_sched_core_t* core) return NULL; } +/* scheduler-sim-diagnostics spec, task 6 (bug-1825): sim_loop.c's + * sched_sim_driver_run() now also calls sim_bootstrap_limit_plugin() + * (sim_limit.c, pulled into this TU via the #include above), which in + * turn calls limit_new_plugin() (plugins/limit.c, not part of this TU) -- + * identical link-satisfying stub rationale as limit_get_configured_ + * limits()/limit_get_usage_instances() immediately above. Returning NULL + * here is also the behaviorally correct answer, not just a link + * placeholder: sim_bootstrap_limit_plugin() already handles a NULL + * limit_new_plugin() return gracefully (logs an ERROR, registers nothing, + * returns) -- the exact same "out of memory" path sim_bootstrap_picker() + * (sim_picker.c) takes for a NULL builtin_fcfs_new_plugin() -- so this + * file's fixtures simply end up with no limit plugin registered, same + * observable state as before this task. */ +xce_sched_plugin_t* limit_new_plugin(void) +{ + return NULL; +} + +/* limit_seed_config() (plugins/limit.c, not part of this TU) -- bug-1825 + * remediation round 2's new honestly-typed config-seed entry point. + * sim_bootstrap_limit_plugin() (sim_limit.c) only calls it after + * plugin_register_core() has already succeeded against a real, non-NULL + * limit_new_plugin() plugin -- unreachable in this file since the + * limit_new_plugin() stub immediately above always returns NULL, but the + * symbol must still resolve at link time. Same link-satisfying-stub + * rationale as limit_new_plugin() immediately above. */ +int limit_seed_config(xce_sched_plugin_t* plugin, xce_sched_core_t* core, + xce_conf4sched_t* config) +{ + (void)plugin; + (void)core; + (void)config; + return 0; +} + /* sched_worker_release_host_ctx() is now a REAL (non-stub) symbol pulled in * from resource.c (task 2.1 addition, below) -- no local stand-in needed or * permitted (a local definition here would collide at link time). Unused by diff --git a/src/scheduler/testsuite/test_sim_record_output.c b/src/scheduler/testsuite/test_sim_record_output.c index 786117c4..c700117a 100644 --- a/src/scheduler/testsuite/test_sim_record_output.c +++ b/src/scheduler/testsuite/test_sim_record_output.c @@ -82,6 +82,7 @@ #include "../src/sim.c" #include "../src/sim_loop.c" #include "../src/sim_picker.c" /* bug-1567: sched_sim_driver_run() now calls sim_bootstrap_picker() (sim_loop.c-cap sibling) as an extern -- undefined reference at link time otherwise */ +#include "../src/sim_limit.c" /* bug-1825: sched_sim_driver_run() now also calls sim_bootstrap_limit_plugin() (sim_loop.c-cap sibling, sim_picker.c's own bug-1567 precedent) as an extern -- undefined reference at link time otherwise */ #include "../src/sim_record.c" #include "../src/sim_verify.c" #include "../src/snapshot.c" @@ -123,6 +124,41 @@ var limit_get_usage_instances(xce_sched_core_t* core) return NULL; } +/* scheduler-sim-diagnostics spec, task 6 (bug-1825): sim_loop.c's + * sched_sim_driver_run() now also calls sim_bootstrap_limit_plugin() + * (sim_limit.c, pulled into this TU via the #include above), which in + * turn calls limit_new_plugin() (plugins/limit.c, not part of this TU) -- + * identical link-satisfying stub rationale as limit_get_configured_ + * limits()/limit_get_usage_instances() immediately above. Returning NULL + * here is also the behaviorally correct answer, not just a link + * placeholder: sim_bootstrap_limit_plugin() already handles a NULL + * limit_new_plugin() return gracefully (logs an ERROR, registers nothing, + * returns) -- the exact same "out of memory" path sim_bootstrap_picker() + * (sim_picker.c) takes for a NULL builtin_fcfs_new_plugin() -- so this + * file's fixtures simply end up with no limit plugin registered, same + * observable state as before this task. */ +xce_sched_plugin_t* limit_new_plugin(void) +{ + return NULL; +} + +/* limit_seed_config() (plugins/limit.c, not part of this TU) -- bug-1825 + * remediation round 2's new honestly-typed config-seed entry point. + * sim_bootstrap_limit_plugin() (sim_limit.c) only calls it after + * plugin_register_core() has already succeeded against a real, non-NULL + * limit_new_plugin() plugin -- unreachable in this file since the + * limit_new_plugin() stub immediately above always returns NULL, but the + * symbol must still resolve at link time. Same link-satisfying-stub + * rationale as limit_new_plugin() immediately above. */ +int limit_seed_config(xce_sched_plugin_t* plugin, xce_sched_core_t* core, + xce_conf4sched_t* config) +{ + (void)plugin; + (void)core; + (void)config; + return 0; +} + /* resource-accounting.md's reconciliation (resource.c, not linked here -- * this file only #includes core_events.c). core_events.c's * XCE_CORE_EVENT_JOB_RUSAGE handler calls this whenever job->accounts != diff --git a/src/scheduler/testsuite/test_sim_resource_contention.c b/src/scheduler/testsuite/test_sim_resource_contention.c index 69ad935e..bb309ade 100644 --- a/src/scheduler/testsuite/test_sim_resource_contention.c +++ b/src/scheduler/testsuite/test_sim_resource_contention.c @@ -147,6 +147,7 @@ #include "../src/sim.c" #include "../src/sim_loop.c" #include "../src/sim_picker.c" /* bug-1567: sched_sim_driver_run() now calls sim_bootstrap_picker() (sim_loop.c-cap sibling) as an extern -- undefined reference at link time otherwise */ +#include "../src/sim_limit.c" /* bug-1825: sched_sim_driver_run() now also calls sim_bootstrap_limit_plugin() (sim_loop.c-cap sibling, sim_picker.c's own bug-1567 precedent) as an extern -- undefined reference at link time otherwise */ #include "../src/sim_record.c" #include "../src/sim_verify.c" #include "../src/snapshot.c" @@ -188,6 +189,41 @@ var limit_get_usage_instances(xce_sched_core_t* core) return NULL; } +/* scheduler-sim-diagnostics spec, task 6 (bug-1825): sim_loop.c's + * sched_sim_driver_run() now also calls sim_bootstrap_limit_plugin() + * (sim_limit.c, pulled into this TU via the #include above), which in + * turn calls limit_new_plugin() (plugins/limit.c, not part of this TU) -- + * identical link-satisfying stub rationale as limit_get_configured_ + * limits()/limit_get_usage_instances() immediately above. Returning NULL + * here is also the behaviorally correct answer, not just a link + * placeholder: sim_bootstrap_limit_plugin() already handles a NULL + * limit_new_plugin() return gracefully (logs an ERROR, registers nothing, + * returns) -- the exact same "out of memory" path sim_bootstrap_picker() + * (sim_picker.c) takes for a NULL builtin_fcfs_new_plugin() -- so this + * file's fixtures simply end up with no limit plugin registered, same + * observable state as before this task. */ +xce_sched_plugin_t* limit_new_plugin(void) +{ + return NULL; +} + +/* limit_seed_config() (plugins/limit.c, not part of this TU) -- bug-1825 + * remediation round 2's new honestly-typed config-seed entry point. + * sim_bootstrap_limit_plugin() (sim_limit.c) only calls it after + * plugin_register_core() has already succeeded against a real, non-NULL + * limit_new_plugin() plugin -- unreachable in this file since the + * limit_new_plugin() stub immediately above always returns NULL, but the + * symbol must still resolve at link time. Same link-satisfying-stub + * rationale as limit_new_plugin() immediately above. */ +int limit_seed_config(xce_sched_plugin_t* plugin, xce_sched_core_t* core, + xce_conf4sched_t* config) +{ + (void)plugin; + (void)core; + (void)config; + return 0; +} + /* xce_sched_driver_run_normal_mode_round()'s (driver.c) linker dependency -- * a no-op stand-in, same rationale test_sim_equivalence.c's own copy * documents (this file drives its own resource accounting directly via diff --git a/src/scheduler/testsuite/test_sim_round_boundary_reconciliation.c b/src/scheduler/testsuite/test_sim_round_boundary_reconciliation.c index 056c91e8..8acde9c0 100644 --- a/src/scheduler/testsuite/test_sim_round_boundary_reconciliation.c +++ b/src/scheduler/testsuite/test_sim_round_boundary_reconciliation.c @@ -115,6 +115,7 @@ #include "../src/sim.c" #include "../src/sim_loop.c" #include "../src/sim_picker.c" /* bug-1567: sched_sim_driver_run() calls sim_bootstrap_picker() (sim_loop.c-cap sibling) as an extern -- undefined reference at link time otherwise */ +#include "../src/sim_limit.c" /* bug-1825: sched_sim_driver_run() now also calls sim_bootstrap_limit_plugin() (sim_loop.c-cap sibling, sim_picker.c's own bug-1567 precedent) as an extern -- undefined reference at link time otherwise */ #include "../src/sim_record.c" #include "../src/sim_verify.c" #include "../src/snapshot.c" @@ -156,6 +157,41 @@ var limit_get_usage_instances(xce_sched_core_t* core) return NULL; } +/* scheduler-sim-diagnostics spec, task 6 (bug-1825): sim_loop.c's + * sched_sim_driver_run() now also calls sim_bootstrap_limit_plugin() + * (sim_limit.c, pulled into this TU via the #include above), which in + * turn calls limit_new_plugin() (plugins/limit.c, not part of this TU) -- + * identical link-satisfying stub rationale as limit_get_configured_ + * limits()/limit_get_usage_instances() immediately above. Returning NULL + * here is also the behaviorally correct answer, not just a link + * placeholder: sim_bootstrap_limit_plugin() already handles a NULL + * limit_new_plugin() return gracefully (logs an ERROR, registers nothing, + * returns) -- the exact same "out of memory" path sim_bootstrap_picker() + * (sim_picker.c) takes for a NULL builtin_fcfs_new_plugin() -- so this + * file's fixtures simply end up with no limit plugin registered, same + * observable state as before this task. */ +xce_sched_plugin_t* limit_new_plugin(void) +{ + return NULL; +} + +/* limit_seed_config() (plugins/limit.c, not part of this TU) -- bug-1825 + * remediation round 2's new honestly-typed config-seed entry point. + * sim_bootstrap_limit_plugin() (sim_limit.c) only calls it after + * plugin_register_core() has already succeeded against a real, non-NULL + * limit_new_plugin() plugin -- unreachable in this file since the + * limit_new_plugin() stub immediately above always returns NULL, but the + * symbol must still resolve at link time. Same link-satisfying-stub + * rationale as limit_new_plugin() immediately above. */ +int limit_seed_config(xce_sched_plugin_t* plugin, xce_sched_core_t* core, + xce_conf4sched_t* config) +{ + (void)plugin; + (void)core; + (void)config; + return 0; +} + /* resource.c's sched_try_alloc_on_host_()'s own link-time dependency -- * never actually invoked (this file calls sched_check_host_satisfy() * directly, never sched_try_alloc_on_hosts()/sched_try_alloc_on_host_() diff --git a/src/scheduler/testsuite/test_sim_session_ordering.c b/src/scheduler/testsuite/test_sim_session_ordering.c index e6e84fc8..59a43d1b 100644 --- a/src/scheduler/testsuite/test_sim_session_ordering.c +++ b/src/scheduler/testsuite/test_sim_session_ordering.c @@ -147,6 +147,7 @@ #include "../src/sim.c" #include "../src/sim_loop.c" #include "../src/sim_picker.c" /* bug-1567: sched_sim_driver_run() calls sim_bootstrap_picker() (sim_loop.c-cap sibling) as an extern -- undefined reference at link time otherwise */ +#include "../src/sim_limit.c" /* bug-1825: sched_sim_driver_run() now also calls sim_bootstrap_limit_plugin() (sim_loop.c-cap sibling, sim_picker.c's own bug-1567 precedent) as an extern -- undefined reference at link time otherwise */ #include "../src/sim_record.c" #include "../src/sim_verify.c" #include "../src/snapshot.c" @@ -188,6 +189,41 @@ var limit_get_usage_instances(xce_sched_core_t* core) return NULL; } +/* scheduler-sim-diagnostics spec, task 6 (bug-1825): sim_loop.c's + * sched_sim_driver_run() now also calls sim_bootstrap_limit_plugin() + * (sim_limit.c, pulled into this TU via the #include above), which in + * turn calls limit_new_plugin() (plugins/limit.c, not part of this TU) -- + * identical link-satisfying stub rationale as limit_get_configured_ + * limits()/limit_get_usage_instances() immediately above. Returning NULL + * here is also the behaviorally correct answer, not just a link + * placeholder: sim_bootstrap_limit_plugin() already handles a NULL + * limit_new_plugin() return gracefully (logs an ERROR, registers nothing, + * returns) -- the exact same "out of memory" path sim_bootstrap_picker() + * (sim_picker.c) takes for a NULL builtin_fcfs_new_plugin() -- so this + * file's fixtures simply end up with no limit plugin registered, same + * observable state as before this task. */ +xce_sched_plugin_t* limit_new_plugin(void) +{ + return NULL; +} + +/* limit_seed_config() (plugins/limit.c, not part of this TU) -- bug-1825 + * remediation round 2's new honestly-typed config-seed entry point. + * sim_bootstrap_limit_plugin() (sim_limit.c) only calls it after + * plugin_register_core() has already succeeded against a real, non-NULL + * limit_new_plugin() plugin -- unreachable in this file since the + * limit_new_plugin() stub immediately above always returns NULL, but the + * symbol must still resolve at link time. Same link-satisfying-stub + * rationale as limit_new_plugin() immediately above. */ +int limit_seed_config(xce_sched_plugin_t* plugin, xce_sched_core_t* core, + xce_conf4sched_t* config) +{ + (void)plugin; + (void)core; + (void)config; + return 0; +} + /* core_reset_workers()'s (core_session.c) linker dependency -- never * invoked (no worker is ever added here). */ void sched_worker_release_host_ctx(xce_sched_worker_t* w) { (void)w; } diff --git a/src/scheduler/testsuite/test_sim_snapshot_reconcile_gap.c b/src/scheduler/testsuite/test_sim_snapshot_reconcile_gap.c index b1e7563f..1e2a4b05 100644 --- a/src/scheduler/testsuite/test_sim_snapshot_reconcile_gap.c +++ b/src/scheduler/testsuite/test_sim_snapshot_reconcile_gap.c @@ -101,6 +101,7 @@ #include "../src/sim.c" #include "../src/sim_loop.c" #include "../src/sim_picker.c" /* bug-1567: sched_sim_driver_run() calls sim_bootstrap_picker() (sim_loop.c-cap sibling) as an extern -- undefined reference at link time otherwise */ +#include "../src/sim_limit.c" /* bug-1825: sched_sim_driver_run() now also calls sim_bootstrap_limit_plugin() (sim_loop.c-cap sibling, sim_picker.c's own bug-1567 precedent) as an extern -- undefined reference at link time otherwise */ #include "../src/sim_record.c" #include "../src/sim_verify.c" #include "../src/snapshot.c" @@ -142,6 +143,41 @@ var limit_get_usage_instances(xce_sched_core_t* core) return NULL; } +/* scheduler-sim-diagnostics spec, task 6 (bug-1825): sim_loop.c's + * sched_sim_driver_run() now also calls sim_bootstrap_limit_plugin() + * (sim_limit.c, pulled into this TU via the #include above), which in + * turn calls limit_new_plugin() (plugins/limit.c, not part of this TU) -- + * identical link-satisfying stub rationale as limit_get_configured_ + * limits()/limit_get_usage_instances() immediately above. Returning NULL + * here is also the behaviorally correct answer, not just a link + * placeholder: sim_bootstrap_limit_plugin() already handles a NULL + * limit_new_plugin() return gracefully (logs an ERROR, registers nothing, + * returns) -- the exact same "out of memory" path sim_bootstrap_picker() + * (sim_picker.c) takes for a NULL builtin_fcfs_new_plugin() -- so this + * file's fixtures simply end up with no limit plugin registered, same + * observable state as before this task. */ +xce_sched_plugin_t* limit_new_plugin(void) +{ + return NULL; +} + +/* limit_seed_config() (plugins/limit.c, not part of this TU) -- bug-1825 + * remediation round 2's new honestly-typed config-seed entry point. + * sim_bootstrap_limit_plugin() (sim_limit.c) only calls it after + * plugin_register_core() has already succeeded against a real, non-NULL + * limit_new_plugin() plugin -- unreachable in this file since the + * limit_new_plugin() stub immediately above always returns NULL, but the + * symbol must still resolve at link time. Same link-satisfying-stub + * rationale as limit_new_plugin() immediately above. */ +int limit_seed_config(xce_sched_plugin_t* plugin, xce_sched_core_t* core, + xce_conf4sched_t* config) +{ + (void)plugin; + (void)core; + (void)config; + return 0; +} + /* core_reset_workers()'s (core_session.c) linker dependency -- same stub * test_sim_verify.c already establishes; never invoked (no worker is ever * added here). */ diff --git a/src/scheduler/testsuite/test_sim_verify.c b/src/scheduler/testsuite/test_sim_verify.c index e82b140d..37fa23e4 100644 --- a/src/scheduler/testsuite/test_sim_verify.c +++ b/src/scheduler/testsuite/test_sim_verify.c @@ -102,6 +102,7 @@ #include "../src/sim.c" #include "../src/sim_loop.c" #include "../src/sim_picker.c" /* bug-1567: sched_sim_driver_run() now calls sim_bootstrap_picker() (sim_loop.c-cap sibling) as an extern -- undefined reference at link time otherwise */ +#include "../src/sim_limit.c" /* bug-1825: sched_sim_driver_run() now also calls sim_bootstrap_limit_plugin() (sim_loop.c-cap sibling, sim_picker.c's own bug-1567 precedent) as an extern -- undefined reference at link time otherwise */ #include "../src/sim_record.c" #include "../src/sim_verify.c" #include "../src/snapshot.c" @@ -143,6 +144,41 @@ var limit_get_usage_instances(xce_sched_core_t* core) return NULL; } +/* scheduler-sim-diagnostics spec, task 6 (bug-1825): sim_loop.c's + * sched_sim_driver_run() now also calls sim_bootstrap_limit_plugin() + * (sim_limit.c, pulled into this TU via the #include above), which in + * turn calls limit_new_plugin() (plugins/limit.c, not part of this TU) -- + * identical link-satisfying stub rationale as limit_get_configured_ + * limits()/limit_get_usage_instances() immediately above. Returning NULL + * here is also the behaviorally correct answer, not just a link + * placeholder: sim_bootstrap_limit_plugin() already handles a NULL + * limit_new_plugin() return gracefully (logs an ERROR, registers nothing, + * returns) -- the exact same "out of memory" path sim_bootstrap_picker() + * (sim_picker.c) takes for a NULL builtin_fcfs_new_plugin() -- so this + * file's fixtures simply end up with no limit plugin registered, same + * observable state as before this task. */ +xce_sched_plugin_t* limit_new_plugin(void) +{ + return NULL; +} + +/* limit_seed_config() (plugins/limit.c, not part of this TU) -- bug-1825 + * remediation round 2's new honestly-typed config-seed entry point. + * sim_bootstrap_limit_plugin() (sim_limit.c) only calls it after + * plugin_register_core() has already succeeded against a real, non-NULL + * limit_new_plugin() plugin -- unreachable in this file since the + * limit_new_plugin() stub immediately above always returns NULL, but the + * symbol must still resolve at link time. Same link-satisfying-stub + * rationale as limit_new_plugin() immediately above. */ +int limit_seed_config(xce_sched_plugin_t* plugin, xce_sched_core_t* core, + xce_conf4sched_t* config) +{ + (void)plugin; + (void)core; + (void)config; + return 0; +} + /* resource-accounting.md's reconciliation (resource.c, not linked here -- * this file only #includes core_events.c). core_events.c's * XCE_CORE_EVENT_JOB_RUSAGE handler calls this whenever job->accounts !=