merge: sync Rongsong's scheduler-framework (c5b172c7..3442a57d, 781 commits)
Merge origin/scheduler-framework into merge-sf-20260903 (97 commits on ours: bug-1490/1494/1498/1509/1513/1521/1522 fixes, PEND-reason P1, scheduler-agent Phase 2, bug-924 owner-pid shim). Bookkeeping excluded per policy: .kiro/, .wolf/, CHANGES.md, reports/ (92 files dropped); .claude/ + CLAUDE.md keep ours. Key theirs-side changes absorbed: - bug-1938/1960: QUEUE_STAT_CLOSED no longer gates dispatch (closed queue refuses new submissions only; pending jobs still run) -- supersedes our QUEUE_CLOSED preason on the dispatch path; sched_queue_status_reason drops that branch - bug-1846: config push now stashes and applies between scheduling rounds (sched_stash_config/sched_apply_pending_config), applied at the same main-thread round boundary that preserves bug-1513 serialization - bug-1760: sched_check_pending_rejoin exposed + xcecli_is_connection_broken public predicate - bug-1936: execuser/exechome/execcwd/exdata carried on submission (superset of our bug-1500 execuser fallback; our job-typed resolveExecUser removed as dead code) - bug-1945~1962: apiserver auth hardening (HS256 pinning, default- secret rejection, logout revocation, bcrypt, RBAC) -- our API-key branch folded into controllers.ParseTokenClaims so both coexist - bug-1736: rawRunUsage job run-usage fields in the Go model - xce_test relocated src/controller/testsuite/test.c -> src/harness/test.c; controller suite split into controller_test.c + fixtures; their clitest fixtures spawn xce_test mid-suite under `meson test -j` Ours preserved across the merge: - PEND-reason P1 (codes 1-9, preason.h, job_preason machinery) -- gate re-ported into their relocated allocator (sched_election.c) after plugin_job_election, with attach on the allocator thread only (bug-1521 race discipline) - PEND-reason P0 main_reason semantics RECONCILED to theirs: the merge initially re-carried our first-detail-add-promotes-main_reason fallback in add_job_preason (4e2ac86), but their whole trace/verify stack treats "detail-only => main_reason 0" as a contract -- sched_trace_capture counts main_reason as its own occurrence ("Expected 2 Was 3" in test_trace_capture), the outcome renderer prints "main reason: none" for detail-only PENDs (test_repl_trace_e2e), and the committed .replay/.sim_verify baselines encode main_reason=0 (test_repl_trace_baseline byte compare, test_trace_equivalence off-vs-traced). The fallback is dropped from the shared lib; the user-visible "overall reason" survives as an API-layer view: rawJobToModel (convert_pure.go) promotes the first detail row to main_reason_name when main_reason is 0 (main_reason VALUE marshals unchanged). add_job_preason keeps only the NULL-reasons guard - bug-1498: sched_job_is_ready rejects non-PEND/suspended jobs - bug-1490/1781: xce_jobctrl_t.action as capnp union discriminant (their jobCtrlUnionDiscriminant, same semantics) - xcecli typed PendingReason field (53c00834), bug-1522 reconnect resend, xcecli_force_reconnect + their is_connection_broken both exported in client.h/libclient - t1/t2/t3 test-node hostconfig in src/configs/hosts.yaml (bug-1494), /xce/v1/events + /xce/v1/apikeys whitelist routes (agent Phase 2) - lib ASAN_OPTIONS=detect_leaks=0 test env + their oom-test block - drop-audit: our bug-1504 fix covered by their bug-1680 (no action) bug-924 follow-up (replaces the67f3f6adscratch-dir wipe): the take-theirs audit's one "drop" was re-evaluated and REVERTED instead of re-applied -- a global `find /tmp -name 'xce_*' -exec rm -rf` at suite start/exit races the LIVE /tmp/xce_* fixtures of sibling suites under `meson test -j` (harness_unit_test's clitest fixtures spawn xce_test as a child mid-suite; the wipe destroyed the parent's fixtures -> test_test_cli failures). Coverage instead: - mkdtemp_clean shim gains mkdtemp_clean_mark/sweep_since (testsuite_support/mkdtemp_clean.[ch]) - new case_scratch_sweep.[ch]: aggregate runners (controller_test.c, harness_test.c -- hundreds of cases per process) call case_scratch_sweep_begin/_end_ok from setUp/tearDown; a PASSED case's dirs are swept immediately in tearDown (after engine reaping), FAILED cases keep their dirs for post-mortem; XCE_MKDTEMP_KEEP_ALL=1 disables. One full controller_unit_test run was observed to leak 12G+ to ENOSPC without this, cascading into unrelated "Expected Non-NULL" failures - harness_unit_test_deps links the shim library as well Test reconciliations on adopt: - core-ready-gates: sched_queue_is_ready now gates only INACT_ADMIN|INACT_WINDOW (bug-1938/1960); CLOSED queues elect -- covered end-to-end by test_queue_admin_control.c - test_worker_resync: the 4 restart-replay RUN assertions predate the bug-1509 B3 marking (8858afed) landing on the same lineage; replayed RUN jobs now assert RUN|UNKWN|TRANSIENT (TWR_ASSERT_REPLAYED_RUN); terminal DONE/EXIT assertions unchanged - convert_pure.go: our job-typed resolveExecUser deleted (superseded by their bug-1780/1936 two-arg version) - test_preason: the 3 P0-promotion assertions reverted to the detail-keeps-main-0 contract; new tests cover limit-code detail rows and explicit-main-wins-over-later-details; apiserver convert_test.go gains the detail-only name-promotion cases Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
commit
0c8607345e
663 changed files with 139848 additions and 13614 deletions
|
|
@ -968,6 +968,86 @@ static void test_apply_pump_guard_does_not_break_job_ctrl_live_append(void)
|
|||
"PASS\n");
|
||||
}
|
||||
|
||||
/* ── bug-448 / bug-1626: job_ctrl's own reference and lock discipline ── */
|
||||
|
||||
/* bug-448: job_ctrl takes a reference on the job and must drop it on every
|
||||
* exit path; the normal (not-started) path never did. */
|
||||
static void test_job_ctrl_releases_its_job_reference(void)
|
||||
{
|
||||
tap_setup();
|
||||
|
||||
job_t* j = tap_seed_job(907, JOB_STAT_PSUSP);
|
||||
TEST_ASSERT_NOT_NULL(j);
|
||||
unsigned long before = job_get_refcount(j);
|
||||
|
||||
xce_signal_t sig = { .signo = SIGCONT, .force = 0 };
|
||||
xce_jobctrl_t ctrl = { .action = JobControl_u_siginfo, .jobid = 907 };
|
||||
ctrl.u.siginfo = &sig;
|
||||
int rc = job_ctrl(&g_tap_c, &ctrl);
|
||||
|
||||
sig.signo = SIGKILL;
|
||||
int rc2 = job_ctrl(&g_tap_c, &ctrl);
|
||||
|
||||
unsigned long after = job_get_refcount(j);
|
||||
tap_teardown();
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(0, rc);
|
||||
TEST_ASSERT_EQUAL_INT(0, rc2);
|
||||
TEST_ASSERT_EQUAL_UINT_MESSAGE(before, after,
|
||||
"bug-448: job_ctrl must release the reference it takes on the job");
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
job_t* j;
|
||||
atomic_int done;
|
||||
int rc;
|
||||
} tap_jobctrl_arg_t;
|
||||
|
||||
static void* tap_jobctrl_thread(void* arg)
|
||||
{
|
||||
tap_jobctrl_arg_t* a = (tap_jobctrl_arg_t*)arg;
|
||||
xce_signal_t sig = { .signo = SIGSTOP, .force = 0 };
|
||||
xce_jobctrl_t ctrl = { .action = JobControl_u_siginfo,
|
||||
.jobid = a->j->job.jobid };
|
||||
ctrl.u.siginfo = &sig;
|
||||
|
||||
a->rc = job_ctrl(&g_tap_c, &ctrl);
|
||||
atomic_store(&a->done, 1);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* bug-1626: the not-started branch mutates the job (mark_job_update /
|
||||
* mark_job_fail), so job_ctrl must hold the job's WRITE lock -- it may not
|
||||
* proceed while another thread holds a read lock on the job. */
|
||||
static void test_job_ctrl_mutates_not_started_job_under_write_lock(void)
|
||||
{
|
||||
tap_setup();
|
||||
|
||||
job_t* j = tap_seed_job(908, JOB_STAT_PEND);
|
||||
TEST_ASSERT_NOT_NULL(j);
|
||||
|
||||
tap_jobctrl_arg_t arg = { .j = j, .rc = -1 };
|
||||
atomic_store(&arg.done, 0);
|
||||
|
||||
lock(j, RTTI_LOCK_READ);
|
||||
pthread_t th;
|
||||
TEST_ASSERT_EQUAL_INT(0, pthread_create(&th, NULL, tap_jobctrl_thread,
|
||||
&arg));
|
||||
usleep(200 * 1000);
|
||||
int done_while_reader_held = atomic_load(&arg.done);
|
||||
unlock(j);
|
||||
pthread_join(th, NULL);
|
||||
|
||||
uint32_t state = j->job.state;
|
||||
tap_teardown();
|
||||
|
||||
TEST_ASSERT_EQUAL_INT_MESSAGE(0, done_while_reader_held,
|
||||
"bug-1626: job_ctrl changed a job while another thread held its "
|
||||
"read lock -- it must take RTTI_LOCK_WRITE");
|
||||
TEST_ASSERT_EQUAL_INT(0, arg.rc);
|
||||
TEST_ASSERT_TRUE(state & JOB_STAT_PSUSP);
|
||||
}
|
||||
|
||||
/* ── 4. Merge-staleness regression: FAIL must not clobber a racing FINISH ─
|
||||
*
|
||||
* Reproduces the round-1 corruption bug found by review: a FINISH fact
|
||||
|
|
@ -1999,6 +2079,8 @@ void test_apply_pump(void)
|
|||
RUN_TEST(test_apply_pump_run_engages_live_fence_on_failed_lsn);
|
||||
RUN_TEST(test_apply_pump_run_self_clears_fence_on_successful_retry);
|
||||
RUN_TEST(test_apply_pump_guard_does_not_break_job_ctrl_live_append);
|
||||
RUN_TEST(test_job_ctrl_releases_its_job_reference);
|
||||
RUN_TEST(test_job_ctrl_mutates_not_started_job_under_write_lock);
|
||||
RUN_TEST(test_apply_pump_fail_racing_finish_merges_against_current_state);
|
||||
RUN_TEST(test_apply_pump_late_pending_apply_registration_self_heals);
|
||||
RUN_TEST(test_register_pending_apply_race_against_concurrent_apply_self_heals);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue