fix(scheduler): re-derive worker/shared resource used-totals on snapshot load

xce_sched_core_load_snapshot() left every restored worker's resInUse
(and core->sharedInUse) at zero -- zarray_init() in the workers-
restoration block clears them, and nothing re-summed against the
restored job allocations. A caller inspecting worker/resource state
between a snapshot load and the first scheduling session (e.g. the
REPL's `info worker` before the first `step`) saw a misleadingly empty
"used" total for jobs already known to be RUNNING. Added one
sched_account_jobs(core) call after workers/jobs are restored -- a
correct idempotent-from-zero re-derivation using the same
sched_charge_account() chain the next real session would use anyway.
Must not be called again without an intervening reset (accumulates,
does not assign) -- verified no such double-call exists in this change.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019QxaSzHRB7pmYFp4cVnwkA
This commit is contained in:
Rongsong Shen 2026-08-29 11:53:32 +00:00
parent 42fdeb6669
commit 14f95b06fe
No known key found for this signature in database
GPG key ID: 5BCDAAE0123E9FE2
5 changed files with 309 additions and 48 deletions

View file

@ -644,11 +644,27 @@ test_sim_replay_calls_account_jobs_once_per_round_within_session_window(
/* THE central regression proof (task 2.1's own scope, Req 1.3/1.4):
* before this task, this count is 0 (sim_loop.c never called
* sched_account_jobs() at all, bug-1613) -- after, it is 2, one per
* replayed round, not merely once ever. */
TEST_ASSERT_EQUAL_INT(2, atomic_load(&g_spy_calls));
* sched_account_jobs() at all, bug-1613) -- after, it is 2 per-round
* calls plus ONE load-time call (task 4.6:
* xce_sched_core_load_snapshot(), called from sched_sim_driver_start()
* at the very top of sched_sim_driver_run(), now also charges
* sched_account_jobs() once for whatever RUN-state jobs the loaded
* snapshot already has -- see snapshot_load.c's own doc comment on
* that call). Total 3, not merely once ever and not merely once per
* round. */
TEST_ASSERT_EQUAL_INT(3, atomic_load(&g_spy_calls));
for (i = 0; i < 2; i++) {
/* Index 0 is task 4.6's load-time call -- by design it runs BEFORE any
* session begins (core->session_active == 0 at that point is correct,
* not a violation of the round-boundary contract below, which only
* governs calls made ONCE A ROUND IS UNDERWAY). */
TEST_ASSERT_FALSE_MESSAGE(
g_spy_session_active_ok[0],
"the load-time sched_account_jobs() call (task 4.6) runs before "
"any session begins -- core->session_active must be 0 here, not "
"the round-boundary contract's session_active==1");
for (i = 1; i < 3; i++) {
TEST_ASSERT_TRUE_MESSAGE(
g_spy_session_active_ok[i],
"sched_account_jobs() must run with core->session_active==1 -- "
@ -737,8 +753,12 @@ static void test_sim_replay_batch_mode_uses_the_same_round_boundary(void)
rc = sched_sim_driver_run(&sim, &sim_args);
TEST_ASSERT_EQUAL_INT(0, rc);
TEST_ASSERT_EQUAL_INT(1, atomic_load(&g_spy_calls));
TEST_ASSERT_TRUE(g_spy_session_active_ok[0]);
/* 2, not 1: task 4.6's load-time call (index 0, session_active==0 by
* design -- see the two-round test's own comment above) plus this
* batch run's one replayed round (index 1, session_active==1). */
TEST_ASSERT_EQUAL_INT(2, atomic_load(&g_spy_calls));
TEST_ASSERT_FALSE(g_spy_session_active_ok[0]);
TEST_ASSERT_TRUE(g_spy_session_active_ok[1]);
TEST_ASSERT_EQUAL_INT(1, atomic_load(&g_begin_session_spy_calls));
TEST_ASSERT_TRUE(g_begin_session_spy_active_ok[0]);