aboutsummaryrefslogtreecommitdiff
path: root/src/core/hold.h
diff options
context:
space:
mode:
authorGravatar zinc <zinc@downsta.ir> 2026-10-15 17:03:03 +0330
committerGravatar zinc <zinc@downsta.ir> 2026-10-15 17:03:03 +0330
commit7cb983bbea98d2038b2a8015a6d1f1afd40991c9 (patch)
treeec3d88a15801207fc6d8d3c6d142617ffec85460 /src/core/hold.h
parentmerge work/borrow: borrow returns before definition, borrows clause, borrow m... (diff)
parentbench: rss probe path fix (diff)
merge work/actors: wasm seed bootstrap (seed/MANIFEST), shared copy-on-write actor memory, top-level hold blocks on all backends and lsp
Diffstat (limited to 'src/core/hold.h')
-rw-r--r--src/core/hold.h116
1 files changed, 116 insertions, 0 deletions
diff --git a/src/core/hold.h b/src/core/hold.h
new file mode 100644
index 0000000..d4ab1f0
--- /dev/null
+++ b/src/core/hold.h
@@ -0,0 +1,116 @@
+/* hold blocks (spec 4.16): the host side of a monitor. a function of a hold block
+ stores op 57 (enter) at its start and op 58 (leave) at its exit, with the block's
+ rank and the address of its descriptor word. the descriptor points at a table in vm
+ memory: n, then n pairs (address, words) of the block's shared variables.
+
+ the shared values live here, outside every vm, so all actors (each with its own
+ memory) see one copy. entering a block from outside takes its lock and copies the
+ values into the vm's variables; leaving copies them back and unlocks. inside, they
+ are plain globals. the lock is per block and reentrant per thread (a nested call
+ only counts), and blocks are taken in rank (declaration) order: entering a block
+ while holding a later one is an error, which rules out lock-order deadlocks between
+ blocks. a worker that ends while holding blocks releases them without writing back.
+
+ included by src/core/core.c and src/micro/micro.c; HOLD_THREADS selects pthreads,
+ otherwise a single machine needs no locks (wasm, bare metal). no malloc. */
+#ifndef HOLD_H
+#define HOLD_H
+#define HOLD_BLOCKS 256
+#define HOLD_CELLS 65536
+#ifdef HOLD_THREADS
+#include <pthread.h>
+static pthread_mutex_t hold_lock[HOLD_BLOCKS], hold_meta = PTHREAD_MUTEX_INITIALIZER;
+static pthread_once_t hold_once = PTHREAD_ONCE_INIT;
+static void hold_init(void) { int i; for (i = 0; i < HOLD_BLOCKS; i++) pthread_mutex_init(&hold_lock[i], 0); }
+#define HOLD_TLS _Thread_local
+#else
+#define HOLD_TLS
+#endif
+static int64_t hold_cells[HOLD_CELLS];
+static int hold_base[HOLD_BLOCKS], hold_size[HOLD_BLOCKS], hold_ready[HOLD_BLOCKS], hold_next;
+static HOLD_TLS int hold_depth[HOLD_BLOCKS], hold_top; /* this thread: nesting per block, highest rank held */
+static HOLD_TLS unsigned char hold_order[HOLD_BLOCKS]; /* ranks held, in the order taken */
+static HOLD_TLS int hold_n;
+
+/* the table's entries, checked against the machine. -1 when it is malformed */
+static int64_t hold_table(int64_t *mem, uint64_t words, int64_t desc, int64_t *n)
+{
+ int64_t t, i;
+ if (desc <= 0 || (uint64_t)desc >= words) return -1;
+ t = mem[desc];
+ if (t <= 0 || (uint64_t)t >= words) return -1;
+ *n = mem[t];
+ if (*n < 0 || *n > 64 || (uint64_t)(t + 1 + 2 * *n) > words) return -1;
+ for (i = 0; i < *n; i++) {
+ int64_t a = mem[t + 1 + 2 * i], w = mem[t + 2 + 2 * i];
+ if (a <= 0 || w <= 0 || (uint64_t)(a + w) > words) return -1;
+ }
+ return t;
+}
+static void hold_copy(int64_t *mem, int64_t t, int64_t n, int rank, int in)
+{
+ int64_t i, k, c = hold_base[rank];
+ for (i = 0; i < n; i++) {
+ int64_t a = mem[t + 1 + 2 * i], w = mem[t + 2 + 2 * i];
+ for (k = 0; k < w; k++, c++) { if (in) mem[a + k] = hold_cells[c]; else hold_cells[c] = mem[a + k]; }
+ }
+}
+/* op 57: 0, or -1 (bad block), or -2 (taken out of rank order; the caller stops) */
+static int64_t hold_enter(int64_t *mem, uint64_t words, int64_t rank, int64_t desc)
+{
+ int64_t t, n, i, total = 0;
+ if (rank <= 0 || rank >= HOLD_BLOCKS) return -1;
+ if (hold_depth[rank]) { hold_depth[rank]++; return 0; }
+ if (hold_top > rank) return -2;
+ t = hold_table(mem, words, desc, &n); if (t < 0) return -1;
+ for (i = 0; i < n; i++) total += mem[t + 2 + 2 * i];
+#ifdef HOLD_THREADS
+ pthread_once(&hold_once, hold_init);
+ pthread_mutex_lock(&hold_lock[rank]);
+ pthread_mutex_lock(&hold_meta);
+#endif
+ if (!hold_ready[rank]) { /* the first entry anywhere: the image's initial values become the shared ones */
+ if (hold_next + total > HOLD_CELLS) {
+#ifdef HOLD_THREADS
+ pthread_mutex_unlock(&hold_meta); pthread_mutex_unlock(&hold_lock[rank]);
+#endif
+ return -1;
+ }
+ hold_base[rank] = hold_next; hold_size[rank] = (int)total; hold_next += (int)total; hold_ready[rank] = 1;
+ hold_copy(mem, t, n, (int)rank, 0);
+ }
+#ifdef HOLD_THREADS
+ pthread_mutex_unlock(&hold_meta);
+#endif
+ hold_copy(mem, t, n, (int)rank, 1);
+ hold_depth[rank] = 1; hold_order[hold_n++] = (unsigned char)rank; hold_top = (int)rank;
+ return 0;
+}
+/* op 58 */
+static int64_t hold_leave(int64_t *mem, uint64_t words, int64_t rank, int64_t desc)
+{
+ int64_t t, n;
+ if (rank <= 0 || rank >= HOLD_BLOCKS || !hold_depth[rank]) return -1;
+ if (--hold_depth[rank]) return 0;
+ t = hold_table(mem, words, desc, &n);
+ if (t >= 0) hold_copy(mem, t, n, (int)rank, 0);
+ if (hold_n > 0 && hold_order[hold_n - 1] == rank) hold_n--;
+ hold_top = hold_n ? hold_order[hold_n - 1] : 0;
+#ifdef HOLD_THREADS
+ pthread_mutex_unlock(&hold_lock[rank]);
+#endif
+ return 0;
+}
+/* a machine that ends while inside blocks gives them up, without writing back */
+static void hold_release_all(void)
+{
+ while (hold_n > 0) {
+ int r = hold_order[--hold_n];
+ hold_depth[r] = 0;
+#ifdef HOLD_THREADS
+ pthread_mutex_unlock(&hold_lock[r]);
+#endif
+ }
+ hold_top = 0;
+}
+#endif