==> 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}