/* 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 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