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

@ -3,6 +3,8 @@
#include <stdlib.h>
#include <unistd.h>
#include "xce/errnox.h"
#include "mylock.h"
#include "netio.h"
#include "utils.h"
@ -24,6 +26,7 @@ int vtask_init(vtask_t* task, vtask_func_t entry, void* data)
task->vcpu = -1;
task->data = data;
task->owner = NULL;
task->cls = VTASK_LIVENESS; /* never-gated by default; legacy enq paths */
return 0;
}
@ -68,6 +71,14 @@ void run_task(vtask_t* task)
}
vtaskmgr = (vtaskmgr_t*)task->owner;
/* LivenessAdmission (task 1.6): a VTASK_REQ_BLOCK task held a
* blocking-admission slot from enqueue (vtask_enq_classified) through its
* run; release it now so the next REQ_BLOCK can be admitted. */
if (vtaskmgr != NULL && task->cls == VTASK_REQ_BLOCK) {
vtaskmgr_blocking_release(vtaskmgr);
}
if (vtaskmgr != NULL) {
/* wake up dispatcher */
vtaskmgr_wakeup(vtaskmgr);
@ -168,10 +179,14 @@ int vtaskmgr_init(vtaskmgr_t* vtaskmgr, int nvcpus)
return -EINVAL;
}
vtaskmgr->dispatcher = 0;
vtaskmgr->n_vcpus = nvcpus;
vtaskmgr->vcpus = (vcpu_t*)calloc(nvcpus, sizeof(vcpu_t));
vtaskmgr->dispatcher = 0;
vtaskmgr->n_vcpus = nvcpus;
vtaskmgr->liveness_reserved = 0; /* gate disabled until wired (task 1.6) */
vtaskmgr->blocking_inflight = 0;
pthread_mutex_init(&vtaskmgr->blk_lock, NULL);
vtaskmgr->vcpus = (vcpu_t*)calloc(nvcpus, sizeof(vcpu_t));
if (vtaskmgr->vcpus == NULL) {
pthread_mutex_destroy(&vtaskmgr->blk_lock);
return -ENOMEM;
}
for (i = 0; i < nvcpus; i++) {
@ -335,10 +350,116 @@ void vtaskmgr_fini(vtaskmgr_t* vtaskmgr)
dataq_fini(&(vtaskmgr->taskq));
cbuf_fini(&(vtaskmgr->wtbuf));
cbuf_fini(&(vtaskmgr->wbuf));
pthread_mutex_destroy(&vtaskmgr->blk_lock);
return;
}
/* ============================================================
* LivenessAdmission (raft-drive-async task 1.6)
* ============================================================ */
void vtaskmgr_set_liveness_reserved(vtaskmgr_t* vtaskmgr, int reserved)
{
if (vtaskmgr == NULL) {
return;
}
if (reserved < 0) {
reserved = 0;
}
pthread_mutex_lock(&vtaskmgr->blk_lock);
vtaskmgr->liveness_reserved = reserved;
pthread_mutex_unlock(&vtaskmgr->blk_lock);
}
int vtaskmgr_blocking_cap(const vtaskmgr_t* vtaskmgr)
{
int cap;
if (vtaskmgr == NULL) {
return 0;
}
if (vtaskmgr->liveness_reserved <= 0) {
/* Gate disabled: blocking is uncapped (legacy behavior). */
return vtaskmgr->n_vcpus;
}
cap = vtaskmgr->n_vcpus - vtaskmgr->liveness_reserved;
/* Floor at 1 so a commit (REQ_BLOCK) can always run, even on a degenerate
* small-core box where the reservation would otherwise drive the cap to 0
* (design Sizing note). */
return (cap < 1) ? 1 : cap;
}
int vtaskmgr_blocking_acquire(vtaskmgr_t* vtaskmgr)
{
int rc = 0;
if (vtaskmgr == NULL) {
return -EINVAL;
}
pthread_mutex_lock(&vtaskmgr->blk_lock);
if (vtaskmgr->liveness_reserved <= 0) {
/* Gate disabled: admit uncounted (legacy: nothing reserved). */
pthread_mutex_unlock(&vtaskmgr->blk_lock);
return 0;
}
int cap = vtaskmgr->n_vcpus - vtaskmgr->liveness_reserved;
if (cap < 1) {
cap = 1;
}
if (vtaskmgr->blocking_inflight >= cap) {
/* Reservation would be breached — fast reject (never parks). */
rc = -E_XCE_NOT_READY;
} else {
vtaskmgr->blocking_inflight++;
}
pthread_mutex_unlock(&vtaskmgr->blk_lock);
return rc;
}
void vtaskmgr_blocking_release(vtaskmgr_t* vtaskmgr)
{
if (vtaskmgr == NULL) {
return;
}
pthread_mutex_lock(&vtaskmgr->blk_lock);
if (vtaskmgr->liveness_reserved > 0 && vtaskmgr->blocking_inflight > 0) {
vtaskmgr->blocking_inflight--;
}
pthread_mutex_unlock(&vtaskmgr->blk_lock);
}
int vtask_enq_classified(vtaskmgr_t* vtaskmgr, vtask_t* task, vtask_class_t cls)
{
int rc;
if (vtaskmgr == NULL || task == NULL) {
return -EINVAL;
}
task->cls = cls;
if (cls == VTASK_REQ_BLOCK) {
/* Admission-gated: reserve a blocking slot BEFORE enqueue. On reject
* the task is NOT enqueued — the caller still owns it. The slot is
* released in run_task when this task completes. */
rc = vtaskmgr_blocking_acquire(vtaskmgr);
if (rc != 0) {
return rc;
}
rc = vtask_enq(vtaskmgr, task);
if (rc < 0) {
/* Enqueue failed after acquiring — give the slot back. */
vtaskmgr_blocking_release(vtaskmgr);
}
return rc;
}
/* VTASK_LIVENESS / VTASK_LIVE_BLOCK are never admission-gated. */
return vtask_enq(vtaskmgr, task);
}
int vtask_enq(vtaskmgr_t* vtaskmgr, vtask_t* task)
{
int rc = 0;