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) <noreply@anthropic.com>
This commit is contained in:
Rongsong Shen 2026-10-10 10:28:35 +00:00
parent 2cd02cdc8c
commit 3224e90683
Signed by untrusted user: rshen
GPG key ID: 80533536B0EB651C
13 changed files with 452 additions and 7 deletions

View file

@ -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*); 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 - * xce_unsatisfiable_lookup_cb_t / xce_unsatisfiable_populate_cb_t -
* driver-registered, OPTIONAL unsatisfiable-resreq cache consult/populate * 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_cache_clear_cb_t above. NULL until a driver registers one
* (xce_core_set_cache_clear_cb()). */ * (xce_core_set_cache_clear_cb()). */
xce_core_cache_clear_cb_t 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 /* Req 5.2 (scheduler-sim-live-allocation spec, task 1.1): the
* unsatisfiable-resreq cache consult/populate seam -- see * unsatisfiable-resreq cache consult/populate seam -- see
* xce_unsatisfiable_lookup_cb_t above. NULL until a driver registers * 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*, extern int xce_core_set_cache_clear_cb(xce_sched_core_t*,
xce_core_cache_clear_cb_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 * 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 * into two exported halves (task 5.2, Req 1 AC 7 / Req 5 AC 9) so a driver

View file

@ -122,6 +122,7 @@ typedef struct {
extern int plugin_register(void*, void*); extern int plugin_register(void*, void*);
extern void plugin_unregister(void*); extern void plugin_unregister(void*);
extern int plugin_config_update(void*, xce_conf4sched_t*); 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_queue_update(void*, xce_sched_queue_t*);
extern int plugin_job_update(void*, xce_sched_job_t*); extern int plugin_job_update(void*, xce_sched_job_t*);
extern int plugin_worker_update(void*, xce_sched_worker_t*); extern int plugin_worker_update(void*, xce_sched_worker_t*);

View file

@ -851,6 +851,16 @@ int xce_core_push_events(xce_sched_core_t* core, xce_core_event_t* events,
rc); 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) { if (config != NULL) {
core_apply_config_resources(core, config); core_apply_config_resources(core, config);
core_apply_config_resvals(core, config); core_apply_config_resvals(core, config);

View file

@ -103,6 +103,22 @@ int xce_core_set_cache_clear_cb(xce_sched_core_t* core,
return 0; 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/ /* core_reset_workers - mirrors scheduler.c's sched_reset_workers/
* sched_reset_worker_ (release each available worker's stale per-session * sched_reset_worker_ (release each available worker's stale per-session
* host_eval_ctx snapshot + reset its in-use resource values), parameterized * host_eval_ctx snapshot + reset its in-use resource values), parameterized

View file

@ -247,14 +247,30 @@ void plugin_unregister(void* arg)
int plugin_config_update(void* arg, xce_conf4sched_t* config) int plugin_config_update(void* arg, xce_conf4sched_t* config)
{ {
xce_scheduler_t* sched = (xce_scheduler_t*)arg; xce_scheduler_t* sched = (xce_scheduler_t*)arg;
iterator_t it;
int rc;
if (sched == NULL) { if (sched == NULL) {
return -EINVAL; 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) { if (rc < 0) {
return rc; return rc;
} }

View file

@ -128,14 +128,16 @@ static int limit_seed_config_impl(xce_sched_plugin_t* plugin,
return 0; 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, static int limit_update_config(xce_sched_plugin_t* plugin,
xce_conf4sched_t* config) xce_conf4sched_t* config)
{ {
xce_scheduler_t* sched = (xce_scheduler_t*)plugin->sched;
TRACE("%s ...", __func__); TRACE("%s ...", __func__);
return limit_seed_config_impl(plugin, &(sched->core), config); return limit_seed_config_impl(plugin, plugin->core, config);
} }
typedef struct { typedef struct {

View file

@ -140,4 +140,9 @@ void sim_bootstrap_limit_plugin(xce_sched_sim_driver_t* sim)
* type-punned alternative was not. */ * type-punned alternative was not. */
limit_seed_config(plugin, &(sim->core), sim->core.config); 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);
} }

View file

@ -3135,6 +3135,28 @@ test('scheduler sim group limit parity test',
sched_sim_group_limit_parity_test, suite: 'scheduler', sched_sim_group_limit_parity_test, suite: 'scheduler',
env: ['ASAN_OPTIONS=detect_leaks=0']) 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 # 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 # own priority and does not overtake jobs already pending in the target
# queue. Same real-scheduler_srcs link technique as above. # queue. Same real-scheduler_srcs link technique as above.

View file

@ -129,6 +129,9 @@ static void fixture_init(xce_scheduler_t* sched, xce_sched_plugin_t* plugin)
memset(plugin, 0, sizeof(*plugin)); memset(plugin, 0, sizeof(*plugin));
plugin->sched = sched; plugin->sched = sched;
/* as plugin_register() sets it; limit_update_config() reads it
* (bug-1964) */
plugin->core = &(sched->core);
rc = limit_init(plugin); rc = limit_init(plugin);
TEST_ASSERT_EQUAL_INT(0, rc); TEST_ASSERT_EQUAL_INT(0, rc);

View file

@ -522,6 +522,9 @@ static void register_limit_plugin(xce_sched_sim_driver_t* sim,
memset(plugin, 0, sizeof(*plugin)); memset(plugin, 0, sizeof(*plugin));
plugin->name = "xce_sched_limit"; plugin->name = "xce_sched_limit";
plugin->sched = group_sched; 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); rc = limit_init(plugin);
TEST_ASSERT_EQUAL_INT(0, rc); TEST_ASSERT_EQUAL_INT(0, rc);

View file

@ -46,7 +46,12 @@
* from a live CONFIG_PUSH). A configPush replay event would therefore * from a live CONFIG_PUSH). A configPush replay event would therefore
* silently never reach the limit plugin in EITHER mode -- not a * silently never reach the limit plugin in EITHER mode -- not a
* sim-mode-specific gap, just not how limit config ever reaches a plugin * 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, * 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 * and sim_limit.c's own header comment) is: a snapshot's `config` field
* (SnapshotData.config, a real Conf4Sched capnp field, schema-verified * (SnapshotData.config, a real Conf4Sched capnp field, schema-verified

View file

@ -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 <errno.h>
#include <pthread.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unity/unity.h>
#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();
}

View file

@ -234,6 +234,17 @@ int limit_seed_config(xce_sched_plugin_t* plugin, xce_sched_core_t* core,
return 0; 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 /* core_reset_workers()'s (core_session.c) linker dependency -- never
* invoked (no worker is ever added here). */ * invoked (no worker is ever added here). */
void sched_worker_release_host_ctx(xce_sched_worker_t* w) { (void)w; } void sched_worker_release_host_ctx(xce_sched_worker_t* w) { (void)w; }