From 42021a500d19dbe091075338d1a621498a81f167 Mon Sep 17 00:00:00 2001 From: eunhhu Date: Sat, 26 Sep 2026 22:56:32 +0100 Subject: Snapshot an app's saved states for readers on other threads Right after a save, pure_app_snapshot makes a read-only snapshot of the saved states: numbers are copied, text states are frozen drafts that share the store mapping (pure_draft_freeze, no copy), and lists are copies of their item pointers that share the items. An unchanged list shares the last snapshot's copy. pure_app_read binds a reader app on another thread to a snapshot so the existing view functions read it. A shared item never changes again: a list stamps each item with its share count, copies a shared item it changes, and retires shared items it lets go instead of freeing them. Snapshots form a chain that goes oldest first, and items retired while a snapshot was the newest go with it. References are atomic and may be released on any thread. test-module checks that each snapshot equals the app, that kept snapshots stay as made through changes, removals, a module switch, the app stopping and the store closing, that a failed save's snapshot equals the one before, and that two threads read the newest snapshots while the app goes on (also under ASan, UBSan and LeakSanitizer). --- prototypes/connections/module.h | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'prototypes/connections/module.h') diff --git a/prototypes/connections/module.h b/prototypes/connections/module.h index 313c6e1..3a75fc1 100644 --- a/prototypes/connections/module.h +++ b/prototypes/connections/module.h @@ -228,6 +228,9 @@ typedef struct { unsigned char *copied; int64_t revision; PureArena arena, view_arena; + struct PureSnapshot *snapshot; /* the newest snapshot made, or NULL */ + PureRetired retired; /* list items let go since it was made */ + bool reading; /* a reader: its states are a snapshot's */ } PureApp; PureResult pure_app_start(PureApp *, const PureModule *, PureData data); @@ -274,4 +277,23 @@ PureResult pure_app_cell_text(PureApp *, uint32_t column, size_t row, bool (*vis * states keep their content, cursors their position in that text); new states * start at their initial value. The module must outlive the app. */ PureResult pure_app_replace(PureApp *, const PureModule *); + +/* Readers on other threads. Right after a save an app can make a snapshot of + * its saved states, for evaluating views anywhere while it keeps applying + * events: numbers are copied; text states share their stored versions and + * lists their items (a list unchanged since the last snapshot shares that + * snapshot's copy), so the cost does not grow with the text. NULL when a + * change waits for a save, when a text state is not all stored (for example + * without a data host that keeps states), or when memory runs out. The caller + * gets a reference; the app keeps its own until the next snapshot. */ +typedef struct PureSnapshot PureSnapshot; +PureSnapshot *pure_app_snapshot(PureApp *); +void pure_snapshot_hold(PureSnapshot *); +/* On any thread; the last reference frees it. */ +void pure_snapshot_release(PureSnapshot *); +/* Show a snapshot's states in reader, a zeroed app or a reader before: its + * views read them while the snapshot is held, with the reader's own arena and + * row cache (one thread at a time). It applies no events. pure_app_stop frees + * the reader and leaves the snapshot alone. */ +void pure_app_read(PureApp *reader, const PureSnapshot *); #endif -- cgit v1.2.3