feat(scheduler-core): implement scheduler-side admin request handler (task 9.4)
Implements the scheduler's first request-reply opcode (OPCode_schedAdmin). sched_handle_admin_request() (handler.c) decodes SchedAdminRequest and dispatches by action: recordStatus answers synchronously (pure read); recordStart/recordStop are deferred onto the existing pend_updates queue (XCE_SCHED_OP_ADMIN_RECORD) since sched_record_start()'s snapshot call requires the session_active==0 precondition only guaranteed on the driver thread, not the async request-handling thread. driver.c's xce_sched_driver_translate_pending_events() performs the deferred action and sends the reply via a new xcecli_reply_schedadmin() (client.c/client.h), correlated by seq copied before the original request is freed. Adversarial review found a genuine cross-thread race: sched_record_status() read driver-owned recording state (including a pointer the driver thread frees) from the vtask thread with no lock -- the codebase's first such race, missed by ASan (doesn't detect races). Fixed with a dedicated record_lock scoped strictly to in-memory field access, never spanning file I/O; sched_record_status() now strdup()s under the lock instead of returning a borrowed live pointer. Verified with a real two-thread lock-contention test (independently confirmed non-vacuous by removing the lock and observing the test fail) plus a deterministic use-after-free reproduction. 26/26 scheduler suite, clang-format clean.
This commit is contained in:
parent
7494542a60
commit
e819a50600
19 changed files with 1761 additions and 115 deletions
|
|
@ -27,14 +27,30 @@
|
|||
* test_job_array_expand.c for the real kvmgr-backed sched_get_job). */
|
||||
int sched_init_config(xce_scheduler_t* s, xce_conf4sched_t* c)
|
||||
{
|
||||
(void)s; (void)c; return 0;
|
||||
(void)s;
|
||||
(void)c;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* sched_get_job relocated to core.h (scheduler-core spec, task 4.1) -- now
|
||||
* operates on xce_sched_core_t*, not xce_scheduler_t*. */
|
||||
xce_sched_job_t* sched_get_job(xce_sched_core_t* sched, uint64_t jobid)
|
||||
{
|
||||
(void)sched; (void)jobid; return NULL;
|
||||
(void)sched;
|
||||
(void)jobid;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* sched_record_status (recording.c, scheduler-core spec, task 9.4) --
|
||||
* handler.c's OPCode_schedAdmin/recordStatus path references it; this file
|
||||
* never exercises OPCode_schedAdmin (its own scope is notify/dataset
|
||||
* enqueue), so a link-satisfying stub is enough, same rationale as
|
||||
* sched_init_config above. */
|
||||
int sched_record_status(xce_scheduler_t* sched, sched_admin_reply_t* reply)
|
||||
{
|
||||
(void)sched;
|
||||
(void)reply;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
#include "../src/handler.c"
|
||||
|
|
@ -118,8 +134,7 @@ static void test_handle_job_rusage_enqueues_rusage(void)
|
|||
xce_sched_op_t op = (xce_sched_op_t)-1;
|
||||
fixture_init(&sched);
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(-EINVAL,
|
||||
sched_handle_sched_job_rusage(&sched, NULL));
|
||||
TEST_ASSERT_EQUAL_INT(-EINVAL, sched_handle_sched_job_rusage(&sched, NULL));
|
||||
|
||||
xce_job_rusages_t* ru = (xce_job_rusages_t*)calloc(1, sizeof(*ru));
|
||||
ru->jobid = 7;
|
||||
|
|
@ -132,20 +147,18 @@ static void test_handle_job_rusage_enqueues_rusage(void)
|
|||
static void test_handle_update_resvals_skips_empty_enqueues_nonempty(void)
|
||||
{
|
||||
xce_scheduler_t sched;
|
||||
xce_sched_op_t op = (xce_sched_op_t)-1;
|
||||
xce_sched_op_t op = (xce_sched_op_t)-1;
|
||||
xce_resval_list_t empty = { .count = 0, .vals = NULL };
|
||||
xce_resval_list_t full = { .count = 1, .vals = NULL };
|
||||
fixture_init(&sched);
|
||||
|
||||
/* NULL or count==0 -> skip (return 0, nothing enqueued) */
|
||||
TEST_ASSERT_EQUAL_INT(0, sched_handle_sched_update_resvals(&sched, NULL));
|
||||
TEST_ASSERT_EQUAL_INT(0,
|
||||
sched_handle_sched_update_resvals(&sched, &empty));
|
||||
TEST_ASSERT_EQUAL_INT(0, sched_handle_sched_update_resvals(&sched, &empty));
|
||||
TEST_ASSERT_EQUAL_INT(0, pop_op(&sched, NULL));
|
||||
|
||||
/* count>0 -> enqueue UPDATE_RESVALS */
|
||||
TEST_ASSERT_EQUAL_INT(0,
|
||||
sched_handle_sched_update_resvals(&sched, &full));
|
||||
TEST_ASSERT_EQUAL_INT(0, sched_handle_sched_update_resvals(&sched, &full));
|
||||
TEST_ASSERT_EQUAL_INT(1, pop_op(&sched, &op));
|
||||
TEST_ASSERT_EQUAL_INT(XCE_SCHED_OP_UPDATE_RESVALS, op);
|
||||
}
|
||||
|
|
@ -154,9 +167,9 @@ static void test_handle_update_resvals_skips_empty_enqueues_nonempty(void)
|
|||
|
||||
static void test_handle_resources_skips_empty_enqueues_nonempty(void)
|
||||
{
|
||||
xce_scheduler_t sched;
|
||||
xce_sched_op_t op = (xce_sched_op_t)-1;
|
||||
xce_resource_t* one[1];
|
||||
xce_scheduler_t sched;
|
||||
xce_sched_op_t op = (xce_sched_op_t)-1;
|
||||
xce_resource_t* one[1];
|
||||
fixture_init(&sched);
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(0, sched_handle_resources(&sched, 0, NULL));
|
||||
|
|
@ -188,7 +201,8 @@ static void test_handle_resvals_full_skips_empty_enqueues_nonempty(void)
|
|||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
(void)argc; (void)argv;
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
UNITY_BEGIN();
|
||||
RUN_TEST(test_handle_update_job_enqueues_job_fields, 0);
|
||||
RUN_TEST(test_handle_update_queue_enqueues_queue, 0);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue