#pragma once /* * slowlane.h — SlowLane executor: a bounded pool of dedicated worker threads * that runs long blocking I/O OFF the Raft drive threads (iomgr epoll loop, * delaymgr timer), with admission control and per-key in-flight dedup. * * Service interface is verbatim from * .kiro/specs/raft-drive-async/design.md (Components -> SlowLane). * * The lane owns scheduling/isolation, NOT the work content: the submitted * `fn` (peer connect, snapshot transfer, config-push body) stays in its own * module and is responsible for clearing any caller-side in-flight state on * completion. `slowlane_submit` is non-blocking: a drive-thread caller * enqueues and returns immediately, never waiting for `fn` to run. * * raft-drive-async task 1.2 — Requirements 3.1, 3.2, 3.3, 3.4, 1.5. */ #include #include typedef void (*slowlane_fn_t)(void *arg); /* * One queued unit of work. Allocated by the lane on submit, freed by the * worker after `fn` returns. Held on the FIFO pending queue until a worker * picks it up. */ typedef struct slowlane_task { uint64_t key; /* dedup key; 0 disables dedup */ slowlane_fn_t fn; void *arg; struct slowlane_task *next; /* intrusive FIFO link */ } slowlane_task_t; /* * SlowLane executor state. All fields are lane-private and guarded by `lock`; * callers touch the lane only through the API below. Bounded by design: * - `n_workers` dedicated pthreads, isolated from the vtask request pool; * - at most `max_inflight` tasks RUNNING concurrently (admission cap); * - at most one in-flight task per non-zero key (dedup). */ typedef struct slowlane { pthread_mutex_t lock; pthread_cond_t cond; /* signalled when work or a slot frees up */ slowlane_task_t *q_head; /* FIFO pending queue (admission-deferred) */ slowlane_task_t *q_tail; uint64_t *inflight_keys; /* running OR queued non-zero keys (dedup set) */ int n_inflight_keys; int cap_inflight_keys; int n_workers; int max_inflight; /* admission cap on concurrently RUNNING tasks */ int n_running; /* tasks currently executing on a worker */ pthread_t *workers; int started; /* worker threads created */ int stopping; /* fini requested: drain + join */ } slowlane_t; /* * Initialise the lane and start `n_workers` dedicated worker threads. * `max_inflight` bounds the number of tasks RUNNING concurrently (excess is * queued, never dropped, never run on a drive thread). * Returns 0 on success, -EINVAL on a bad argument, -errno on a thread/alloc * failure (lane left un-started). */ int slowlane_init(slowlane_t *sl, int n_workers, int max_inflight); /* * Submit `fn(arg)` to run exactly once on a lane worker. NON-BLOCKING: the * caller (which may be a drive thread) returns immediately. * * Returns (per design SlowLane Service Interface): * 0 enqueued AND a worker slot was free (running < max_inflight) — * the task can start immediately * -EEXIST `key` (non-zero) already in flight — deduped no-op, NOT enqueued * -EAGAIN admission cap full at submit time (running >= max_inflight) — the * task IS still queued/deferred (never dropped, never run on a drive * thread); the caller is told it was deferred, not that it failed * -errno allocation failure / lane stopping * * In all of {0, -EAGAIN} the work is enqueued and will run exactly once. Only * -EEXIST and -errno mean the task was not enqueued. * * `key == 0` disables dedup (every key-0 submit is independent work). */ int slowlane_submit(slowlane_t *sl, uint64_t key, slowlane_fn_t fn, void *arg); /* * Drain the pending queue, stop and join all workers. Shutdown only. * After return the lane is dormant; re-init to reuse. */ void slowlane_fini(slowlane_t *sl);