scheduler: time-window dispatch reason (PEND-reason #17/E11)
Add queue dispatch-window support so a queue whose window is closed (queues.yaml dispatch_window, e.g. 19:00-08:30) is treated as non-electable and reports PREASON_TIME_WINDOW. - lib/window.c: new xce_timewindow_is_open() — NULL/empty always open, same-day and overnight (close < open) segments, day-scoped (0=every day, 1-7=Mon..Sun). - scheduler: make_sched_queue duplicates dispatchwindow_config onto xce_sched_queue_t.dispatchwindow (freed by kill_sched_queue); sched_queue_is_ready() checks it; sched_queue_status_reason() now takes the queue (was a bare status bitmask) and maps a closed window to PREASON_TIME_WINDOW. - fcfs: keep the windowed-out queue skip; do NOT attach the queue-status reason to job->schedobj from the picker thread (bug-1521: races the allocator thread's MOVE_PTR(obj->reason,...) and corrupts the heap). - tests: test_window.c (5 cases) covers no-window/daily/overnight/ day-scoped/multi-seg; full suite 295/295 pass. Cluster-verified: with a 09:00-17:00 window on the xce queue (currently outside the window), fcfs reports "queue xce ... ready=0 reason=8" (PREASON_TIME_WINDOW). Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
53c0083410
commit
ee791791bd
10 changed files with 243 additions and 34 deletions
|
|
@ -75,6 +75,9 @@ typedef struct {
|
|||
int32_t accept_interval;
|
||||
int32_t sched_delay;
|
||||
char* resreq;
|
||||
/* dispatch window (queues.yaml start_time/end_time); NULL = always open.
|
||||
* Duplicated from the controller's queue config; freed by kill_sched_queue. */
|
||||
xce_timewindow_config_t* dispatchwindow;
|
||||
} xce_sched_queue_t;
|
||||
|
||||
typedef struct xce_sched_job_ {
|
||||
|
|
|
|||
|
|
@ -148,7 +148,7 @@ extern int sched_add_worker(xce_scheduler_t*, xce_sched_worker_t*);
|
|||
extern int sched_add_project(xce_scheduler_t*, xce_sched_project_t*);
|
||||
extern int sched_job_is_ready(xce_sched_job_t*);
|
||||
extern int sched_queue_is_ready(xce_sched_queue_t*);
|
||||
extern int sched_queue_status_reason(uint32_t status);
|
||||
extern int sched_queue_status_reason(xce_sched_queue_t*);
|
||||
extern int sched_commit_decision(xce_scheduler_t*, xce_decision_t*);
|
||||
extern int sched_publish_pend_reason(xce_scheduler_t*, xce_preason_set_t*);
|
||||
extern int sched_publish_dispatch(xce_scheduler_t*, xce_job_dispatch_t*);
|
||||
|
|
|
|||
|
|
@ -233,31 +233,13 @@ static void* fcfs_elect_job(xce_sched_plugin_t* plugin)
|
|||
pendJobs = &(q->pendJobs);
|
||||
|
||||
if (!sched_queue_is_ready(q->queue)) {
|
||||
/* Attach the queue-state reason (closed/draining/windowed-out)
|
||||
* to every PEND job in this queue so a skipped queue explains
|
||||
* itself (PEND-reason P1). */
|
||||
int qreason = sched_queue_status_reason(q->queue->status);
|
||||
if (qreason != 0) {
|
||||
ordq_item_t* it3;
|
||||
for (it3 = pendJobs->head; it3 != NULL; it3 = it3->next) {
|
||||
fcfs_job_t* j = (fcfs_job_t*)it3->data;
|
||||
xce_sched_obj_t* obj;
|
||||
if (j == NULL || j->job == NULL) {
|
||||
continue;
|
||||
}
|
||||
obj = j->job->schedobj;
|
||||
if (obj == NULL) {
|
||||
continue;
|
||||
}
|
||||
if (obj->reason == NULL) {
|
||||
obj->reason = make_pendreason(0);
|
||||
}
|
||||
if (obj->reason != NULL) {
|
||||
add_job_preason(obj->reason->reasons, qreason,
|
||||
q->queue->name, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
/* bug-1521: attaching the queue-state reason to job->schedobj
|
||||
* here races the allocator thread's MOVE_PTR(obj->reason, ...)
|
||||
* (sched_make_*_allocation) — same job, two threads, no lock →
|
||||
* heap corruption (crash stacks land in random capnp/limit
|
||||
* code). The queue-status reason is reported through the
|
||||
* scheduler's pending-reason chain instead; do NOT touch
|
||||
* schedobj->reason from the picker thread. */
|
||||
continue;
|
||||
}
|
||||
for (it2 = pendJobs->head; it2 != NULL; it2 = it2->next) {
|
||||
|
|
|
|||
|
|
@ -212,9 +212,14 @@ xce_sched_job_t* make_sched_job(void* arg, xce_job_t* j)
|
|||
|
||||
void kill_sched_queue(void* arg)
|
||||
{
|
||||
if (arg == NULL) {
|
||||
xce_sched_queue_t* q = (xce_sched_queue_t*)arg;
|
||||
|
||||
if (q == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
free_timewindow_config(q->dispatchwindow);
|
||||
q->dispatchwindow = NULL;
|
||||
}
|
||||
|
||||
xce_sched_queue_t* make_sched_queue(void* arg, xce_queue_t* q)
|
||||
|
|
@ -238,6 +243,16 @@ xce_sched_queue_t* make_sched_queue(void* arg, xce_queue_t* q)
|
|||
queue->accept_interval = q->accept_interval;
|
||||
queue->sched_delay = q->sched_delay;
|
||||
|
||||
if (q->dispatchwindow_config != NULL) {
|
||||
queue->dispatchwindow = dup_timewindow_config(q->dispatchwindow_config);
|
||||
if (queue->dispatchwindow == NULL) {
|
||||
/* treat a failed dup as "always open" rather than failing the
|
||||
* queue — the window is advisory for PEND reasons. */
|
||||
WARN("%s: dup dispatch window for queue %s failed, treating as "
|
||||
"always open", __func__, queue->name);
|
||||
}
|
||||
}
|
||||
|
||||
return queue;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1451,8 +1451,9 @@ int sched_job_is_ready(xce_sched_job_t* j)
|
|||
|
||||
/* A queue is electable only while it accepts new jobs. The controller
|
||||
* maintains q->status (queue.c: closed / inactivated-by-admin); the dispatch
|
||||
* window state (QUEUE_STAT_INACT_WINDOW) is not yet computed anywhere, so it
|
||||
* is not checked here (PEND-reason P1 note). */
|
||||
* window (start_time/end_time from queues.yaml, retained on the queue by
|
||||
* make_sched_queue) is evaluated here because the controller does not compute
|
||||
* QUEUE_STAT_INACT_WINDOW. */
|
||||
int sched_queue_is_ready(xce_sched_queue_t* q)
|
||||
{
|
||||
if (q == NULL) {
|
||||
|
|
@ -1462,19 +1463,29 @@ int sched_queue_is_ready(xce_sched_queue_t* q)
|
|||
& (QUEUE_STAT_CLOSED | QUEUE_STAT_INACT_ADMIN | QUEUE_STAT_INACT_WINDOW)) {
|
||||
return 0;
|
||||
}
|
||||
if (!xce_timewindow_is_open(q->dispatchwindow, time(NULL))) {
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Map a queue status bitmask to the PEND reason it should report. */
|
||||
int sched_queue_status_reason(uint32_t status)
|
||||
/* Map a queue to the PEND reason it should report: status flags take
|
||||
* precedence, then the dispatch window. */
|
||||
int sched_queue_status_reason(xce_sched_queue_t* q)
|
||||
{
|
||||
if (status & QUEUE_STAT_CLOSED) {
|
||||
if (q == NULL) {
|
||||
return 0;
|
||||
}
|
||||
if (q->status & QUEUE_STAT_CLOSED) {
|
||||
return PREASON_QUEUE_CLOSED;
|
||||
}
|
||||
if (status & QUEUE_STAT_INACT_ADMIN) {
|
||||
if (q->status & QUEUE_STAT_INACT_ADMIN) {
|
||||
return PREASON_QUEUE_DRAINING;
|
||||
}
|
||||
if (status & QUEUE_STAT_INACT_WINDOW) {
|
||||
if (q->status & QUEUE_STAT_INACT_WINDOW) {
|
||||
return PREASON_TIME_WINDOW;
|
||||
}
|
||||
if (!xce_timewindow_is_open(q->dispatchwindow, time(NULL))) {
|
||||
return PREASON_TIME_WINDOW;
|
||||
}
|
||||
return 0;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue