fix(scheduler): repeat WORKERS_PUSH with a NULL resval clobbers good values (bug-1688)
info worker all/<name> in the interactive/sim REPL initially printed full per-resource values correctly, then lost them after a later WORKERS_PUSH event replayed. core_apply_worker_resvals()'s per-resval loop only guarded against nv==NULL/nv->name==NULL; a resval entry with a NULL nv->val (a legitimate "no update this round" marker some producers send) fell through and overwrote the worker's existing good value with nothing, instead of being skipped -- same defensive pattern already used elsewhere in this function, just missing here. A related count-doubling gap in the same event path is fixed by switching an iteration bound from `.count` to `.caps`. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AgYRU3zDTqNGV5Jk8aiKLg
This commit is contained in:
parent
47532f0a76
commit
0ebf3a817f
4 changed files with 286 additions and 3 deletions
|
|
@ -927,6 +927,112 @@ static void test_info_worker_reports_string_and_boolean_resource_values(void)
|
|||
"PASS\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* bug-1688: repl_info_worker_one()'s resource-print loop used
|
||||
* `for (i = 0; i < w->resvals.count; i++)` -- treating `.count` (number of
|
||||
* currently-populated slots) as a safe index upper bound for iterating the
|
||||
* zarray BY INDEX. `.count` can legitimately be less than the highest
|
||||
* occupied index once entries are sparse/non-contiguous (zarray_put_ex()'s
|
||||
* count bookkeeping, zarray.c, only tracks "how many slots read NULL at
|
||||
* write time" -- it is not recomputed from the array's actual contents).
|
||||
* `.caps` (allocated capacity, monotonic, never shrinks) is this codebase's
|
||||
* own established convention for this identical structure -- see
|
||||
* resource.c:228's `int num_res = (int)w->resvals.caps;`. Regression: a
|
||||
* worker whose resvals zarray has TWO populated slots (idx 0 "mem", idx 1
|
||||
* "r1m") but whose `.count` field has been driven down to 1 (the exact
|
||||
* post-corruption shape bug-1688's OTHER root cause -- the NULL-nv->val
|
||||
* WORKERS_PUSH overwrite, core_events.c -- can leave resvals in, and
|
||||
* reproducible directly here without needing that whole mechanism) must
|
||||
* still show BOTH resource lines, not silently truncate to the first one.
|
||||
*/
|
||||
static void test_info_worker_shows_all_populated_resources_when_count_is_stale(
|
||||
void)
|
||||
{
|
||||
xce_sched_sim_driver_t sim;
|
||||
xce_core_event_t events[1];
|
||||
xce_resource_t wire_mem;
|
||||
xce_resource_t wire_r1m;
|
||||
xce_worker_t wire_worker;
|
||||
xce_worker_t* worker_ptrs[1];
|
||||
xce_val_t val_mem;
|
||||
xce_val_t val_r1m;
|
||||
xce_name_val_t nv_mem;
|
||||
xce_name_val_t nv_r1m;
|
||||
xce_name_val_t* nv_ptrs[2];
|
||||
xce_sched_worker_t* sw;
|
||||
char* captured;
|
||||
int rc;
|
||||
|
||||
memset(&sim, 0, sizeof(sim));
|
||||
rc = xce_sched_core_init(&(sim.core));
|
||||
TEST_ASSERT_EQUAL_INT(0, rc);
|
||||
|
||||
memset(&wire_mem, 0, sizeof(wire_mem));
|
||||
wire_mem.name = "mem";
|
||||
TEST_ASSERT_NOT_NULL(makeResource(sim.core.resmgr, &wire_mem));
|
||||
memset(&wire_r1m, 0, sizeof(wire_r1m));
|
||||
wire_r1m.name = "r1m";
|
||||
TEST_ASSERT_NOT_NULL(makeResource(sim.core.resmgr, &wire_r1m));
|
||||
|
||||
memset(&val_mem, 0, sizeof(val_mem));
|
||||
val_mem.which = Value_u_fvalue;
|
||||
val_mem.u.fvalue = 100.0;
|
||||
memset(&nv_mem, 0, sizeof(nv_mem));
|
||||
nv_mem.name = "mem";
|
||||
nv_mem.val = &val_mem;
|
||||
|
||||
memset(&val_r1m, 0, sizeof(val_r1m));
|
||||
val_r1m.which = Value_u_fvalue;
|
||||
val_r1m.u.fvalue = 5.0;
|
||||
memset(&nv_r1m, 0, sizeof(nv_r1m));
|
||||
nv_r1m.name = "r1m";
|
||||
nv_r1m.val = &val_r1m;
|
||||
|
||||
nv_ptrs[0] = &nv_mem;
|
||||
nv_ptrs[1] = &nv_r1m;
|
||||
|
||||
memset(&wire_worker, 0, sizeof(wire_worker));
|
||||
wire_worker.name = "hostA";
|
||||
wire_worker.status = 0;
|
||||
wire_worker.nresvals_to_sched = 2;
|
||||
wire_worker.resvals_to_sched = nv_ptrs;
|
||||
worker_ptrs[0] = &wire_worker;
|
||||
|
||||
memset(events, 0, sizeof(events));
|
||||
events[0].type = XCE_CORE_EVENT_WORKERS_PUSH;
|
||||
events[0].u.workers_push.nworkers = 1;
|
||||
events[0].u.workers_push.workers = worker_ptrs;
|
||||
|
||||
rc = xce_core_push_events(&(sim.core), events, 1);
|
||||
TEST_ASSERT_EQUAL_INT(0, rc);
|
||||
|
||||
sw = sched_get_worker(&(sim.core), "hostA");
|
||||
TEST_ASSERT_NOT_NULL(sw);
|
||||
TEST_ASSERT_EQUAL_INT(2, sw->resvals.count);
|
||||
|
||||
/* Directly force the sparse-vs-stale-count shape: both idx 0 ("mem")
|
||||
* and idx 1 ("r1m") stay populated, but .count now under-reports the
|
||||
* true occupied high-water mark -- exactly what a `.count`-bound loop
|
||||
* gets wrong and a `.caps`-bound loop (this fix) does not. */
|
||||
sw->resvals.count = 1;
|
||||
|
||||
captured = capture_dispatch(&sim, "info worker hostA");
|
||||
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(
|
||||
strstr(captured, "resource mem: value=100.00"),
|
||||
"the first populated slot must still display");
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(
|
||||
strstr(captured, "resource r1m: value=5.00"),
|
||||
"a populated slot at an index >= the (stale) .count must still "
|
||||
"display -- iterating by .count instead of .caps (bug-1688) "
|
||||
"silently truncates output here");
|
||||
|
||||
free(captured);
|
||||
|
||||
printf("test_info_worker_shows_all_populated_resources_when_count_is_"
|
||||
"stale: PASS\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* bug-1590: repl_info_resource_one() (the "info resource <name>"
|
||||
* aggregate-across-workers command) had the same defect one layer up --
|
||||
|
|
@ -1880,6 +1986,7 @@ int main(void)
|
|||
RUN_TEST(test_info_worker_reports_resource_values);
|
||||
RUN_TEST(test_info_worker_reports_consumable_value_and_used);
|
||||
RUN_TEST(test_info_worker_reports_string_and_boolean_resource_values);
|
||||
RUN_TEST(test_info_worker_shows_all_populated_resources_when_count_is_stale);
|
||||
RUN_TEST(test_info_resource_reports_string_value);
|
||||
RUN_TEST(test_info_resource_reports_consumable_value_and_used);
|
||||
RUN_TEST(test_info_resource_all_lists_only_global_shared_resources);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue