feat(ha-convergence-recovery): clamped rejoin reconciliation + intake trigger (task 3.2)
This commit is contained in:
parent
5521720b0c
commit
63c12461a5
3 changed files with 168 additions and 31 deletions
|
|
@ -211,21 +211,39 @@ static int dc_task_count(const delaymgr_t *mgr)
|
|||
return count;
|
||||
}
|
||||
|
||||
/* Run the single pending delaymgr task synchronously by invoking its stored
|
||||
* entry(args), then return the entry's rc. Returns INT_MIN sentinel if there
|
||||
* is no pending task (RED: nothing was scheduled). The task is NOT removed
|
||||
* from the queue (delaymgr_fini frees it at teardown) — we only need to run
|
||||
* its body once to observe truncation + restart. */
|
||||
/* Run the single pending delaymgr one-shot task synchronously by invoking its
|
||||
* stored entry(args), then return the entry's rc. Returns DC_NO_TASK sentinel if
|
||||
* there is no pending task (RED: nothing was scheduled).
|
||||
*
|
||||
* The run task is DEQUEUED and its node freed, mirroring the real delaymgr worker
|
||||
* (delay.c: ordq_get → runner_fn → runcount-- → FREEUP for a runcount==1 task).
|
||||
* This is required for correctness, not just fidelity: the convergence task bodies
|
||||
* free their own heap ctx (the args), exactly as production does (the delaymgr
|
||||
* killer frees only the delay_task_t node, never args; production execv never
|
||||
* re-runs a one-shot). Peeking without dequeuing would let a SECOND
|
||||
* dc_run_pending_task call re-run an already-run task on a freed ctx
|
||||
* (heap-use-after-free) — e.g. the rejoin once-guard test, whose second reconcile
|
||||
* correctly schedules NO new task so the head still points at the consumed one.
|
||||
* Dequeuing makes that second call return DC_NO_TASK, as production would. */
|
||||
#define DC_NO_TASK 0x7fffffff
|
||||
static int dc_run_pending_task(const delaymgr_t *mgr)
|
||||
static int dc_run_pending_task(delaymgr_t *mgr)
|
||||
{
|
||||
const ordq_item_t *it = mgr->tasks.head;
|
||||
if (it == NULL || it->data == NULL)
|
||||
ordq_item_t *it = ordq_get(&mgr->tasks, NULL);
|
||||
if (it == NULL || it->data == NULL) {
|
||||
if (it != NULL)
|
||||
free(it);
|
||||
return DC_NO_TASK;
|
||||
const delay_task_t *t = (const delay_task_t *)it->data;
|
||||
if (t->entry == NULL)
|
||||
}
|
||||
delay_task_t *t = (delay_task_t *)it->data;
|
||||
if (t->entry == NULL) {
|
||||
free(t);
|
||||
free(it);
|
||||
return DC_NO_TASK;
|
||||
return t->entry(t->args);
|
||||
}
|
||||
int rc = t->entry(t->args); /* task body frees its own args ctx */
|
||||
free(t); /* mirror the worker's FREEUP(t) for runcount==1 */
|
||||
free(it); /* mirror the worker's FREEUP(it) */
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* ── Tests ──────────────────────────────────────────────────────────────── */
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue