skip to content
==> src/node.c <==
1/* lib.rs's node_loop and run_nodes. */
2#include "mesh/worker_node.h"
3
4#include "mesh/abi/control_block.h"
5#include "mesh/abi/node.h"
6#include "mesh/abi/ops.h"
7#include "mesh/abi/program_table.h"
8#include "mesh/abi/ring.h"
9#include "mesh/abi/trace.h"
10
11#include <pthread.h>
12#include <stdio.h>
13#include <sched.h>
14#include <stdlib.h>
15#include <time.h>
16
17atomic_bool mesh_worker_stop_all;
18
19#define HEARTBEAT_NS 2000000u
20
21static uint32_t relaxed(mesh_mailbox mb, size_t off) {
22    return atomic_load_explicit((_Atomic uint32_t *)(void *)(mb.base + off), memory_order_relaxed);
23}
24
25static void nap_ns(long ns) {
26    struct timespec t = {0, ns};
27    nanosleep(&t, NULL);
28}
29
30/* Idle polling: spin right after work, then sleep longer the longer it stays idle. */
31static void idle(uint64_t since) {
32    uint64_t t = mesh_worker_now_ns() - since;
33    if (t < 50000u) sched_yield();
34    else if (t < 10000000u) nap_ns(20000);
35    else if (t < 1000000000u) nap_ns(200000);
36    else nap_ns(1000000);
37}
38
39static uint32_t cpu_of_this_thread(uint32_t node) {
40#if defined(__linux__)
41    int c = sched_getcpu();
42    if (c >= 0) return (uint32_t)c;
43#endif
44    return node;
45}
46
47void mesh_worker_node_loop(mesh_mailbox mb, uint32_t slot, uint32_t node, const mesh_worker_program *p, const atomic_bool *stop) {
48    mesh_worker_ctx ctx;
49    mesh_worker_ctx_new(mb, slot, node, &ctx);
50    size_t ctl = mesh_prog_ctl(slot), rec = mesh_node_rec(slot, node);
51    mesh_mailbox_set(mb, rec + MESH_N_SM, cpu_of_this_thread(node));
52    mesh_mailbox_fetch_add(mb, ctl + MESH_P_ONLINE, 1);
53    uint32_t beat = relaxed(mb, rec + MESH_N_HEARTBEAT);
54    uint64_t beat_at = mesh_worker_now_ns(), idle_since = beat_at;
55    for (;;) {
56        uint32_t t;
57        for (;;) {
58            t = mesh_mailbox_get(mb, ctl + MESH_P_RING_HEAD);
59            size_t e = ctl + MESH_P_RING + MESH_ENTRY_BYTES * (t % MESH_PROG_RING);
60            if (mesh_mailbox_get(mb, e + MESH_E_SEQ) == t + 1) {
61                if (mesh_mailbox_cas(mb, ctl + MESH_P_RING_HEAD, t, t + 1) == t) break;
62                continue;
63            }
64            /* Nothing published: a ticket published before the shutdown was claimed above first. */
65            if (relaxed(mb, ctl + MESH_P_SHUTDOWN) != 0 || (stop && atomic_load_explicit(stop, memory_order_relaxed)) ||
66                atomic_load_explicit(&mesh_worker_stop_all, memory_order_relaxed))
67                goto halt;
68            uint64_t now = mesh_worker_now_ns();
69            if (now - beat_at >= HEARTBEAT_NS) {
70                beat_at = now;
71                mesh_mailbox_set(mb, rec + MESH_N_HEARTBEAT, ++beat);
72            }
73            idle(idle_since);
74        }
75        size_t e = ctl + MESH_P_RING + MESH_ENTRY_BYTES * (t % MESH_PROG_RING);
76        uint32_t op = relaxed(mb, e + MESH_E_OP), arg = relaxed(mb, e + MESH_E_ARG), cslot = relaxed(mb, e + MESH_E_SLOT) % MESH_PROG_RESULTS;
77        ctx.ticket = t;
78        uint64_t start = mesh_worker_now_ns();
79        uint32_t result;
80        if (op == MESH_OP_INIT) {
81            if (p->init) p->init(&ctx, p->user);
82            result = MESH_R_NONE;
83        } else {
84            result = p->task(&ctx, op, arg, p->user);
85        }
86        uint64_t end = mesh_worker_now_ns();
87        mesh_mailbox_set(mb, ctl + MESH_P_RESULTS + 4 * cslot, result);
88        mesh_mailbox_publish(mb, ctl + MESH_P_MARKS + 4 * cslot, t + 1);
89        mesh_mailbox_fetch_add(mb, ctl + MESH_P_DONE, 1);
90        mesh_mailbox_set(mb, rec + MESH_N_TASKS, relaxed(mb, rec + MESH_N_TASKS) + 1);
91        mesh_mailbox_set(mb, rec + MESH_N_LAST_OP, op);
92        mesh_mailbox_set64(mb, rec + MESH_N_BUSY_NS, mesh_mailbox_get64(mb, rec + MESH_N_BUSY_NS) + (end - start));
93        mesh_mailbox_set(mb, rec + MESH_N_HEARTBEAT, ++beat);
94        uint32_t n = mesh_mailbox_fetch_add(mb, ctl + MESH_P_TRACE_N, 1);
95        size_t tr = mesh_trace_rec(slot, n);
96        mesh_mailbox_set(mb, tr, slot | node << 8 | op << 16);
97        mesh_mailbox_set(mb, tr + 4, t);
98        mesh_mailbox_set(mb, tr + 8, (uint32_t)start);
99        mesh_mailbox_set(mb, tr + 12, (uint32_t)end);
100        idle_since = mesh_worker_now_ns();
101    }
102halt:
103    mesh_mailbox_fetch_add(mb, ctl + MESH_P_HALTED, 1);
104}
105
106typedef struct {
107    mesh_mailbox mb;
108    uint32_t slot, node;
109    const mesh_worker_program *p;
110    const atomic_bool *stop;
111} node_arg;
112
113static void *node_main(void *a) {
114    node_arg *n = a;
115    mesh_worker_node_loop(n->mb, n->slot, n->node, n->p, n->stop);
116    return NULL;
117}
118
119void mesh_worker_run_nodes(mesh_mailbox mb, uint32_t slot, const mesh_worker_program *p, const atomic_bool *stop) {
120    uint32_t e[32];
121    mesh_worker_entry(mb, slot, e);
122    uint32_t nodes = e[MESH_PT_NODES / 4];
123    if (nodes < 1) nodes = 1;
124    if (nodes > MESH_PROG_MAX_NODES) nodes = MESH_PROG_MAX_NODES;
125    pthread_t th[MESH_PROG_MAX_NODES];
126    node_arg args[MESH_PROG_MAX_NODES];
127    uint32_t started = 0;
128    for (uint32_t n = 0; n < nodes; n++) {
129        args[n] = (node_arg){mb, slot, n, p, stop};
130        if (pthread_create(&th[n], NULL, node_main, &args[n]) != 0) {
131            fputs("mesh-worker: a node thread could not start\n", stderr);
132            abort();
133        }
134        started++;
135    }
136    for (uint32_t n = 0; n < started; n++) pthread_join(th[n], NULL);
137}