skip to content
==> src/select.c <==
1/* jobs.rs's select and Pick. */
2#include "mesh/jobs/select.h"
3
4#include <stdlib.h>
5#include <string.h>
6
7void mj_picks_free(mj_picks *p) {
8    for (size_t i = 0; i < p->n; i++) {
9        free(p->v[i].score.explain);
10        free(p->v[i].why);
11    }
12    free(p->v);
13    *p = (mj_picks){0};
14}
15
16mj_picks mj_picks_clone(const mj_picks *p) {
17    mj_picks c = {p->n ? mxalloc(p->n * sizeof *c.v) : NULL, p->n};
18    for (size_t i = 0; i < p->n; i++) {
19        c.v[i] = p->v[i];
20        c.v[i].score.explain = p->v[i].score.explain ? mstrdup(p->v[i].score.explain) : NULL;
21        c.v[i].why = p->v[i].why ? mstrdup(p->v[i].why) : NULL;
22    }
23    return c;
24}
25
26char *mj_pick_line(const mj_pick *p) {
27    if (p->ok)
28        return mfmt("pick system %u eligible %s%s", p->member, p->score.explain ? p->score.explain : "",
29                    p->busy ? " busy: floor-sized leases (the only member)" : "");
30    return mfmt("pick system %u rejected: %s", p->member, p->why ? p->why : "");
31}
32
33const mj_pick *mj_picks_ok(const mj_picks *p, uint32_t m) {
34    for (size_t i = 0; i < p->n; i++)
35        if (p->v[i].member == m && p->v[i].ok) return &p->v[i];
36    return NULL;
37}
38
39void mj_member_progs_free(mj_member_progs *p) {
40    for (uint32_t k = 0; k < MJ_MEMBERS; k++) mj_progs_free(p->v[k], p->n[k]);
41    memset(p, 0, sizeof *p);
42}
43
44void mj_member_progs_set(mj_member_progs *p, uint32_t k, mj_prog *progs, size_t n) {
45    mj_progs_free(p->v[k], p->n[k]);
46    p->has[k] = true;
47    p->v[k] = progs;
48    p->n[k] = n;
49}
50
51void mj_member_progs_remove(mj_member_progs *p, uint32_t k) {
52    mj_progs_free(p->v[k], p->n[k]);
53    p->has[k] = false;
54    p->v[k] = NULL;
55    p->n[k] = 0;
56}
57
58/* The job's first program member k runs (a multi-program job: any of them decides). */
59static const char *runs_which(const mj_spec *job, const mj_member_progs *progs, uint32_t k, size_t *len) {
60    if (k >= MJ_MEMBERS || !progs->has[k]) return NULL;
61    const char *p = job->program;
62    for (;;) {
63        const char *c = strchr(p, ',');
64        size_t n = c ? (size_t)(c - p) : strlen(p);
65        for (size_t i = 0; i < progs->n[k]; i++)
66            if (strlen(progs->v[k][i].name) == n && memcmp(progs->v[k][i].name, p, n) == 0) {
67                *len = n;
68                return p;
69            }
70        if (!c) return NULL;
71        p = c + 1;
72    }
73}
74
75static int rank_cmp(const mj_pick *a, const mj_pick *b) {
76    int ka = a->ok ? 0 : 1, kb = b->ok ? 0 : 1;
77    if (ka != kb) return ka < kb ? -1 : 1;
78    double ta = a->ok ? -a->score.total : 0.0, tb = b->ok ? -b->score.total : 0.0;
79    if (ta != tb) return ta < tb ? -1 : 1; /* the totals are never NaN */
80    return a->member < b->member ? -1 : a->member > b->member ? 1 : 0;
81}
82
83mj_picks mj_select(const mj_spec *job, uint32_t alive, const mj_member_progs *progs, const mj_sched *cx) {
84    uint32_t allowed = job->members == 0 ? UINT32_MAX : job->members;
85    uint32_t n = cx ? cx->nmembers : 0;
86    for (uint32_t k = 0; k < MJ_MEMBERS; k++)
87        if (progs->has[k] && k + 1 > n) n = k + 1;
88    mj_picks out = {n ? mxalloc(n * sizeof *out.v) : NULL, n};
89    for (uint32_t k = 0; k < n; k++) {
90        mj_pick *p = &out.v[k];
91        p->member = k;
92        size_t len = 0;
93        const char *which = runs_which(job, progs, k, &len);
94        if ((alive >> k & 1) == 0) p->why = mstrdup("down");
95        else if ((allowed >> k & 1) == 0) p->why = mstrdup("not in the job's members");
96        else if (!which) p->why = mfmt("%s does not run there (not placed there, or not started)", job->program);
97        else if (!cx || k >= cx->nmembers) {
98            p->ok = true;
99            p->score.explain = mstrdup(MJ_SCORE_DEFAULT_EXPLAIN);
100        } else {
101            char *prog = mstrndup(which, len);
102            bool busy = false;
103            int v = cx->judge(cx->ctx, k, prog, &p->score, &busy, &p->why);
104            free(prog);
105            if (v < 0) {
106                p->ok = true;
107                p->score = (mj_score){0.0, mstrdup(MJ_SCORE_DEFAULT_EXPLAIN)};
108            } else if (v > 0) {
109                p->ok = true;
110                p->busy = busy;
111            }
112        }
113    }
114    /* HTCondor's backoff: a busy member gets leases only when no idle member runs the program. */
115    bool idle = false;
116    for (uint32_t k = 0; k < n; k++)
117        if (out.v[k].ok && !out.v[k].busy) idle = true;
118    if (idle)
119        for (uint32_t k = 0; k < n; k++) {
120            mj_pick *p = &out.v[k];
121            if (!p->ok || !p->busy) continue;
122            char *why = cx && cx->busy_why ? cx->busy_why(cx->ctx, p->member) : NULL;
123            free(p->score.explain);
124            p->score = (mj_score){0};
125            p->ok = false;
126            p->why = mfmt("busy (owner policy: %s): leases go to the idle members", why ? why : "");
127            p->busy = false;
128            free(why);
129        }
130    /* A stable sort: eligible first by score, then the rest, each by member id. */
131    for (uint32_t i = 1; i < n; i++) {
132        mj_pick x = out.v[i];
133        uint32_t j = i;
134        while (j > 0 && rank_cmp(&out.v[j - 1], &x) > 0) {
135            out.v[j] = out.v[j - 1];
136            j--;
137        }
138        out.v[j] = x;
139    }
140    return out;
141}