diff options
| -rw-r--r-- | prototypes/connections/state.h | 8 | ||||
| -rw-r--r-- | prototypes/connections/store-tree.c | 27 | ||||
| -rw-r--r-- | prototypes/connections/test-tree-store.c | 32 |
3 files changed, 60 insertions, 7 deletions
diff --git a/prototypes/connections/state.h b/prototypes/connections/state.h index d1bc0fa..d0b386c 100644 --- a/prototypes/connections/state.h +++ b/prototypes/connections/state.h @@ -24,6 +24,14 @@ int64_t pure_draft_revision(const PureDraft *); * splice, commit or close of this draft. */ PureResult pure_draft_read(const PureDraft *, size_t offset, size_t length, bool (*visit)(void *, PureSpan), void *context); +/* An immutable version of a draft's value. It stays readable while the draft + * is held (reclamation is suspended) and until the next commit or close. */ +typedef struct { uint64_t ref, bytes; } PureVersion; +PureVersion pure_draft_version(const PureDraft *); +PureResult pure_draft_read_version(const PureDraft *, PureVersion, size_t offset, size_t length, + bool (*visit)(void *, PureSpan), void *context); +/* While held, edits keep every earlier version's nodes (one transaction). */ +void pure_draft_hold(PureDraft *, bool held); /* Publish if the key is still at the draft's base revision. On success the * draft continues from the new revision; on any failure it is unchanged. */ PureResult pure_draft_commit(PureDraft *, int64_t *revision); diff --git a/prototypes/connections/store-tree.c b/prototypes/connections/store-tree.c index 1c0ad91..74763ca 100644 --- a/prototypes/connections/store-tree.c +++ b/prototypes/connections/store-tree.c @@ -943,6 +943,7 @@ struct PureDraft { Entry root; int64_t revision; size_t live; /* arena bytes reachable at the last collection */ + bool held; /* a transaction still reads earlier versions */ }; /* Rebase the draft on the committed version of its key. */ @@ -1013,6 +1014,7 @@ static size_t mark(const Tree *tree, Entry entry, unsigned char *marks) { * longer reaches once they outweigh the reachable ones (and 32 KiB). */ static void collect(PureDraft *draft) { Tree *tree = &draft->tree; + if (draft->held) return; size_t allowance = draft->live > 32768 ? draft->live : 32768; if (tree->bytes <= draft->live || tree->bytes - draft->live <= allowance) return; unsigned char *marks = calloc(tree->count, 1); @@ -1044,16 +1046,31 @@ PureResult pure_draft_splice(PureDraft *draft, size_t offset, size_t remove, Pur return ok ? PURE_OK : PURE_STORE_ERROR; } -PureResult pure_draft_read(const PureDraft *draft, size_t offset, size_t length, - bool (*visit)(void *, PureSpan), void *context) { +PureVersion pure_draft_version(const PureDraft *draft) { + return (PureVersion){draft->root.ref, draft->root.bytes}; +} + +PureResult pure_draft_read_version(const PureDraft *draft, PureVersion version, size_t offset, + size_t length, bool (*visit)(void *, PureSpan), void *context) { + Entry root = {version.ref, version.bytes}; unsigned height; - if (offset > draft->root.bytes || length > draft->root.bytes - offset) return PURE_LIMIT; + if (offset > root.bytes || length > root.bytes - offset) return PURE_LIMIT; if (!length) return PURE_OK; - if (!root_height(&draft->tree, draft->root, &height)) return PURE_STORE_ERROR; - return visit_range(&draft->tree, draft->root, height, offset, length, visit, context) ? + if (!root_height(&draft->tree, root, &height)) return PURE_STORE_ERROR; + return visit_range(&draft->tree, root, height, offset, length, visit, context) ? PURE_OK : PURE_STORE_ERROR; } +PureResult pure_draft_read(const PureDraft *draft, size_t offset, size_t length, + bool (*visit)(void *, PureSpan), void *context) { + return pure_draft_read_version(draft, pure_draft_version(draft), offset, length, visit, context); +} + +void pure_draft_hold(PureDraft *draft, bool held) { + draft->held = held; + if (!held) collect(draft); +} + PureResult pure_draft_commit(PureDraft *draft, int64_t *revision) { *revision = draft->revision; Session session; diff --git a/prototypes/connections/test-tree-store.c b/prototypes/connections/test-tree-store.c index 8cf1f1e..a9aea86 100644 --- a/prototypes/connections/test-tree-store.c +++ b/prototypes/connections/test-tree-store.c @@ -545,6 +545,33 @@ static void draft_relocation(const char *path) { free(big); } +/* A held draft keeps earlier versions readable across later edits. */ +static void draft_versions(const char *path) { + static unsigned char bytes[200000], scratch[200000]; + uint32_t state = 17; + fill(bytes, sizeof(bytes), &state); + int64_t revision; + CHECK(pure_store_put(path, "versioned", 0, (PureSpan){bytes, sizeof(bytes)}, &revision) == PURE_OK); + PureDraft *draft; + CHECK(pure_draft_open(path, "versioned", &draft) == PURE_OK); + pure_draft_hold(draft, true); + PureVersion first = pure_draft_version(draft); + for (unsigned i = 0; i < 400; ++i) { + unsigned char byte = (unsigned char)i; + CHECK(pure_draft_splice(draft, (i * 977u) % 150000, 3, (PureSpan){&byte, 1}) == PURE_OK); + } + Gather g = {scratch, 0, 0, 0}; + CHECK(pure_draft_read_version(draft, first, 0, sizeof(bytes), gather, &g) == PURE_OK); + CHECK((size_t)(g.out - scratch) == sizeof(bytes) && !memcmp(scratch, bytes, sizeof(bytes))); + CHECK(pure_draft_size(draft) == sizeof(bytes) - 800); + size_t nodes, held_bytes, after; + pure_draft_test_arena(draft, &nodes, &held_bytes); + pure_draft_hold(draft, false); + pure_draft_test_arena(draft, &nodes, &after); + CHECK(after < held_bytes); + pure_draft_close(draft); +} + /* A failed commit changes neither the store nor the draft. */ static void draft_faults(const char *path) { unsigned char scratch[64]; @@ -593,7 +620,8 @@ static void clean(const char *path) { int main(void) { void (*checks[])(const char *) = {oracle, amplification, faults, crashes, torn_head, isolation, corruption, many_keys, revision_limit, keys, - drafts, typing, draft_conflict, draft_relocation, draft_faults}; + drafts, typing, draft_conflict, draft_relocation, draft_faults, + draft_versions}; for (size_t i = 0; i < sizeof(checks) / sizeof(*checks); ++i) { char path[] = "/tmp/pure-tree-check-XXXXXX"; CHECK(mkdtemp(path) && pure_store_init(path) == PURE_OK); @@ -603,6 +631,6 @@ int main(void) { } puts("{\"tree_store\":\"random-oracle-balance-held-views-one-byte-amplification-faults-" "process-crash-torn-head-lockless-isolation-compaction-corruption-1024-keys-revision-limit-key-listing-" - "draft-oracle-typing-arena-conflict-relocation-commit-faults\"}"); + "draft-oracle-typing-arena-conflict-relocation-commit-faults-held-versions\"}"); return 0; } |
