Task 1.4: requirement path push/pop with balanced begin/end entries carrying the full nesting path, one entry per traced event kind, and a line budget that stops storing at the bound and counts omitted lines. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
222 lines
10 KiB
C
222 lines
10 KiB
C
#if !defined(_SCHED_ALLOC_ITER_H_)
|
|
|
|
#define _SCHED_ALLOC_ITER_H_ 1
|
|
|
|
/*
|
|
* alloc_iter.h - lazy allocation-plan and host-candidate iterator
|
|
* contracts (scheduler-allocation spec, Req 2 AC 1-6 / Req 3 AC 1-6).
|
|
*
|
|
* This header defines two reusable abstractions any allocator plugin can
|
|
* adopt:
|
|
*
|
|
* xce_alloc_plan_iter_t - generates candidate allocation plans (a
|
|
* partition of a primary resreq's maxTasks into per-host slot buckets),
|
|
* one per next() call, most-preferred first.
|
|
*
|
|
* xce_host_iter_t - for a given plan, generates candidate hosts (one
|
|
* per next() call) matching the plan's select-expression + availability
|
|
* filter.
|
|
*
|
|
* No allocator is wired onto these interfaces yet -- see
|
|
* ../../testsuite/test_alloc_plan_iter.c for a trivial, no-op reference
|
|
* implementation of each proving the contract shape compiles and is
|
|
* usable.
|
|
*/
|
|
|
|
#include <rtti.h>
|
|
|
|
#include "resreq.h"
|
|
#include "scheduler.h"
|
|
#include "xce/types.h"
|
|
|
|
/*
|
|
* xce_alloc_plan_t - a candidate allocation plan: a partition of a
|
|
* primary resreq's maxTasks into per-host slot buckets, e.g. for
|
|
* maxTasks=3: [3], [2,1], [1,1,1] -- NOT a uniform (nhosts,
|
|
* slots_per_host) pair, which cannot express uneven splits.
|
|
*
|
|
* Produced by xce_alloc_plan_iter_t.next(); the returned pointer is valid
|
|
* only until the following next() or fini() call on that same iterator
|
|
* (it is not independently heap-owned by the caller).
|
|
*
|
|
* Tagged so sched/trace.h can forward-declare it without including this
|
|
* header (which pulls in scheduler.h -> core.h, and core.h will carry the
|
|
* trace record pointer).
|
|
*/
|
|
typedef struct xce_alloc_plan_s {
|
|
int nbuckets; /* number of hosts this plan needs (==
|
|
* len(bucket_slots)) */
|
|
int* bucket_slots; /* partition of maxTasks; bucket_slots[i] = slots
|
|
* needed on the i-th distinct host claimed for
|
|
* this plan */
|
|
/* host_filter: derived from primary_resreq->select, plus slot
|
|
* availability -- consumed by xce_host_iter_t (Req 3), not exposed as
|
|
* a separate field here. This interface does not mandate a
|
|
* representation for it; each concrete plan-iterator implementation
|
|
* carries whatever it needs to reconstruct the filter.
|
|
*
|
|
* Added by task 2.1 (host_iter.c, the real implementation): `ctx` is
|
|
* exactly that carrier. xce_host_iter_t.init()'s own three declared
|
|
* parameters (iter, plan, workers) cannot, on their own, reach the
|
|
* select expression or the currently-staged resource want -- `workers`
|
|
* is a bare RTTI var with no back-pointer to the owning
|
|
* xce_scheduler_t (confirmed against sched_collect_workers()'s own
|
|
* iter_make(&it, sched->core.workers) body, scheduler.c), and this
|
|
* struct's other two fields (nbuckets/bucket_slots) carry only the
|
|
* partition shape. `ctx` (already carrying ->sched, ->primary->select,
|
|
* and ->alloc_req.host -- the resource want staged by whichever
|
|
* allocator's prepare_allocation_hook ran) supplies everything
|
|
* sched_meet_host_selection()/sched_check_host_satisfy() need, without
|
|
* widening init()'s own parameter list. Not owned by the plan; the
|
|
* caller (allocate_primary(), a later task) retains ownership for the
|
|
* duration of one job's allocation attempt. */
|
|
xce_sched_ctx_t* ctx;
|
|
} xce_alloc_plan_t;
|
|
|
|
typedef struct xce_alloc_plan_iter_s xce_alloc_plan_iter_t;
|
|
|
|
/*
|
|
* xce_alloc_plan_iter_t - lazy, cancel-safe candidate-plan generator (Req
|
|
* 2). One instance is owned by a single allocator's job_alloc_fn for the
|
|
* lifetime of one job's allocation attempt.
|
|
*
|
|
* Allocation convention: stack-allocated by the caller, then initialized
|
|
* IN PLACE -- init() takes the iterator's own address as its first
|
|
* argument, matching this codebase's existing sched_ctx_init(&ctx, ...)
|
|
* convention (schedobj.c). This is NOT a heap-allocating factory
|
|
* function:
|
|
*
|
|
* xce_alloc_plan_iter_t iter_storage;
|
|
* xce_alloc_plan_iter_t* iter = &iter_storage;
|
|
* xce_alloc_plan_t* plan;
|
|
*
|
|
* if (iter->init(iter, primary_resreq, core) != 0) { ... }
|
|
* while ((plan = iter->next(iter)) != NULL) { ... }
|
|
* iter->fini(iter);
|
|
*/
|
|
struct xce_alloc_plan_iter_s {
|
|
/* Initialize the iterator for a given primary resreq and scheduling
|
|
* core state. Returns 0 on success, nonzero on error. */
|
|
int (*init)(xce_alloc_plan_iter_t* iter, xce_primary_resreq_t* resreq,
|
|
xce_sched_core_t* core);
|
|
|
|
/* Return the next candidate plan, or NULL when exhausted. Successive
|
|
* calls return plans ordered most-preferred to least-preferred, per
|
|
* the owning allocator's own preference policy; the NULL
|
|
* end-of-iteration signal is stable across repeated calls once
|
|
* exhausted. The returned plan pointer is valid until the next
|
|
* next() or fini() call. Calls pthread_testcancel() (or an
|
|
* equivalent real cancellation point) at least once per invocation. */
|
|
xce_alloc_plan_t* (*next)(xce_alloc_plan_iter_t* iter);
|
|
|
|
/* Release all resources held by the iterator. The interface defines
|
|
* no other required cleanup step. */
|
|
void (*fini)(xce_alloc_plan_iter_t* iter);
|
|
|
|
/* Private state: current position, strategy-specific data. */
|
|
void* private;
|
|
};
|
|
|
|
typedef struct xce_host_iter_s xce_host_iter_t;
|
|
|
|
/*
|
|
* xce_host_iter_t - lazy, cancel-safe candidate-host filter for one plan
|
|
* bucket (Req 3). Shares the same stack-allocate-then-init-in-place
|
|
* convention as xce_alloc_plan_iter_t above.
|
|
*
|
|
* Scoped PER PLAN BUCKET, not per plan: a multi-bucket plan requires a
|
|
* fresh xce_host_iter_t instance (a new init() call) for each bucket --
|
|
* never one instance shared across a plan's buckets. next() is a
|
|
* forward-only cursor with no rewind; sharing one instance across buckets
|
|
* would permanently drop a host an earlier, larger bucket tried and
|
|
* rejected as too small, even when that same host exactly satisfies a
|
|
* later, smaller bucket. Host exclusivity ACROSS buckets of the same plan
|
|
* (is_claimed()) is the caller's responsibility, not this interface's --
|
|
* this iterator only ever answers "does this select expression +
|
|
* availability filter match," with no memory of prior buckets.
|
|
*/
|
|
struct xce_host_iter_s {
|
|
/* Initialize the iterator for a given plan and worker set. Returns 0
|
|
* on success, nonzero on error. */
|
|
int (*init)(xce_host_iter_t* iter, xce_alloc_plan_t* plan, var workers);
|
|
|
|
/* Return the next candidate worker matching the plan's host filter,
|
|
* or NULL when exhausted for this instance's bucket search --
|
|
* exhaustion is scoped to this one instance, not to the plan as a
|
|
* whole (see the per-bucket-freshness note above: a plan with more
|
|
* buckets remaining requires a fresh host_iter instance). The
|
|
* returned worker pointer is valid until the next next() or fini()
|
|
* call. Calls pthread_testcancel() (or an equivalent real
|
|
* cancellation point) at least once per invocation. */
|
|
xce_sched_worker_t* (*next)(xce_host_iter_t* iter);
|
|
|
|
/* Release all resources held by the host iterator, independent of
|
|
* the owning plan iterator's lifecycle. */
|
|
void (*fini)(xce_host_iter_t* iter);
|
|
|
|
/* Private state: current position, filtering state. */
|
|
void* private;
|
|
};
|
|
|
|
/*
|
|
* xce_sched_allocator_iface_t - the allocator plugin contract
|
|
* (scheduler-allocator-runner Req 1.2). An allocator plugin supplies only
|
|
* its policy -- an optional acceptance guard plus a plan generator -- and
|
|
* sets job_alloc_fn = sched_allocator_run; the framework runner owns the
|
|
* rest of the allocation attempt. The typedef is forward-declared in
|
|
* plugin.h; the object is `static const` in the plugin file and
|
|
* referenced from xce_sched_plugin_t.allocator_iface.
|
|
*/
|
|
struct xce_sched_allocator_iface_s {
|
|
/* Optional. Returns 1 when this plugin allocates `primary`, 0 when it
|
|
* does not (the job stays pending with no reason and no checks run).
|
|
* NULL accepts every request. */
|
|
int (*accepts)(const xce_primary_resreq_t* primary);
|
|
|
|
/* Required. Same contract as xce_alloc_plan_iter_t init/next/fini
|
|
* above: plan_init returns 0 and leaves `iter` ready for plan_next, or
|
|
* nonzero with nothing to release (plan_fini is then not called);
|
|
* plan_next returns plans most-preferred first, NULL when exhausted;
|
|
* plan_fini releases the iterator's private state. */
|
|
int (*plan_init)(xce_alloc_plan_iter_t* iter, xce_primary_resreq_t* resreq,
|
|
xce_sched_core_t* core);
|
|
xce_alloc_plan_t* (*plan_next)(xce_alloc_plan_iter_t* iter);
|
|
void (*plan_fini)(xce_alloc_plan_iter_t* iter);
|
|
};
|
|
|
|
/*
|
|
* sched_allocator_run - the framework allocation entry point, the
|
|
* job_alloc_fn of every allocator plugin; `plugin` is the
|
|
* xce_sched_plugin_t* whose allocator_iface drives the attempt. Returns
|
|
* > 0 when satisfied (ctx->alloc set), 0 when pending, < 0 a negative
|
|
* errno (-EINVAL for a missing plugin, ctx, core or allocator_iface).
|
|
*/
|
|
extern int sched_allocator_run(void* plugin, xce_sched_ctx_t* ctx);
|
|
|
|
/*
|
|
* sched_host_iter_init/next/fini - the one shared xce_host_iter_t
|
|
* implementation (host_iter.c, task 2.1, Req 3). Callers wire these onto a
|
|
* stack-allocated, caller-owned xce_host_iter_t before calling init(),
|
|
* following the exact same manual-vtable-assignment convention task 1.1's
|
|
* own reference stubs established (see testsuite/test_alloc_plan_iter.c):
|
|
*
|
|
* xce_host_iter_t host_iter_storage;
|
|
* xce_host_iter_t* host_iter = &host_iter_storage;
|
|
*
|
|
* host_iter->init = sched_host_iter_init;
|
|
* host_iter->next = sched_host_iter_next;
|
|
* host_iter->fini = sched_host_iter_fini;
|
|
*
|
|
* if (host_iter->init(host_iter, plan, sched->core.workers) == 0) { ... }
|
|
*
|
|
* plan->ctx (see xce_alloc_plan_t above) must be non-NULL: it supplies the
|
|
* xce_scheduler_t/select-expression/resource-want state this
|
|
* implementation needs but init()'s own (iter, plan, workers) parameters
|
|
* cannot reach on their own.
|
|
*/
|
|
extern int sched_host_iter_init(xce_host_iter_t* iter, xce_alloc_plan_t* plan,
|
|
var workers);
|
|
extern xce_sched_worker_t* sched_host_iter_next(xce_host_iter_t* iter);
|
|
extern void sched_host_iter_fini(xce_host_iter_t* iter);
|
|
|
|
#endif
|