feat(raft-drive-async): LivenessAdmission pool reservation + commit fold (tasks 1.6/1.7)

vtaskmgr gains vtask_class_t (LIVENESS/LIVE_BLOCK/REQ_BLOCK) + a pool-level
blocking reservation: REQ_BLOCK fast-rejects -XCE_ERR_NOT_READY past
n_vcpus-R; LIVENESS never gated (deadlock-freedom). commit_submit folds its
inflight_waits onto the pool via commit_set_pool; production wired in
cluster.c phase2 (vtaskmgr_set_liveness_reserved(R) + commit_set_pool).
Lock order p->lock->ce->lock->blk_lock. Dormant until 3.1. Commit suites
38/30/22/38 green; lib 282/0, controller 196/0, ASAN clean. Phase 1 done.
This commit is contained in:
Rongsong Shen 2026-06-26 07:43:19 +00:00
parent 0781c6c027
commit a36234a103
12 changed files with 721 additions and 22 deletions

View file

@ -191,10 +191,11 @@ static void commit_advance_locked(commit_engine_t* ce)
pthread_cond_broadcast(&ce->cond);
}
/* Admission cap for concurrent blocking waits, expressed only relative to
* the n_vcpus init argument: keep COMMIT_WAIT_RESERVED vtasks free of
* blocking commit waits, floored at 1 so one wait is always admittable
* (Req 7.1, 7.2). */
/* Admission cap for concurrent blocking waits, LOCAL (no-pool) fallback:
* keep COMMIT_WAIT_RESERVED vtasks free of blocking commit waits, floored at 1
* so one wait is always admittable (Req 7.1, 7.2). Used only when no vtaskmgr
* pool is wired (commit_set_pool); when a pool is wired the pool owns the
* single reservation (cap = n_vcpus - VTASK_LIVENESS_RESERVED). */
static int wait_cap(const commit_engine_t* ce)
{
int cap = ce->n_vcpus - COMMIT_WAIT_RESERVED;
@ -202,6 +203,40 @@ static int wait_cap(const commit_engine_t* ce)
return (cap < 1) ? 1 : cap;
}
/* raft-drive-async task 1.6: acquire a blocking-commit-wait admission slot.
* When a pool is wired, delegate to the pool's single LivenessAdmission
* reservation (the pool owns the cap); otherwise apply the local cap. Returns
* 0 (admitted) or -XCE_ERR_NOT_READY (cap exhausted — fast reject, never
* blocks). Caller holds ce->lock. inflight_waits is maintained in BOTH modes
* as the observable in-flight commit-wait count. */
static int wait_admit_locked(commit_engine_t* ce)
{
if (ce->pool != NULL) {
int rc = vtaskmgr_blocking_acquire(ce->pool);
if (rc != 0) {
return rc; /* -E_XCE_NOT_READY == -XCE_ERR_NOT_READY */
}
ce->inflight_waits++;
return 0;
}
/* Local fallback: cap = max(1, n_vcpus - COMMIT_WAIT_RESERVED). */
if (ce->inflight_waits >= wait_cap(ce)) {
return -XCE_ERR_NOT_READY;
}
ce->inflight_waits++;
return 0;
}
/* Release a blocking-commit-wait admission slot. Caller holds ce->lock. */
static void wait_release_locked(commit_engine_t* ce)
{
ce->inflight_waits--;
if (ce->pool != NULL) {
vtaskmgr_blocking_release(ce->pool);
}
}
/* WAIT completion predicate (Req 1.1): own entry committed AND the
* own-term gate (when set) reached. Caller holds ce->lock. */
static int wait_done_locked(const commit_engine_t* ce, uint64_t lsn)
@ -270,6 +305,7 @@ int commit_engine_init(commit_engine_t* ce, persist_t* p, int n_peers, int n_vcp
ce->p = p;
ce->n_peers = n_peers;
ce->n_vcpus = n_vcpus;
ce->pool = NULL; /* local cap until commit_set_pool wires the pool */
ce->inflight_waits = 0;
ce->writable_lsn = 0; /* no own-term gate until set by promotion */
ce->raft_commit_index = 0; /* advisory record only; does not gate commit (optA) */
@ -283,6 +319,20 @@ int commit_engine_init(commit_engine_t* ce, persist_t* p, int n_peers, int n_vcp
return 0;
}
/* raft-drive-async task 1.6: fold commit-wait admission onto the vtaskmgr
* pool's single LivenessAdmission reservation. Set-once at wiring time; safe
* to set NULL to restore the local fallback. The pool must outlive the engine
* (controller lifecycle guarantees this — vtaskmgr_fini after commit_engine_fini). */
void commit_set_pool(commit_engine_t* ce, vtaskmgr_t* pool)
{
if (ce == NULL) {
return;
}
pthread_mutex_lock(&ce->lock);
ce->pool = pool;
pthread_mutex_unlock(&ce->lock);
}
void commit_engine_fini(commit_engine_t* ce)
{
if (ce == NULL) {
@ -311,7 +361,7 @@ int commit_submit(commit_engine_t* ce, uint64_t lsn, commit_mode_t mode,
struct timespec dl;
uint64_t cur, gate;
int rc = 0;
int cap, wrc;
int wrc, arc;
if (ce == NULL || ce->p == NULL || lsn == 0) {
return -EINVAL;
@ -333,19 +383,19 @@ int commit_submit(commit_engine_t* ce, uint64_t lsn, commit_mode_t mode,
/* Admission check: cap exhaustion is the ONLY -XCE_ERR_NOT_READY
* cause (Req 7.1, 7.2; CORRECTED contract). Retriable fast-reject —
* never blocks. */
cap = wait_cap(ce);
if (ce->inflight_waits >= cap) {
* never blocks. When a vtaskmgr pool is wired (raft-drive-async task 1.6)
* the cap is the pool's single LivenessAdmission reservation; otherwise
* the local COMMIT_WAIT_RESERVED cap. */
arc = wait_admit_locked(ce);
if (arc != 0) {
pthread_mutex_unlock(&ce->lock);
WARN("commit: WAIT rejected, admission cap exhausted "
"(cap=%d n_vcpus=%d lsn=%lu)",
cap, ce->n_vcpus, lsn);
return -XCE_ERR_NOT_READY;
"(pool=%s n_vcpus=%d lsn=%lu)",
ce->pool ? "pool" : "local", ce->n_vcpus, lsn);
return arc;
}
ce->inflight_waits++;
#ifdef XCE_TESTING
assert(ce->inflight_waits > 0 && ce->inflight_waits <= cap);
assert(ce->inflight_waits > 0);
#endif
/* Standalone / single-member: self constitutes the quorum (Req 6.3).
@ -367,7 +417,7 @@ int commit_submit(commit_engine_t* ce, uint64_t lsn, commit_mode_t mode,
}
}
ce->inflight_waits--;
wait_release_locked(ce);
#ifdef XCE_TESTING
assert(ce->inflight_waits >= 0);
#endif