From 3224e906834387a4d7b5079909c3f597743d3753 Mon Sep 17 00:00:00 2001 From: Rongsong Shen Date: Sat, 10 Oct 2026 10:28:35 +0000 Subject: [PATCH] fix(scheduler): apply a mid-replay CONFIG_PUSH to the sim limit plugin (bug-1964) Cause: live applies every pushed config through sched_init_config(), whose plugin_config_update() runs each plugin's config_hook (the limit plugin builds its limits there). The simulator seeded the limit plugin only at bootstrap (sim_bootstrap_limit_plugin()); the core's CONFIG_PUSH event loaded groups, resources and queues but never told the plugins, so a limit added during a replay never existed in sim. Fix: one shared fan-out, plugin_config_update_core(core, config), which live's plugin_config_update() now delegates to. The core's CONFIG_PUSH calls a driver-registered xce_core_config_cb_t after loading groups (live order), keeping the core free of plugin.c; the simulator registers the fan-out in sim_bootstrap_limit_plugin(). limit_update_config() reads the honestly-typed plugin->core instead of casting plugin->sched, so the hook is safe on the sim core; the hand-built plugin fixtures in test_limit_plugin.c/test_repl_limit_table.c now set plugin->core as plugin_register() does. test_sim_limit_config_push_parity.c covers it. Co-Authored-By: Claude Opus 5.5 (1M context) --- src/scheduler/include/core.h | 26 ++ src/scheduler/include/plugin.h | 1 + src/scheduler/src/core_events.c | 10 + src/scheduler/src/core_session.c | 16 + src/scheduler/src/plugin.c | 22 +- src/scheduler/src/plugins/limit.c | 8 +- src/scheduler/src/sim_limit.c | 5 + src/scheduler/testsuite/meson.build | 22 ++ src/scheduler/testsuite/test_limit_plugin.c | 3 + .../testsuite/test_repl_limit_table.c | 3 + .../testsuite/test_sim_e2e_limit_visibility.c | 7 +- .../test_sim_limit_config_push_parity.c | 325 ++++++++++++++++++ .../testsuite/test_sim_session_ordering.c | 11 + 13 files changed, 452 insertions(+), 7 deletions(-) create mode 100644 src/scheduler/testsuite/test_sim_limit_config_push_parity.c diff --git a/src/scheduler/include/core.h b/src/scheduler/include/core.h index a7b8b921..f84c9f8e 100644 --- a/src/scheduler/include/core.h +++ b/src/scheduler/include/core.h @@ -89,6 +89,21 @@ typedef int (*xce_core_decision_cb_t)(void*, xce_decision_t*); */ typedef void (*xce_core_cache_clear_cb_t)(xce_sched_core_t*); +/* + * xce_core_config_cb_t - driver-registered plugin config hook (bug-1964): + * called by the core's CONFIG_PUSH event (core_events.c) with the pushed + * config, after the config's named groups are loaded and before its + * resources and queues are applied -- the order live's sched_init_config() + * (scheduler.c) uses. Both drivers hand a pushed config to every + * registered plugin's config_hook through the one plugin.c fan-out, + * plugin_config_update_core(): live calls it from sched_init_config(), the + * simulator registers it here (sim_bootstrap_limit_plugin(), sim_limit.c), + * so a limit added mid-replay reaches the limit plugin as it does live. A + * hook rather than a direct call keeps the core free of plugin.c; NULL + * (the default) means "no plugin to tell". + */ +typedef int (*xce_core_config_cb_t)(xce_sched_core_t*, xce_conf4sched_t*); + /* * xce_unsatisfiable_lookup_cb_t / xce_unsatisfiable_populate_cb_t - * driver-registered, OPTIONAL unsatisfiable-resreq cache consult/populate @@ -197,6 +212,10 @@ struct xce_sched_core_s { * xce_core_cache_clear_cb_t above. NULL until a driver registers one * (xce_core_set_cache_clear_cb()). */ xce_core_cache_clear_cb_t cache_clear_cb; + /* bug-1964: plugin config hook for the CONFIG_PUSH event -- see + * xce_core_config_cb_t above. NULL until a driver registers one + * (xce_core_set_config_cb()). */ + xce_core_config_cb_t config_cb; /* Req 5.2 (scheduler-sim-live-allocation spec, task 1.1): the * unsatisfiable-resreq cache consult/populate seam -- see * xce_unsatisfiable_lookup_cb_t above. NULL until a driver registers @@ -621,6 +640,13 @@ extern int xce_core_set_decision_cb(xce_sched_core_t*, xce_core_decision_cb_t, extern int xce_core_set_cache_clear_cb(xce_sched_core_t*, xce_core_cache_clear_cb_t); +/* + * xce_core_set_config_cb - registers the driver's plugin config hook for + * the CONFIG_PUSH event (bug-1964; see xce_core_config_cb_t). Same calling + * convention as xce_core_set_cache_clear_cb() above; NULL unregisters. + */ +extern int xce_core_set_config_cb(xce_sched_core_t*, xce_core_config_cb_t); + /* * xce_core_begin_session / xce_core_signal_go - Req 5 AC 5 (task 4.4) split * into two exported halves (task 5.2, Req 1 AC 7 / Req 5 AC 9) so a driver diff --git a/src/scheduler/include/plugin.h b/src/scheduler/include/plugin.h index d523b21a..48fe24f9 100644 --- a/src/scheduler/include/plugin.h +++ b/src/scheduler/include/plugin.h @@ -122,6 +122,7 @@ typedef struct { extern int plugin_register(void*, void*); extern void plugin_unregister(void*); extern int plugin_config_update(void*, xce_conf4sched_t*); +extern int plugin_config_update_core(xce_sched_core_t*, xce_conf4sched_t*); extern int plugin_queue_update(void*, xce_sched_queue_t*); extern int plugin_job_update(void*, xce_sched_job_t*); extern int plugin_worker_update(void*, xce_sched_worker_t*); diff --git a/src/scheduler/src/core_events.c b/src/scheduler/src/core_events.c index 6eebaf08..95d9d5b6 100644 --- a/src/scheduler/src/core_events.c +++ b/src/scheduler/src/core_events.c @@ -851,6 +851,16 @@ int xce_core_push_events(xce_sched_core_t* core, xce_core_event_t* events, rc); } + /* bug-1964: then the plugins (the limit plugin builds its + * limits from config->limits), as sched_init_config() does */ + if ((config != NULL) && (core->config_cb != NULL)) { + rc = core->config_cb(core, config); + if (rc < 0) { + WARN("%s CONFIG_PUSH: plugin config update failed rc=%d", + __func__, rc); + } + } + if (config != NULL) { core_apply_config_resources(core, config); core_apply_config_resvals(core, config); diff --git a/src/scheduler/src/core_session.c b/src/scheduler/src/core_session.c index c7368e8b..804364b7 100644 --- a/src/scheduler/src/core_session.c +++ b/src/scheduler/src/core_session.c @@ -103,6 +103,22 @@ int xce_core_set_cache_clear_cb(xce_sched_core_t* core, return 0; } +/* + * xce_core_set_config_cb (bug-1964) - registers the driver-supplied plugin + * config hook the CONFIG_PUSH event calls. See core.h's + * xce_core_config_cb_t doc comment. + */ +int xce_core_set_config_cb(xce_sched_core_t* core, xce_core_config_cb_t cb) +{ + if (core == NULL) { + return -EINVAL; + } + + core->config_cb = cb; + + return 0; +} + /* core_reset_workers - mirrors scheduler.c's sched_reset_workers/ * sched_reset_worker_ (release each available worker's stale per-session * host_eval_ctx snapshot + reset its in-use resource values), parameterized diff --git a/src/scheduler/src/plugin.c b/src/scheduler/src/plugin.c index 58e8afaa..2e1eb2c6 100644 --- a/src/scheduler/src/plugin.c +++ b/src/scheduler/src/plugin.c @@ -247,14 +247,30 @@ void plugin_unregister(void* arg) int plugin_config_update(void* arg, xce_conf4sched_t* config) { xce_scheduler_t* sched = (xce_scheduler_t*)arg; - iterator_t it; - int rc; if (sched == NULL) { return -EINVAL; } - rc = iter_make(&it, sched->core.plugins); + return plugin_config_update_core(&(sched->core), config); +} + +/* + * plugin_config_update_core (bug-1964) - hands `config` to the config_hook + * of every plugin registered on `core`. The one fan-out of both drivers: + * live reaches it through plugin_config_update() (sched_init_config()), + * the simulator through the core's CONFIG_PUSH config_cb (sim_limit.c). + */ +int plugin_config_update_core(xce_sched_core_t* core, xce_conf4sched_t* config) +{ + iterator_t it; + int rc; + + if (core == NULL) { + return -EINVAL; + } + + rc = iter_make(&it, core->plugins); if (rc < 0) { return rc; } diff --git a/src/scheduler/src/plugins/limit.c b/src/scheduler/src/plugins/limit.c index a24ea202..41ad753a 100644 --- a/src/scheduler/src/plugins/limit.c +++ b/src/scheduler/src/plugins/limit.c @@ -128,14 +128,16 @@ static int limit_seed_config_impl(xce_sched_plugin_t* plugin, return 0; } +/* bug-1964: reads plugin->core, set by both plugin_register() and + * plugin_register_core(), not a plugin->sched cast -- the simulator runs + * this hook too now (plugin_config_update_core() from the core's + * CONFIG_PUSH), and its plugins have no xce_scheduler_t behind them. */ 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); + return limit_seed_config_impl(plugin, plugin->core, config); } typedef struct { diff --git a/src/scheduler/src/sim_limit.c b/src/scheduler/src/sim_limit.c index b8a44c78..4d220e94 100644 --- a/src/scheduler/src/sim_limit.c +++ b/src/scheduler/src/sim_limit.c @@ -140,4 +140,9 @@ void sim_bootstrap_limit_plugin(xce_sched_sim_driver_t* sim) * type-punned alternative was not. */ limit_seed_config(plugin, &(sim->core), sim->core.config); } + + /* bug-1964: a later CONFIG_PUSH in the replay reaches the plugins + * (this one's config_hook builds the pushed limits) through the same + * plugin.c fan-out live's sched_init_config() uses */ + xce_core_set_config_cb(&(sim->core), plugin_config_update_core); } diff --git a/src/scheduler/testsuite/meson.build b/src/scheduler/testsuite/meson.build index c805ba15..0e4d445c 100644 --- a/src/scheduler/testsuite/meson.build +++ b/src/scheduler/testsuite/meson.build @@ -3135,6 +3135,28 @@ test('scheduler sim group limit parity test', sched_sim_group_limit_parity_test, suite: 'scheduler', env: ['ASAN_OPTIONS=detect_leaks=0']) +# bug-1964: a CONFIG_PUSH during a replay reaches the simulator's limit +# plugin as the live scheduler's sched_init_config() does. Same +# real-scheduler_srcs link technique as above. +sched_sim_limit_config_push_parity_test_srcs = [ + 'test_sim_limit_config_push_parity.c'] +foreach f : scheduler_srcs + if f != 'src/main.c' + sched_sim_limit_config_push_parity_test_srcs += ['../' + f] + endif +endforeach + +sched_sim_limit_config_push_parity_test = executable( + 'sched_sim_limit_config_push_parity_test', + sched_sim_limit_config_push_parity_test_srcs, + include_directories:[incdir], + dependencies: [scheduler_test_deps], + install: false) + +test('scheduler sim limit config push parity test', + sched_sim_limit_config_push_parity_test, suite: 'scheduler', + env: ['ASAN_OPTIONS=detect_leaks=0']) + # bug-1944: a job moved to another queue (core JOB_UPDATE event) keeps its # own priority and does not overtake jobs already pending in the target # queue. Same real-scheduler_srcs link technique as above. diff --git a/src/scheduler/testsuite/test_limit_plugin.c b/src/scheduler/testsuite/test_limit_plugin.c index 681bff7e..f42d7ffa 100644 --- a/src/scheduler/testsuite/test_limit_plugin.c +++ b/src/scheduler/testsuite/test_limit_plugin.c @@ -129,6 +129,9 @@ static void fixture_init(xce_scheduler_t* sched, xce_sched_plugin_t* plugin) memset(plugin, 0, sizeof(*plugin)); plugin->sched = sched; + /* as plugin_register() sets it; limit_update_config() reads it + * (bug-1964) */ + plugin->core = &(sched->core); rc = limit_init(plugin); TEST_ASSERT_EQUAL_INT(0, rc); diff --git a/src/scheduler/testsuite/test_repl_limit_table.c b/src/scheduler/testsuite/test_repl_limit_table.c index e9985b25..14956cdd 100644 --- a/src/scheduler/testsuite/test_repl_limit_table.c +++ b/src/scheduler/testsuite/test_repl_limit_table.c @@ -522,6 +522,9 @@ static void register_limit_plugin(xce_sched_sim_driver_t* sim, memset(plugin, 0, sizeof(*plugin)); plugin->name = "xce_sched_limit"; plugin->sched = group_sched; + /* as plugin_register() sets it; limit_update_config() reads it + * (bug-1964) */ + plugin->core = &(group_sched->core); rc = limit_init(plugin); TEST_ASSERT_EQUAL_INT(0, rc); diff --git a/src/scheduler/testsuite/test_sim_e2e_limit_visibility.c b/src/scheduler/testsuite/test_sim_e2e_limit_visibility.c index 9ac89b47..c75c8839 100644 --- a/src/scheduler/testsuite/test_sim_e2e_limit_visibility.c +++ b/src/scheduler/testsuite/test_sim_e2e_limit_visibility.c @@ -46,7 +46,12 @@ * from a live CONFIG_PUSH). A configPush replay event would therefore * silently never reach the limit plugin in EITHER mode -- not a * sim-mode-specific gap, just not how limit config ever reaches a plugin - * post-startup in this codebase today. The REAL mechanism (confirmed by + * post-startup in this codebase today. (Update, bug-1964: no longer true. + * Live re-applies every pushed config through sched_apply_pending_config() + * -> sched_init_config(), and the core's CONFIG_PUSH now reaches the + * plugins through the simulator's config_cb, plugin_config_update_core(); + * test_sim_limit_config_push_parity.c covers that path. This fixture keeps + * the snapshot seed.) The REAL mechanism (confirmed by * reading sim_loop.c's sched_sim_driver_run(), sim.c's snapshot-load path, * and sim_limit.c's own header comment) is: a snapshot's `config` field * (SnapshotData.config, a real Conf4Sched capnp field, schema-verified diff --git a/src/scheduler/testsuite/test_sim_limit_config_push_parity.c b/src/scheduler/testsuite/test_sim_limit_config_push_parity.c new file mode 100644 index 00000000..c2e9da65 --- /dev/null +++ b/src/scheduler/testsuite/test_sim_limit_config_push_parity.c @@ -0,0 +1,325 @@ +/* + * test_sim_limit_config_push_parity.c - bug-1964: a CONFIG_PUSH that + * arrives during a replay must reach the limit plugin in the simulator + * exactly as it does in the live scheduler. + * + * Live applies every pushed config through sched_init_config() + * (scheduler.c), whose plugin_config_update() hands it to each plugin's + * config_hook -- the limit plugin builds its limits there. The simulator + * seeds the limit plugin once, from the replay snapshot's config + * (sim_bootstrap_limit_plugin(), sim_limit.c), and applies a later push + * through the core's CONFIG_PUSH event (core_events.c). + * + * Fixture: both drivers start from a config without limits (live: applied + * by sched_init_config(); sim: the replay snapshot's config), then receive + * a second config that adds a host-scoped limit (live: sched_init_config() + * again; sim: a CONFIG_PUSH event through xce_core_push_events(), as + * sim_loop.c replays it). One pending job then runs a round. Both drivers + * must have the limit configured and must match the job against it (one + * usage instance). Limits do not gate admission in this codebase (see + * test_sim_contention_limit_driver_parity.c), so the limit plugin's state + * is what a replay decides with and what `info limit` shows. + * + * Fixture technique and safety conventions as in + * test_sim_group_limit_parity.c (real scheduler_srcs, real picker/ + * allocator threads, `static` fixtures, exactly one RUN_TEST()). + */ +#include +#include +#include +#include +#include + +#include + +#include "controller.capnp.h" +#include "decision.h" +#include "plugin.h" +#include "preason.h" +#include "resobj.h" +#include "sched/types.h" +#include "scheduler.h" +#include "sim.h" +#include "xcelimit.h" +#include "xstring.h" + +extern void sim_bootstrap_picker(xce_sched_sim_driver_t* sim); +extern void sim_bootstrap_limit_plugin(xce_sched_sim_driver_t* sim); +extern void sim_bootstrap_seqjob_allocator(xce_sched_sim_driver_t* sim); +extern void sim_bootstrap_parjob_allocator(xce_sched_sim_driver_t* sim); + +#define WORKER_NAME "cfglim_worker" +#define QUEUE_NAME "normal" +#define TEST_RESREQ ((char*)"select[type==any] rusage[mem=1]") +#define LIMIT_RESNAME "cfglim_host_res" +#define LIMIT_NAME "cfglim_pushed_limit" +#define JOB_USER "user1" +#define JOB_ID ((uint64_t)196401ULL) + +void setUp(void) { return; } +void tearDown(void) { return; } + +static void register_resources(var resmgr) +{ + xce_resource_t res; + + memset(&res, 0, sizeof(res)); + res.name = strdup("slots"); + res.valtype = XCE_VAL_NUMERIC; + res.ordtype = XCE_ORD_DECR; + res.flags = XCE_RESF_LIMIT | XCE_RESF_CONSUMABLE | XCE_RESF_RELEASE + | XCE_RESF_DEFAULT_HOST; + TEST_ASSERT_NOT_NULL(makeResource(resmgr, &res)); + + memset(&res, 0, sizeof(res)); + res.name = strdup(LIMIT_RESNAME); + TEST_ASSERT_NOT_NULL(makeResource(resmgr, &res)); +} + +static void add_worker_resval(xce_sched_worker_t* w, xce_sched_core_t* core, + const char* resname, double value) +{ + xce_resource_t* r = NULL; + xce_val_t* val = NULL; + int rc; + + rc = key_find(core->resmgr, (void*)resname, (void**)&r); + TEST_ASSERT_TRUE(rc >= 0); + TEST_ASSERT_NOT_NULL(r); + + val = (xce_val_t*)calloc(1, sizeof(*val)); + TEST_ASSERT_NOT_NULL(val); + val->which = Value_u_fvalue; + val->u.fvalue = value; + zarray_put(&(w->resvals), r->idx, val); +} + +static void populate(xce_sched_core_t* core, xce_scheduler_t* job_wrapper, + xce_sched_plugin_t* picker) +{ + xce_sched_queue_t* q = (xce_sched_queue_t*)calloc(1, sizeof(*q)); + xce_sched_worker_t* w = (xce_sched_worker_t*)calloc(1, sizeof(*w)); + job_submit_data_t submit; + xce_job_t j; + xce_sched_job_t* job; + int rc; + + TEST_ASSERT_NOT_NULL(q); + q->name = makeString(QUEUE_NAME); + rc = sched_add_queue(core, q); + TEST_ASSERT_TRUE(rc >= 0); + picker->picker_iface->on_queue_update(picker, q); + + TEST_ASSERT_NOT_NULL(w); + w->name = strdup(WORKER_NAME); + w->idx = -1; + zarray_init(&(w->resvals), 128, (zarray_item_killer_t)kill_sched_resval); + zarray_init(&(w->resInUse), 128, (zarray_item_killer_t)kill_sched_resval); + add_worker_resval(w, core, "slots", 4.0); + add_worker_resval(w, core, LIMIT_RESNAME, 1000.0); + rc = sched_add_worker(core, w); + TEST_ASSERT_TRUE(rc >= 0); + + memset(&submit, 0, sizeof(submit)); + submit.queue = (char*)QUEUE_NAME; + submit.user = (char*)JOB_USER; + submit.project_name = (char*)"default"; + submit.resreq = TEST_RESREQ; + submit.mintasks = 1; + submit.maxtasks = 1; + + memset(&j, 0, sizeof(j)); + j.jobid = JOB_ID; + j.state = JOB_STAT_PEND; + j.sub_time = 1000ULL; + j.queue_time = 1000ULL; + j.submit = &submit; + + job = make_sched_job(job_wrapper, &j); + TEST_ASSERT_NOT_NULL(job); + job->schedobj->u.primary->resreq->minTasks = 1; + job->schedobj->u.primary->resreq->maxTasks = 1; + rc = sched_add_job(core, job); + TEST_ASSERT_TRUE(rc >= 0); + picker->picker_iface->on_job_push(picker, job); +} + +static void run_round(xce_sched_core_t* core) +{ + pthread_t picker_tid; + pthread_t allocator_tid; + xce_decision_t* d; + int rc; + + rc = pthread_create(&picker_tid, NULL, sched_job_picker, core); + TEST_ASSERT_EQUAL_INT(0, rc); + rc = pthread_create(&allocator_tid, NULL, sched_allocator, core); + TEST_ASSERT_EQUAL_INT(0, rc); + + rc = xce_core_go(core); + TEST_ASSERT_EQUAL_INT(0, rc); + + do { + d = NULL; + cbuf_get(&(core->decisions), &d); + } while (d != NULL); + + rc = xce_core_end_session(core); + TEST_ASSERT_EQUAL_INT(0, rc); +} + +static int count_entries(var handle) +{ + iterator_t it; + void* item = NULL; + int count = 0; + int rc; + + if (handle == NULL) { + return 0; + } + + rc = iter_make(&it, handle); + if (rc < 0) { + return 0; + } + + for (;;) { + rc = iter_next(&it, &item); + if ((rc < 0) || (rc == RTTI_ITER_NO_DATA)) { + break; + } + count++; + } + + iter_destroy(&it); + + return count; +} + +static xce_scheduler_t g_normal; +static xce_sched_sim_driver_t g_sim; +static xce_scheduler_t g_sim_job_wrapper; + +/* the pushed config: `static`, since no core here is handed a newer one + * after it, so it is never freed. The first config each driver gets is + * heap-allocated and empty, because applying the pushed one frees it. */ +static xce_limit_val_t g_limit_val = { .resname = (char*)LIMIT_RESNAME, + .percent = 1, + .val = 50.0 }; +static xce_limit_config_t g_limit_cfg = { .name = (char*)LIMIT_NAME, + .val = &g_limit_val }; +static xce_limit_config_t* g_limits[1] = { &g_limit_cfg }; + +static xce_conf4sched_t g_pushed_normal; +static xce_conf4sched_t g_pushed_sim; + +static void init_pushed_config(xce_conf4sched_t* config) +{ + memset(config, 0, sizeof(*config)); + config->nlimits = 1; + config->limits = g_limits; +} + +static xce_conf4sched_t* new_empty_config(void) +{ + xce_conf4sched_t* config = (xce_conf4sched_t*)calloc(1, sizeof(*config)); + + TEST_ASSERT_NOT_NULL(config); + return config; +} + +static void test_config_push_reaches_limit_plugin_in_sim_as_in_live(void) +{ + xce_sched_plugin_t* fcfs_normal = NULL; + xce_sched_plugin_t* fcfs_sim = NULL; + sched_snapshot_data_t snap; + xce_core_event_t ev; + int rc; + + init_pushed_config(&g_pushed_normal); + init_pushed_config(&g_pushed_sim); + + /* ---- live: both configs applied by sched_init_config() ---- */ + memset(&g_normal, 0, sizeof(g_normal)); + rc = xce_sched_core_init(&(g_normal.core)); + TEST_ASSERT_EQUAL_INT(0, rc); + register_resources(g_normal.core.resmgr); + + fcfs_normal = builtin_fcfs_new_plugin(); + TEST_ASSERT_NOT_NULL(fcfs_normal); + rc = plugin_register(&g_normal, fcfs_normal); + TEST_ASSERT_TRUE(rc >= 0); + g_normal.core.job_pick_plugin = (char*)"builtin_fcfs"; + rc = plugin_register(&g_normal, limit_new_plugin()); + TEST_ASSERT_TRUE(rc >= 0); + rc = register_seqjob_allocator(&g_normal); + TEST_ASSERT_TRUE(rc >= 0); + rc = register_parjob_allocator(&g_normal); + TEST_ASSERT_TRUE(rc >= 0); + + rc = sched_init_config(&g_normal, new_empty_config()); + TEST_ASSERT_EQUAL_INT(0, rc); + rc = sched_init_config(&g_normal, &g_pushed_normal); + TEST_ASSERT_EQUAL_INT(0, rc); + + populate(&(g_normal.core), &g_normal, fcfs_normal); + run_round(&(g_normal.core)); + + /* ---- sim: the snapshot's config, the bootstrap order of + * sim_driver_run_body() (sim_loop.c), then the replayed push ---- */ + memset(&g_sim, 0, sizeof(g_sim)); + rc = xce_sched_core_init(&(g_sim.core)); + TEST_ASSERT_EQUAL_INT(0, rc); + register_resources(g_sim.core.resmgr); + + memset(&snap, 0, sizeof(snap)); + snap.config = new_empty_config(); + rc = xce_sched_core_load_snapshot(&(g_sim.core), &snap); + TEST_ASSERT_TRUE(rc >= 0); + + sim_bootstrap_picker(&g_sim); + rc = key_find(g_sim.core.plugins, "builtin_fcfs", (void**)&fcfs_sim); + TEST_ASSERT_TRUE(rc >= 0); + TEST_ASSERT_NOT_NULL(fcfs_sim); + sim_bootstrap_limit_plugin(&g_sim); + sim_bootstrap_seqjob_allocator(&g_sim); + sim_bootstrap_parjob_allocator(&g_sim); + + memset(&ev, 0, sizeof(ev)); + ev.type = XCE_CORE_EVENT_CONFIG_PUSH; + ev.u.config_push.config = &g_pushed_sim; + rc = xce_core_push_events(&(g_sim.core), &ev, 1); + TEST_ASSERT_TRUE(rc >= 0); + + memset(&g_sim_job_wrapper, 0, sizeof(g_sim_job_wrapper)); + rc = xce_sched_core_init(&(g_sim_job_wrapper.core)); + TEST_ASSERT_EQUAL_INT(0, rc); + g_sim_job_wrapper.core.resmgr = g_sim.core.resmgr; + g_sim_job_wrapper.core.workers = g_sim.core.workers; + g_sim_job_wrapper.core.queues = g_sim.core.queues; + + populate(&(g_sim.core), &g_sim_job_wrapper, fcfs_sim); + run_round(&(g_sim.core)); + + TEST_ASSERT_EQUAL_INT_MESSAGE( + 1, count_entries(limit_get_configured_limits(&(g_normal.core))), + "live: the pushed config's limit must be configured"); + TEST_ASSERT_EQUAL_INT_MESSAGE( + 1, count_entries(limit_get_usage_instances(&(g_normal.core))), + "live: the pushed limit must match the job"); + TEST_ASSERT_EQUAL_INT_MESSAGE( + 1, count_entries(limit_get_configured_limits(&(g_sim.core))), + "sim: a CONFIG_PUSH during replay must configure its limit as live " + "does"); + TEST_ASSERT_EQUAL_INT_MESSAGE( + 1, count_entries(limit_get_usage_instances(&(g_sim.core))), + "sim: the pushed limit must match the job as it does live"); +} + +int main(void) +{ + UNITY_BEGIN(); + RUN_TEST(test_config_push_reaches_limit_plugin_in_sim_as_in_live); + return UNITY_END(); +} diff --git a/src/scheduler/testsuite/test_sim_session_ordering.c b/src/scheduler/testsuite/test_sim_session_ordering.c index 9a75aef4..97dc2017 100644 --- a/src/scheduler/testsuite/test_sim_session_ordering.c +++ b/src/scheduler/testsuite/test_sim_session_ordering.c @@ -234,6 +234,17 @@ int limit_seed_config(xce_sched_plugin_t* plugin, xce_sched_core_t* core, return 0; } +/* plugin_config_update_core() (plugin.c, not part of this TU) -- bug-1964: + * sim_bootstrap_limit_plugin() registers it as the core's CONFIG_PUSH + * config_cb, after the limit plugin is registered -- unreachable here for + * the same reason as limit_seed_config() immediately above. */ +int plugin_config_update_core(xce_sched_core_t* core, xce_conf4sched_t* config) +{ + (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; }