skip to content
==> src/rt.c <==
1/* The engine (mesh-host/src/wasm.rs `Rt`): the compile cache and the epoch thread. */
2#include "mesh/exec_wasm.h"
3
4#include "mesh/abi/fnv.h"
5#include "mesh/exec_wasm_cache.h"
6#include "mesh/log.h"
7
8#include <pthread.h>
9#include <stdarg.h>
10#include <stdio.h>
11#include <stdlib.h>
12#include <string.h>
13#include <time.h>
14
15#include "rt_internal.h"
16
17char *mesh_wasm_fmt(const char *fmt, ...) {
18    va_list ap;
19    va_start(ap, fmt);
20    int n = vsnprintf(NULL, 0, fmt, ap);
21    va_end(ap);
22    char *s = malloc(n > 0 ? (size_t)n + 1 : 1);
23    if (!s) return NULL;
24    va_start(ap, fmt);
25    vsnprintf(s, (size_t)n + 1, fmt, ap);
26    va_end(ap);
27    return s;
28}
29
30static void *epoch_thread(void *arg) {
31    mesh_wasm_rt *rt = arg;
32    while (!atomic_load(&rt->stop)) {
33        if (atomic_load(&rt->lanes) == 0) {
34            struct timespec idle = {0, 20000000};
35            nanosleep(&idle, NULL);
36            continue;
37        }
38        atomic_fetch_add(&rt->epoch, 1);
39        struct timespec tick = {0, (long)MESH_WASM_TICK_NS};
40        nanosleep(&tick, NULL);
41    }
42    return NULL;
43}
44
45mesh_wasm_rt *mesh_wasm_rt_new(const char *cache, char **err) {
46    if (cache && !mesh_wasm_cache_dir(cache, err)) return NULL;
47    mesh_wasm_rt *rt = calloc(1, sizeof *rt);
48    if (!rt) {
49        if (err) *err = mesh_wasm_fmt("wasm: out of memory");
50        return NULL;
51    }
52    rt->cache = cache ? strdup(cache) : NULL;
53    if (pthread_create(&rt->thread, NULL, epoch_thread, rt) != 0) {
54        if (err) *err = mesh_wasm_fmt("wasm: epoch thread: cannot start it");
55        free(rt->cache);
56        free(rt);
57        return NULL;
58    }
59    return rt;
60}
61
62void mesh_wasm_rt_free(mesh_wasm_rt *rt) {
63    if (!rt) return;
64    atomic_store(&rt->stop, true);
65    pthread_join(rt->thread, NULL);
66    free(rt->cache);
67    free(rt);
68}
69
70uint64_t mesh_wasm_engine_hash(void) {
71    /* What the compiled form depends on: the interpreter and the proposals it runs. */
72    char id[96];
73    snprintf(id, sizeof id, "alpha-c-mesh wasm-interp 0.1.0 features %x", (unsigned)WASM_F_INTERP);
74    return mesh_fnv1a64((const uint8_t *)id, strlen(id));
75}
76
77static void evicted(const char *path) {
78    mesh_info("wasm: cache over %llu bytes: evicted %s", (unsigned long long)MESH_WASM_CACHE_CAP, path);
79}
80
81static double ms_since(const struct timespec *t0) {
82    struct timespec t1;
83    clock_gettime(CLOCK_MONOTONIC, &t1);
84    return (double)(t1.tv_sec - t0->tv_sec) * 1e3 + (double)(t1.tv_nsec - t0->tv_nsec) / 1e6;
85}
86
87wasm_compiled *mesh_wasm_rt_module(mesh_wasm_rt *rt, const char *name, const uint8_t *wasm, size_t n, double *ms,
88                                   const char **how, char **err) {
89    struct timespec t0;
90    clock_gettime(CLOCK_MONOTONIC, &t0);
91    char msg[512];
92    char *path = rt->cache ? mesh_wasm_cache_path(rt->cache, wasm, n, mesh_wasm_engine_hash()) : NULL;
93    if (path) {
94        uint8_t *cached;
95        size_t clen;
96        if (mesh_wasm_cache_load(path, &cached, &clen)) {
97            /* Our own cache: the module's bytes, written below. */
98            wasm_compiled *m = clen == n && memcmp(cached, wasm, n) == 0
99                                   ? wasm_compile(cached, clen, WASM_F_INTERP, msg, sizeof msg)
100                                   : NULL;
101            free(cached);
102            if (m) {
103                *ms = ms_since(&t0);
104                *how = "cached";
105                free(path);
106                return m;
107            }
108            mesh_error("wasm: %s: cached %s unusable (%s): compiling", name, path, "not this module");
109        }
110    }
111    wasm_compiled *m = wasm_compile(wasm, n, WASM_F_INTERP, msg, sizeof msg);
112    if (!m) {
113        if (err) *err = mesh_wasm_fmt("jit: %s: %s", name, msg);
114        free(path);
115        return NULL;
116    }
117    *ms = ms_since(&t0);
118    if (path) {
119        char *e = NULL;
120        if (!mesh_wasm_cache_store(path, wasm, n, &e)) mesh_error("wasm: cache %s", e ? e : path);
121        free(e);
122        char *dir = strdup(path);
123        char *slash = dir ? strrchr(dir, '/') : NULL;
124        if (slash) *slash = 0;
125        if (dir) mesh_wasm_cache_evict(slash ? dir : ".", MESH_WASM_CACHE_CAP, evicted);
126        free(dir);
127    }
128    free(path);
129    *how = "compiled";
130    return m;
131}