From b8e6a2e5b8fc64ec47adfbe583b8a27200cc5a2e Mon Sep 17 00:00:00 2001 From: eunhhu Date: Mon, 19 Oct 2026 09:58:47 +0100 Subject: The modules an app ran are freed (research 115's next step): every app's module a Loaded with holds (each snapshot of it, each request read with it); one replaced is retired and freed on the apps thread once no app runs it, every connection thread has begun a pass since (woken for it), and nothing holds it; who keeps a module to ask later whether it is the same asks its serial (core: PureModule.serial, a reader's module_serial, pure_snapshot_module, pure_snapshot_release says it freed; serve: routes, groups, links, bindings, queries, the published snapshot), as an address may come back; a link binds under its receiver's lock, a link naming an input under binding_lock; 030's memory.py: 4,000 changes 0 bytes a change (was 19,284), 4,100 freed; stress.py: readers, view workers, push streams and a body across changes, 586 of 600 loads at a freed module's address, every answer right, ASan 1,000 changes clean; a push group that looked while its app resumed missed the new snapshot (published before the app took requests) and waited for its next save: woken again once it takes them (server_view_workers had failed once under load) --- core/module.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) (limited to 'core/module.c') diff --git a/core/module.c b/core/module.c index f970daf..4b20e25 100644 --- a/core/module.c +++ b/core/module.c @@ -2284,6 +2284,8 @@ PURE_COLD PureResult pure_module_load(const void *bytes, size_t size, PureModule } out->runner = out->calls || out->nesting > PURE_MODULE_NESTING ? 2 : out->batches ? 1 : 0; out->jit = pure_jit_make(out); /* none: the executor runs it all */ + static uint64_t loads; + out->serial = __atomic_add_fetch(&loads, 1, __ATOMIC_RELAXED); return PURE_OK; refused: if (error_size) snprintf(error, error_size, "%s (%u)", why, bad); @@ -5171,8 +5173,10 @@ const PureList *pure_snapshot_list(const PureSnapshot *snapshot, uint32_t state) return state >= PURE_LIST_HEARD ? snapshot->runtime[state - PURE_LIST_HEARD] : snapshot->lists[state]; } -void pure_snapshot_release(PureSnapshot *snapshot) { - if (!snapshot || __atomic_sub_fetch(&snapshot->refs, 1, __ATOMIC_ACQ_REL)) return; +const PureModule *pure_snapshot_module(const PureSnapshot *snapshot) { return snapshot->module; } + +bool pure_snapshot_release(PureSnapshot *snapshot) { + if (!snapshot || __atomic_sub_fetch(&snapshot->refs, 1, __ATOMIC_ACQ_REL)) return false; Register *all = snapshot->all; if (all) { /* out of the links: what the one before may read goes to it (still linked, so still there) */ lock_register(all); @@ -5193,6 +5197,7 @@ void pure_snapshot_release(PureSnapshot *snapshot) { pure_retired_free(&all->returned); /* the app is gone: no list takes it back */ free(all); } + return true; } static void let_own_go(PureApp *app); @@ -6058,13 +6063,15 @@ static void let_own_go(PureApp *app) { } bool pure_app_read(PureApp *reader, const PureSnapshot *snapshot) { - if (reader->module != snapshot->module) drop_passes(reader), let_own_go(reader); /* laid out for its module */ + if (reader->module_serial != snapshot->module->serial) /* laid out for its module (by serial: freed, another may */ + drop_passes(reader), let_own_go(reader); /* come to its address) */ if (!reader->states || reader->laid != snapshot->states) { /* its arrays for as many states (027's 4f-2) */ void *was = reader->states; if (!lay_app(reader, snapshot->states)) return false; free(was); } reader->module = snapshot->module; + reader->module_serial = snapshot->module->serial; reader->reading = true; reader->changes = snapshot->changes; /* what the row cache is fresh for */ memcpy(reader->values, snapshot->values, snapshot->states * sizeof(*reader->values)); -- cgit v1.2.3