skip to content
==> src/publish.c <==
1/* Ticket rings and the publishing policies. */
2#include "mesh/publish.h"
3#include "mesh/buf.h"
4
5#include <stdlib.h>
6#include <string.h>
7
8void mesh_pring_init(mesh_pring *r, uint32_t slot) {
9    memset(r, 0, sizeof *r);
10    r->ctl = mesh_prog_ctl(slot);
11}
12
13void mesh_pring_free(mesh_pring *r) {
14    for (size_t i = 0; i < r->n; i++) mesh_reply_drop(r->inflight[i].kind.reply);
15    free(r->inflight);
16    r->inflight = NULL;
17    r->n = r->cap = 0;
18}
19
20static size_t entry(const mesh_pring *r, uint32_t t) { return r->ctl + MESH_P_RING + MESH_ENTRY_BYTES * (size_t)(t % MESH_PROG_RING); }
21
22uint32_t mesh_pring_room(const mesh_pring *r) {
23    if (!r->n) return MESH_PROG_RING;
24    uint32_t used = r->tail - r->inflight[0].ticket;
25    return used >= MESH_PROG_RING ? 0 : MESH_PROG_RING - used;
26}
27
28bool mesh_pring_can_publish(const mesh_pring *r) { return mesh_pring_room(r) > 0; }
29
30bool mesh_pring_publish(mesh_pring *r, mesh_mailbox mb, uint32_t op, uint32_t arg, mesh_kind kind, uint32_t *ticket) {
31    if (!mesh_pring_can_publish(r)) return false;
32    uint32_t t = r->tail;
33    size_t e = entry(r, t);
34    mesh_mailbox_set(mb, e + MESH_E_OP, op);
35    mesh_mailbox_set(mb, e + MESH_E_ARG, arg);
36    mesh_mailbox_set(mb, e + MESH_E_SLOT, t % MESH_PROG_RESULTS);
37    mesh_mailbox_publish(mb, e + MESH_E_SEQ, t + 1);
38    r->tail = t + 1;
39    mesh_mailbox_set64(mb, r->ctl + MESH_P_RING_TAIL, r->tail);
40    if (r->n == r->cap) {
41        r->cap = r->cap ? 2 * r->cap : 16;
42        r->inflight = mesh_xrealloc(r->inflight, r->cap * sizeof *r->inflight);
43    }
44    r->inflight[r->n].ticket = t;
45    r->inflight[r->n].kind = kind;
46    r->n++;
47    if (ticket) *ticket = t;
48    return true;
49}
50
51void mesh_pring_collect(mesh_pring *r, mesh_mailbox mb, mesh_done **done, size_t *n) {
52    *done = NULL;
53    *n = 0;
54    if (!r->n) return;
55    size_t keep = 0, k = 0;
56    mesh_done *d = NULL;
57    for (size_t i = 0; i < r->n; i++) {
58        mesh_out o = r->inflight[i];
59        size_t c = o.ticket % MESH_PROG_RESULTS;
60        if (mesh_mailbox_get(mb, r->ctl + MESH_P_MARKS + 4 * c) == o.ticket + 1) {
61            if (!d) d = mesh_xmalloc(r->n * sizeof *d);
62            d[k].out = o;
63            d[k].result = mesh_mailbox_get(mb, r->ctl + MESH_P_RESULTS + 4 * c);
64            k++;
65        } else r->inflight[keep++] = o;
66    }
67    r->n = keep;
68    *done = d;
69    *n = k;
70}
71
72bool mesh_pring_idle(const mesh_pring *r) { return r->n == 0; }
73
74static uint32_t count(const mesh_pring *r, mesh_tk tk) {
75    uint32_t c = 0;
76    for (size_t i = 0; i < r->n; i++) c += r->inflight[i].kind.tk == tk;
77    return c;
78}
79
80uint32_t mesh_pring_auto_in_flight(const mesh_pring *r) { return count(r, MESH_TK_AUTO); }
81uint32_t mesh_pring_follow_in_flight(const mesh_pring *r) { return count(r, MESH_TK_FOLLOW); }
82size_t mesh_pring_in_flight(const mesh_pring *r) { return r->n; }
83bool mesh_pring_has_req(const mesh_pring *r) { return count(r, MESH_TK_REQ) > 0; }
84bool mesh_pring_has_init(const mesh_pring *r) { return count(r, MESH_TK_INIT) > 0; }
85
86void mesh_pring_rewind(mesh_pring *r, mesh_mailbox mb, mesh_out **outs, size_t *n) {
87    mesh_mailbox_set(mb, r->ctl + MESH_P_RING_HEAD, r->tail);
88    mesh_mailbox_set(mb, r->ctl + MESH_P_ONLINE, 0);
89    mesh_mailbox_set(mb, r->ctl + MESH_P_HALTED, 0);
90    *outs = r->inflight;
91    *n = r->n;
92    r->inflight = NULL;
93    r->n = r->cap = 0;
94}
95
96uint32_t mesh_due(const mesh_manifest *m, uint32_t n, const mesh_pring *r, int64_t *last, int64_t now) {
97    uint32_t a = mesh_pring_auto_in_flight(r);
98    if (m->batch && a > 0) return 0;
99    switch (m->policy) {
100    case MESH_POLICY_CONTINUOUS: return n - (a < n ? a : n);
101    case MESH_POLICY_PERIODIC: {
102        int64_t period = (int64_t)(1000000000ull / m->hz);
103        int64_t since = now > *last ? now - *last : 0;
104        if (since < period) return 0;
105        /* Keep the cadence; after a stall (or a stop) restart it from now. */
106        *last = since < 2 * period ? *last + period : now;
107        return n;
108    }
109    default: return 0;
110    }
111}
112
113uint32_t mesh_publish_auto(mesh_mailbox mb, const mesh_manifest *m, uint32_t view_off, mesh_pring *r, int64_t *last, int64_t now) {
114    uint32_t k = m->fanout;
115    if (m->batch && mesh_pring_room(r) < k) return 0;
116    uint32_t n = mesh_due(m, k, r, last, now);
117    uint32_t base = m->arg_mode == MESH_ARG_VIEW_U32 ? mesh_mailbox_get(mb, (size_t)view_off + m->arg_off) : 0;
118    uint32_t sent = 0;
119    for (uint32_t i = 0; i < n; i++) {
120        if (!mesh_pring_publish(r, mb, MESH_OP_TASK, base + i, mesh_kind_of(MESH_TK_AUTO), NULL)) break;
121        sent++;
122    }
123    return sent;
124}
125
126uint32_t mesh_publish_parts(mesh_mailbox mb, const mesh_manifest *m, uint32_t view_off, mesh_pring *r, int64_t *last,
127                            int64_t now, uint32_t parts) {
128    uint32_t k = 0;
129    for (int b = 0; b < 32; b++) k += (parts >> b) & 1u;
130    if (k == 0 || mesh_pring_room(r) < k) return 0;
131    if (mesh_due(m, k, r, last, now) < k) return 0;
132    uint32_t base = m->arg_mode == MESH_ARG_VIEW_U32 ? mesh_mailbox_get(mb, (size_t)view_off + m->arg_off) : 0;
133    uint32_t sent = 0;
134    for (uint32_t b = 0; b < 32; b++) {
135        if (!((parts >> b) & 1u)) continue;
136        if (!mesh_pring_publish(r, mb, MESH_OP_TASK, base + b, mesh_kind_of(MESH_TK_AUTO), NULL)) break;
137        sent++;
138    }
139    return sent;
140}