skip to content
==> src/shared.c <==
1/* meshd's shared state, its messages, and the requests other threads make of the driver and the fleet. */
2#include "mesh/shared.h"
3#include "mesh/buf.h"
4#include "mesh/log.h"
5#include "mesh/rsfmt.h"
6
7#include <stdlib.h>
8#include <string.h>
9
10/* ---- Src ---- */
11
12mesh_src mesh_src_catalog(const char *dir) {
13    mesh_src s;
14    memset(&s, 0, sizeof s);
15    s.dir = mesh_xstrdup(dir);
16    return s;
17}
18
19mesh_src mesh_src_bytes(const mesh_payload *p, uint64_t hash) {
20    mesh_src s;
21    memset(&s, 0, sizeof s);
22    s.is_bytes = true;
23    mesh_payload_copy(&s.payload, p);
24    s.hash = hash;
25    return s;
26}
27
28mesh_src mesh_src_copy(const mesh_src *s) {
29    if (s->is_bytes) return mesh_src_bytes(&s->payload, s->hash);
30    return mesh_src_catalog(s->dir ? s->dir : "");
31}
32
33void mesh_src_free(mesh_src *s) {
34    free(s->dir);
35    if (s->is_bytes) mesh_payload_free(&s->payload);
36    memset(s, 0, sizeof *s);
37}
38
39char *mesh_src_what(const mesh_src *s) {
40    if (s->is_bytes) return mesh_buf_fmt("%s (from the peer)", s->payload.dir ? s->payload.dir : "");
41    return mesh_xstrdup(s->dir ? s->dir : "");
42}
43
44/* ---- Cmd ---- */
45
46mesh_cmd mesh_cmd_new(mesh_cmd_kind k) {
47    mesh_cmd c;
48    memset(&c, 0, sizeof c);
49    c.k = k;
50    c.run = -1;
51    return c;
52}
53
54static void strv_free(char **v, size_t n) {
55    for (size_t i = 0; i < n; i++) free(v[i]);
56    free(v);
57}
58
59void mesh_cmd_free(mesh_cmd *c) {
60    free(c->name);
61    if (c->has_src) mesh_src_free(&c->src);
62    free(c->bytes);
63    strv_free(c->words, c->nwords);
64    if (c->msg && c->msg_free) c->msg_free(c->msg);
65    for (size_t i = 0; i < c->nplaces; i++) { free(c->places[i].name); strv_free(c->places[i].words, c->places[i].nwords); }
66    free(c->places);
67    if (c->k == MESH_CMD_JOB_LEASE) mj_lease_msg_free(&c->lease);
68    mesh_reply_drop(c->reply);
69    memset(c, 0, sizeof *c);
70}
71
72void mesh_actuals_free(void *p) {
73    mesh_actuals *a = p;
74    if (!a) return;
75    for (size_t i = 0; i < a->n; i++) { free(a->v[i].name); free(a->v[i].dir); }
76    free(a->v);
77    free(a);
78}
79
80void mesh_view_bytes_free(void *p) {
81    mesh_view_bytes *v = p;
82    if (!v) return;
83    free(v->bytes);
84    free(v);
85}
86
87void mesh_job_progs_free(void *p) {
88    mesh_job_progs *j = p;
89    if (!j) return;
90    mj_progs_free(j->v, j->n);
91    free(j);
92}
93
94/* ---- PeerEvent, JobEvent ---- */
95
96mesh_peer_event mesh_peer_event_new(mesh_peer_event_kind k) {
97    mesh_peer_event e;
98    memset(&e, 0, sizeof e);
99    e.k = k;
100    return e;
101}
102
103void mesh_peer_event_free(mesh_peer_event *e) {
104    free(e->line);
105    free(e->text);
106    free(e->name);
107    free(e->dir);
108    if (e->msg && e->msg_free) e->msg_free(e->msg);
109    mesh_reply_drop(e->reply);
110    memset(e, 0, sizeof *e);
111}
112
113mesh_job_event mesh_job_event_new(mesh_job_event_kind k) {
114    mesh_job_event e;
115    memset(&e, 0, sizeof e);
116    e.k = k;
117    return e;
118}
119
120void mesh_job_event_free(mesh_job_event *e) {
121    free(e->body);
122    free(e->line);
123    mesh_reply_drop(e->reply);
124    if (e->k == MESH_JEV_DONE) mj_item_res_free(&e->res);
125    free(e->payload);
126    if (e->msg && e->msg_free) e->msg_free(e->msg);
127    memset(e, 0, sizeof *e);
128}
129
130/* ---- link frames ---- */
131
132static void drop_cmd(void *p) { mesh_cmd_free(p); }
133static void drop_pev(void *p) { mesh_peer_event_free(p); }
134static void drop_jev(void *p) { mesh_job_event_free(p); }
135static void drop_frame(void *p) { free(((mesh_link_frame *)p)->b); }
136
137mesh_chan *mesh_link_queue_new(size_t bound) { return mesh_chan_new(sizeof(mesh_link_frame), bound, drop_frame); }
138
139/* ---- Shared ---- */
140
141uint64_t mesh_shared_mono_us(void) {
142    static _Atomic int64_t t0;
143    int64_t z = 0, now = mesh_mono_us();
144    atomic_compare_exchange_strong(&t0, &z, now);
145    return (uint64_t)(now - atomic_load(&t0));
146}
147
148mesh_shared *mesh_shared_new(mesh_mailbox mb, const char *programs, bool peered, uint32_t me, uint32_t n, _Atomic bool *quit) {
149    mesh_shared_mono_us();
150    mesh_shared *sh = mesh_xcalloc(1, sizeof *sh);
151    sh->mb = mb;
152    mesh_host_block_init(&sh->host);
153    sh->programs = mesh_xstrdup(programs);
154    sh->cmd = mesh_chan_new(sizeof(mesh_cmd), 0, drop_cmd);
155    sh->peer = peered ? mesh_chan_new(sizeof(mesh_peer_event), 0, drop_pev) : NULL;
156    sh->jobs = mesh_chan_new(sizeof(mesh_job_event), 0, drop_jev);
157    sh->blobs = NULL;
158    sh->me = me;
159    sh->n = n;
160    atomic_init(&sh->peer_state, MESH_PEER_SINGLE);
161    atomic_init(&sh->lock_feed, false);
162    for (size_t k = 0; k < MESH_MAX_SYSTEMS; k++) atomic_init(&sh->round_of[k], 0);
163    atomic_init(&sh->ready, false);
164    atomic_init(&sh->fatal, false);
165    atomic_init(&sh->blob_mem, 0);
166    sh->quit = quit;
167    sh->fleet_text = mesh_xstrdup("");
168    sh->groups_text = mesh_xstrdup("");
169    sh->sched_all = mesh_xstrdup("");
170    sh->sched_dry = mesh_xstrdup("");
171    pthread_mutex_init(&sh->mu, NULL);
172    return sh;
173}
174
175void mesh_shared_free(mesh_shared *sh) {
176    if (!sh) return;
177    mesh_chan_release(sh->cmd);
178    if (sh->peer) mesh_chan_release(sh->peer);
179    mesh_chan_release(sh->jobs);
180    for (size_t k = 0; k < MESH_MAX_SYSTEMS; k++) {
181        if (sh->link_out[k]) mesh_chan_release(sh->link_out[k]);
182        if (sh->bulk_out[k]) mesh_chan_release(sh->bulk_out[k]);
183    }
184    free(sh->programs);
185    free(sh->fleet_text);
186    free(sh->groups_text);
187    free(sh->sched_all);
188    free(sh->sched_dry);
189    for (size_t i = 0; i < sh->nexplain; i++) { free(sh->sched_explain[i].name); free(sh->sched_explain[i].text); }
190    free(sh->sched_explain);
191    mesh_recs_free(&sh->fleet_view);
192    mesh_recs_free(&sh->jobs_view_recs);
193    free(sh->pipe_hist);
194    pthread_mutex_destroy(&sh->mu);
195    free(sh);
196}
197
198static mesh_link_frame frame_copy(const uint8_t *f, size_t n) {
199    mesh_link_frame x = {mesh_xmalloc(n ? n : 1), n};
200    if (n) memcpy(x.b, f, n);
201    return x;
202}
203
204/* try_send of a copy to a queue held under mu; the copy is freed when refused. */
205static int try_send(mesh_chan *q, const uint8_t *f, size_t n) {
206    mesh_link_frame x = frame_copy(f, n);
207    int r = mesh_chan_try_send(q, &x);
208    if (r != MESH_CHAN_OK) free(x.b);
209    return r;
210}
211
212bool mesh_shared_send_peer(mesh_shared *sh, uint32_t to, const uint8_t *frame, size_t n) {
213    if (atomic_load(&sh->fatal) || to >= MESH_MAX_SYSTEMS) return false;
214    pthread_mutex_lock(&sh->mu);
215    mesh_chan *q = sh->link_out[to];
216    int r = q ? try_send(q, frame, n) : MESH_CHAN_CLOSED;
217    pthread_mutex_unlock(&sh->mu);
218    return r == MESH_CHAN_OK;
219}
220
221bool mesh_shared_send_peer_wait(mesh_shared *sh, uint32_t to, const uint8_t *frame, size_t n, int64_t within_ms) {
222    if (atomic_load(&sh->fatal) || to >= MESH_MAX_SYSTEMS) return false;
223    int64_t t0 = mesh_mono_ms();
224    for (;;) {
225        pthread_mutex_lock(&sh->mu);
226        mesh_chan *q = sh->link_out[to];
227        int r = q ? try_send(q, frame, n) : MESH_CHAN_CLOSED;
228        pthread_mutex_unlock(&sh->mu);
229        if (r == MESH_CHAN_OK) return true;
230        if (r != MESH_CHAN_FULL || mesh_mono_ms() - t0 >= within_ms) return false;
231        mesh_sleep_us(500);
232    }
233}
234
235bool mesh_shared_send_bulk(mesh_shared *sh, uint32_t to, const uint8_t *frame, size_t n) {
236    if (atomic_load(&sh->fatal) || to >= MESH_MAX_SYSTEMS) return false;
237    pthread_mutex_lock(&sh->mu);
238    mesh_chan *q = sh->bulk_out[to];
239    bool ok = q && try_send(q, frame, n) == MESH_CHAN_OK;
240    /* A doorbell (an empty frame) wakes the writer if it waits on its control queue; a full one is awake. */
241    if (ok && sh->link_out[to]) try_send(sh->link_out[to], NULL, 0);
242    pthread_mutex_unlock(&sh->mu);
243    return ok;
244}
245
246bool mesh_shared_send_mask(mesh_shared *sh, uint32_t mask, const uint8_t *frame, size_t n) {
247    if (atomic_load(&sh->fatal)) return false;
248    pthread_mutex_lock(&sh->mu);
249    bool any = false, ok = true;
250    for (uint32_t k = 0; k < MESH_MAX_SYSTEMS; k++) {
251        if (!((mask >> k) & 1u) || !sh->link_out[k]) continue;
252        any = true;
253        ok &= try_send(sh->link_out[k], frame, n) == MESH_CHAN_OK;
254    }
255    pthread_mutex_unlock(&sh->mu);
256    return any && ok;
257}
258
259bool mesh_shared_broadcast(mesh_shared *sh, const uint8_t *frame, size_t n) { return mesh_shared_send_mask(sh, UINT32_MAX, frame, n); }
260
261void mesh_shared_fail_stop(mesh_shared *sh, const char *why) {
262    if (!atomic_exchange(&sh->fatal, true)) mesh_error("FATAL: %s: fail-stop (nothing more is sent; meshd exits)", why);
263    atomic_store(sh->quit, true);
264}
265
266static char *within_text(const char *prefix, int64_t within_ms) {
267    mesh_buf b = MESH_BUF_INIT;
268    mesh_buf_puts(&b, prefix);
269    mesh_rs_duration_ms(&b, (uint64_t)within_ms);
270    return mesh_buf_take(&b);
271}
272
273bool mesh_shared_ask(mesh_shared *sh, mesh_cmd cmd, int64_t within_ms, char **text) {
274    mesh_reply *r = mesh_reply_new();
275    mesh_reply_drop(cmd.reply);
276    cmd.reply = mesh_reply_sender(r);
277    if (mesh_chan_send(sh->cmd, &cmd) != MESH_CHAN_OK) {
278        mesh_cmd_free(&cmd);
279        mesh_reply_release(r);
280        *text = mesh_xstrdup("driver gone");
281        return false;
282    }
283    mesh_result x;
284    int w = mesh_reply_wait(r, within_ms, &x);
285    mesh_reply_release(r);
286    if (w != MESH_CHAN_OK) { *text = within_text("no answer from the driver in ", within_ms); return false; }
287    *text = x.text ? x.text : mesh_xstrdup("");
288    x.text = NULL;
289    bool ok = x.ok;
290    mesh_result_free(&x);
291    return ok;
292}
293
294void mesh_shared_set_peer_state(mesh_shared *sh, uint32_t s) {
295    atomic_store(&sh->peer_state, s);
296    mesh_host_block_set_u32(&sh->host, MESH_S_PEER_STATE, s);
297}
298
299bool mesh_shared_change(mesh_shared *sh, const char *line, int64_t within_ms, char **text) {
300    if (!sh->peer) { *text = mesh_xstrdup("not in a fleet"); return false; }
301    mesh_reply *r = mesh_reply_new();
302    mesh_peer_event e = mesh_peer_event_new(MESH_PEV_CHANGE);
303    e.line = mesh_xstrdup(line);
304    e.reply = mesh_reply_sender(r);
305    if (mesh_chan_send(sh->peer, &e) != MESH_CHAN_OK) {
306        mesh_peer_event_free(&e);
307        mesh_reply_release(r);
308        *text = mesh_xstrdup("fleet thread gone");
309        return false;
310    }
311    mesh_result x;
312    int w = mesh_reply_wait(r, within_ms, &x);
313    mesh_reply_release(r);
314    if (w != MESH_CHAN_OK) { *text = within_text("peer: no answer from the fleet in ", within_ms); return false; }
315    *text = x.text ? x.text : mesh_xstrdup("");
316    x.text = NULL;
317    bool ok = x.ok;
318    mesh_result_free(&x);
319    return ok;
320}
321
322bool mesh_shared_http(mesh_shared *sh, const uint8_t *head, size_t n, uint8_t **resp, size_t *rn, char **err) {
323    size_t l = 0;
324    while (l < n && head[l] != '\r' && head[l] != '\n') l++;
325    mesh_buf line = MESH_BUF_INIT;
326    mesh_rs_lossy(&line, head, l);
327    mesh_reply *r = mesh_reply_new();
328    mesh_cmd c = mesh_cmd_new(MESH_CMD_REQ);
329    c.name = mesh_xstrdup("http");
330    c.bytes = mesh_xmalloc(n ? n : 1);
331    memcpy(c.bytes, head, n);
332    c.len = n;
333    c.reply = mesh_reply_sender(r);
334    if (mesh_chan_send(sh->cmd, &c) != MESH_CHAN_OK) {
335        mesh_cmd_free(&c);
336        mesh_reply_release(r);
337        mesh_buf_free(&line);
338        *err = mesh_xstrdup("driver gone");
339        return false;
340    }
341    mesh_result x;
342    int w = mesh_reply_wait(r, 5000, &x);
343    mesh_reply_release(r);
344    if (w != MESH_CHAN_OK) { mesh_buf_free(&line); *err = mesh_xstrdup("GPU http timed out"); return false; }
345    if (!x.ok) {
346        *err = x.text ? x.text : mesh_xstrdup("");
347        x.text = NULL;
348        mesh_result_free(&x);
349        mesh_buf_free(&line);
350        return false;
351    }
352    mesh_host_block_add_u32(&sh->host, MESH_S_HTTP_REQUESTS, 1);
353    mesh_host_block_set_str(&sh->host, MESH_S_LAST_HTTP, 64, mesh_buf_str(&line));
354    mesh_buf_free(&line);
355    *resp = x.bytes ? x.bytes : mesh_xmalloc(1);
356    *rn = x.len;
357    x.bytes = NULL;
358    mesh_result_free(&x);
359    return true;
360}
361
362char *mesh_shared_text(mesh_shared *sh, char *const *field) {
363    pthread_mutex_lock(&sh->mu);
364    char *t = mesh_xstrdup(*field ? *field : "");
365    pthread_mutex_unlock(&sh->mu);
366    return t;
367}
368
369void mesh_shared_set_text(mesh_shared *sh, char **field, const char *text) {
370    char *t = mesh_xstrdup(text);
371    pthread_mutex_lock(&sh->mu);
372    free(*field);
373    *field = t;
374    pthread_mutex_unlock(&sh->mu);
375}