aboutsummaryrefslogtreecommitdiffstats
path: root/prototypes/connections
diff options
context:
space:
mode:
Diffstat (limited to 'prototypes/connections')
-rw-r--r--prototypes/connections/state.h8
-rw-r--r--prototypes/connections/store-tree.c27
-rw-r--r--prototypes/connections/test-tree-store.c32
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;
}