==> 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}