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:
Rongsong Shen 2026-08-05 14:11:43 +00:00
parent 7494542a60
commit e819a50600
Signed by: rshen
SSH key fingerprint: SHA256:8Vf2pZ3h6rJa7EhGK+WaAQ9Mta/apZvteW6g9U/WKKk
19 changed files with 1761 additions and 115 deletions

View file

@ -434,6 +434,30 @@ sched_record_lifecycle_test = executable(
dependencies: [scheduler_test_deps],
install: false)
# Contract test for the scheduler-side OPCode_schedAdmin request handler
# (scheduler-core spec, task 9.4, Req 5 AC 10): decodes a forwarded
# SchedAdminRequest, dispatches by action to sched_record_start()/
# sched_record_stop()/sched_record_status(), and sends back a
# SchedAdminReply matching requirements.md's field-semantics table.
# recordStatus is proven synchronous (answered directly on the handler
# thread, both idle and while genuinely recording); recordStart/recordStop
# are proven deferred (enqueued, not executed, until a subsequent
# xce_sched_driver_translate_pending_events() drain actually performs the
# real sched_record_start()/sched_record_stop() call and sends the reply).
# Same white-box #include pattern as sched_driver_translate_test
# (core.c/core_clock.c/core_events.c/driver.c) PLUS ../src/recording.c/
# recording_events.c/snapshot.c/snapshot_wire.c (real record start/stop/
# status/write, task 9.1/9.2/9.3) PLUS ../src/handler.c (the code under
# test). xcecli_reply_schedadmin() is a SPY test double, same pattern
# sched_driver_decision_cb_test already establishes for
# sched_publish_dispatch()/sched_publish_pend_reason().
sched_admin_request_test = executable(
'sched_admin_request_test',
['test_admin_request.c'],
include_directories:[incdir],
dependencies: [scheduler_test_deps],
install: false)
# Register as meson tests so `meson test --suite scheduler` runs them all.
# detect_leaks=0 matches the suite convention (fixtures are not torn down).
test('scheduler test', scheduler_test, suite: 'scheduler',
@ -500,3 +524,6 @@ test('scheduler recording path validation test',
test('scheduler record lifecycle test',
sched_record_lifecycle_test, suite: 'scheduler',
env: ['ASAN_OPTIONS=detect_leaks=0'])
test('scheduler admin request test',
sched_admin_request_test, suite: 'scheduler',
env: ['ASAN_OPTIONS=detect_leaks=0'])