skip to content
==> src/fleet_plan.c <==
1/* fleet.rs: Actual, Action, plan, seed. */
2#include "mesh/fleet_plan.h"
3#include "mesh/mt.h"
4
5#include <stdlib.h>
6#include <string.h>
7
8mfl_actual mfl_actual_new(const char *name, const char *dir, uint64_t hash, bool has_prev, uint64_t prev, bool run) {
9    return (mfl_actual){mt_strdup(name), mt_strdup(dir), hash, has_prev, prev, run, false, false};
10}
11
12void mfl_actual_free(mfl_actual *a) {
13    free(a->name);
14    free(a->dir);
15    *a = (mfl_actual){0};
16}
17
18bool mfl_action_eq(const mfl_action *a, const mfl_action *b) {
19    if (a->kind != b->kind || strcmp(a->name, b->name)) return false;
20    switch (a->kind) {
21    case MFL_LOAD: return strcmp(a->dir, b->dir) == 0 && a->hash == b->hash && a->run == b->run;
22    case MFL_UPGRADE: return strcmp(a->dir, b->dir) == 0 && a->hash == b->hash;
23    case MFL_RUN: return a->run == b->run;
24    case MFL_MIGRATE: return strcmp(a->dir, b->dir) == 0 && a->hash == b->hash && a->run == b->run && a->from == b->from;
25    default: return true;
26    }
27}
28
29void mfl_actions_free(mfl_actions *a) {
30    for (size_t i = 0; i < a->n; i++) {
31        free(a->v[i].name);
32        free(a->v[i].dir);
33    }
34    free(a->v);
35    *a = (mfl_actions){0};
36}
37
38static void add(mfl_actions *a, mfl_kind k, const char *name, const char *dir, uint64_t hash, bool run, uint32_t from) {
39    a->v = mt_realloc(a->v, (a->n + 1) * sizeof *a->v);
40    a->v[a->n++] = (mfl_action){k, mt_strdup(name), dir ? mt_strdup(dir) : NULL, hash, run, from};
41}
42
43static const mfl_actual *find(const mfl_actual *actual, size_t n, const char *name) {
44    for (size_t i = 0; i < n; i++)
45        if (strcmp(actual[i].name, name) == 0) return &actual[i];
46    return NULL;
47}
48
49/* The source of a migration keeps the program as it is (the target takes its view, then the leader releases it). */
50static bool placed(const mfd_prog *p, uint32_t me) { return mfd_prog_here(p, me) && !(p->has_from && p->from == me); }
51
52mfl_actions mfl_plan(const mfd_doc *doc, const mfl_actual *actual, size_t nactual, mfl_skip_fn skip, void *ctx, const char *const *reset_names,
53                     const uint32_t *reset_gens, size_t nresets, uint32_t me) {
54    mfl_actions a = {0};
55    for (size_t i = 0; i < nactual; i++) {
56        const mfd_prog *p = mfd_prog_get(doc, actual[i].name);
57        if (!p || !mfd_prog_here(p, me)) add(&a, MFL_UNLOAD, actual[i].name, NULL, 0, false, 0);
58    }
59    for (size_t i = 0; i < doc->nprogs; i++) {
60        const char *n = doc->pname[i];
61        const mfd_prog *p = &doc->prog[i];
62        if (skip(ctx, n, p->hash) || !placed(p, me)) continue;
63        const mfl_actual *x = find(actual, nactual, n);
64        if (!x) {
65            if (p->has_from) add(&a, MFL_MIGRATE, n, p->dir, p->hash, p->run, p->from);
66            else add(&a, MFL_LOAD, n, p->dir, p->hash, p->run, 0);
67        } else if (x->hash != p->hash) {
68            if (x->has_prev && x->prev == p->hash) add(&a, MFL_ROLLBACK, n, NULL, 0, false, 0);
69            else add(&a, MFL_UPGRADE, n, p->dir, p->hash, false, 0);
70        }
71    }
72    for (size_t i = 0; i < doc->nprogs; i++) {
73        const char *n = doc->pname[i];
74        const mfd_prog *p = &doc->prog[i];
75        if (skip(ctx, n, p->hash) || !placed(p, me)) continue;
76        const mfl_actual *x = find(actual, nactual, n);
77        if (x && (x->run != p->run || (p->run && x->failed))) add(&a, MFL_RUN, n, NULL, 0, p->run, 0);
78        /* A fresh load is a reset; a lockstep program's resets are its group's epochs. */
79        uint32_t applied = 0;
80        for (size_t r = 0; r < nresets; r++)
81            if (strcmp(reset_names[r], n) == 0) applied = reset_gens[r];
82        if (!p->lockstep && x && applied < p->reset) add(&a, MFL_RESET, n, NULL, 0, false, 0);
83    }
84    return a;
85}
86
87mfd_doc mfl_seed(char *const *names, const mfd_prog *specs, size_t n, const ml_list *up, mfl_backend_fn backend_of, void *ctx) {
88    mfd_doc d = {0};
89    for (size_t i = 0; i < n; i++) mfd_prog_put(&d, names[i], mfd_prog_clone(&specs[i]));
90    for (size_t i = 0; i < d.nprogs; i++) {
91        const mfd_prog *p = &d.prog[i];
92        if (!p->lockstep) continue;
93        ml_list members = {0};
94        for (size_t j = 0; j < up->n; j++)
95            if (mfd_can_run(&p->backends, backend_of(ctx, up->v[j]))) ml_push(&members, up->v[j]);
96        mfd_group g = {.name = mt_strdup(p->needs.group ? p->needs.group : d.pname[i]), .program = mt_strdup(d.pname[i]), .epoch = 1, .members = members,
97                       .state = MFD_RUNNING, .on_loss = MFD_LOSS_PAUSE};
98        mfd_group_push(&d, g);
99    }
100    return d;
101}