feat(scheduler-core): wire decision publishing and mode-selection into main.c (task 5.2)

Rewires main.c's live scheduling loop onto the new core-driven
architecture: xce_sched_driver_translate_pending_events() (task 5.1)
-> xce_core_begin_session() -> plugin_begin_session()+sched_account_jobs()
-> xce_core_signal_go() -> decision-drain via the registered
xce_core_decision_cb_t -> plugin_end_session() -> xce_core_end_session(),
all in the new exported xce_sched_driver_run_normal_mode_round()
(driver.c), replacing the old inline sched_update()+sched_begin_session()
+sched_go()+switch+sched_end_session() sequence.

xce_core_go() (task 4.4) is split into xce_core_begin_session()/
xce_core_signal_go() -- a pure decomposition (xce_core_go() itself is
now defined in terms of both, task 4.4's approved test suite passes
unmodified) -- specifically so the driver can interleave
plugin_begin_session()/sched_account_jobs() in the exact safety-critical
window task 4.4 deliberately excluded from pure-core scope: after the
resval reset, strictly before the picker thread is signaled. This is
empirically proven by test_driver_session_ordering.c via real
cross-thread happens-before assertions, not just documented.

xce_sched_driver_on_decision() is the new registered callback:
DISPATCH/PEND forward to the existing sched_publish_dispatch()/
sched_publish_pend_reason(); PREEMPT fails loudly (-ENOSYS, logged)
since no xcecli_preempt() client API exists and nothing in the live
allocator produces one yet (scheduler-preemption's territory).

Adds a --sim CLI flag (parse-only, loud not-yet-implemented failure,
since SimDriver is Phase 3).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019dw5WnN3DXStaEaPYfACU1
This commit is contained in:
Rongsong Shen 2026-08-04 08:05:57 +00:00
parent dceafc6b12
commit 062cf40d29
Signed by: rshen
SSH key fingerprint: SHA256:8Vf2pZ3h6rJa7EhGK+WaAQ9Mta/apZvteW6g9U/WKKk
12 changed files with 1537 additions and 53 deletions

View file

@ -315,6 +315,61 @@ sched_driver_translate_test = executable(
dependencies: [scheduler_test_deps],
install: false)
# Contract test for the normal-mode driver's decision-publish callback
# (scheduler-core spec, task 5.2, Req 5 AC 6 / AC 9's publishing half):
# xce_sched_driver_on_decision() forwards DISPATCH/PEND decisions to
# sched_publish_dispatch()/sched_publish_pend_reason() exactly once each,
# with the same single-item njobs=1/nreasons=1 wrapping; PREEMPT fails
# loudly (-ENOSYS, no publish) rather than fabricating a nonexistent
# xcecli_preempt() call; NULL driver_ctx/decision each return -EINVAL, no
# publish. Same white-box #include pattern as test_driver_translate.c
# (pulls ../src/core.c, ../src/core_clock.c, ../src/core_events.c, and the
# new ../src/driver.c into this TU) PLUS ../src/decision.c for the real
# make_dispatch_decision()/make_pend_decision()/make_preempt_decision()
# constructors this file's fixtures use. sched_publish_dispatch()/
# sched_publish_pend_reason() are SPY test doubles (this file never links
# scheduler.c), same rationale as test_driver_translate.c's own stubs.
sched_driver_decision_cb_test = executable(
'sched_driver_decision_cb_test',
['test_driver_decision_cb.c'],
include_directories:[incdir],
dependencies: [scheduler_test_deps],
install: false)
# Safety-property proof for the begin/end-session ordering seam task 5.2
# introduced (Req 1 AC 7 / Req 5 AC 9): xce_sched_driver_run_normal_mode_round()
# (driver.c) must call plugin_begin_session()/sched_account_jobs() strictly
# AFTER xce_core_begin_session()'s resval reset and strictly BEFORE
# xce_core_signal_go() wakes the picker thread -- otherwise the picker could
# observe a session whose resource-in-use accounting was reset to zero but
# not yet re-summed from running jobs. Drives the REAL
# xce_sched_driver_run_normal_mode_round() (white-box #include of
# ../src/driver.c, same TU as test_driver_translate.c/
# test_driver_decision_cb.c, PLUS ../src/core_session.c for the real
# xce_core_begin_session()/xce_core_signal_go()/xce_core_end_session()
# under test) against a real picker pthread (same design as
# test_core_full_cycle.c's test_picker_thread_fn -- see that file's own
# header comment for the full "why a real thread, not a sequential drain"
# rationale: jbuf's hardcoded capacity-1 rendezvous requires one). Uses the
# REAL plugin_begin_session()/plugin_end_session() (../src/plugin.c) driven
# through a genuinely test-registered xce_sched_plugin_t whose
# begin_session_fn/end_session_fn set/check an ordering flag -- per this
# task's own suggested technique, this is a stronger proof than spying on
# plugin_begin_session() itself. sched_account_jobs() (scheduler.c,
# xce_scheduler_t*-coupled, pulls in resource.c's sched_charge_account())
# is instead a SPY test double recording an order index -- this test's
# target is the DRIVER's ordering discipline, not sched_account_jobs()'s
# own resource-accounting correctness (out of this task's scope, same
# "spy the xce_scheduler_t*-coupled collaborator" rationale
# test_driver_decision_cb.c/test_driver_translate.c already established for
# sched_publish_dispatch()/sched_publish_pend_reason()).
sched_driver_session_ordering_test = executable(
'sched_driver_session_ordering_test',
['test_driver_session_ordering.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',
@ -366,3 +421,9 @@ test('scheduler core full cycle test',
test('scheduler driver translate test',
sched_driver_translate_test, suite: 'scheduler',
env: ['ASAN_OPTIONS=detect_leaks=0'])
test('scheduler driver decision cb test',
sched_driver_decision_cb_test, suite: 'scheduler',
env: ['ASAN_OPTIONS=detect_leaks=0'])
test('scheduler driver session ordering test',
sched_driver_session_ordering_test, suite: 'scheduler',
env: ['ASAN_OPTIONS=detect_leaks=0'])