]> git.ipfire.org Git - thirdparty/git.git/commitdiff
reftable/reader: keep readers alive during iteration
authorPatrick Steinhardt <ps@pks.im>
Fri, 23 Aug 2024 14:12:51 +0000 (16:12 +0200)
committerJunio C Hamano <gitster@pobox.com>
Fri, 23 Aug 2024 15:04:47 +0000 (08:04 -0700)
The lifetime of a table iterator may survive the lifetime of a reader
when the stack gets reloaded. Keep the reader from being released by
increasing its refcount while the iterator is still being used.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
reftable/reader.c
reftable/stack_test.c

index 64a0953e687555e2142a3d4b7577eb32e189da8e..f8770990876b89b83cdd5158d47249e45f4b0523 100644 (file)
@@ -175,6 +175,7 @@ static int table_iter_init(struct table_iter *ti, struct reftable_reader *r)
 {
        struct block_iter bi = BLOCK_ITER_INIT;
        memset(ti, 0, sizeof(*ti));
+       reftable_reader_incref(r);
        ti->r = r;
        ti->bi = bi;
        return 0;
@@ -262,6 +263,7 @@ static void table_iter_close(struct table_iter *ti)
 {
        table_iter_block_done(ti);
        block_iter_close(&ti->bi);
+       reftable_reader_decref(ti->r);
 }
 
 static int table_iter_next_block(struct table_iter *ti)
index bc3bf772749d545d9fca60a683d9864f58a9a961..7fb5beb7c944b6951c1875eab378736ca45449ba 100644 (file)
@@ -1076,6 +1076,55 @@ static void test_reftable_stack_compaction_concurrent_clean(void)
        clear_dir(dir);
 }
 
+static void test_reftable_stack_read_across_reload(void)
+{
+       struct reftable_write_options opts = { 0 };
+       struct reftable_stack *st1 = NULL, *st2 = NULL;
+       struct reftable_ref_record rec = { 0 };
+       struct reftable_iterator it = { 0 };
+       char *dir = get_tmp_dir(__LINE__);
+       int err;
+
+       /* Create a first stack and set up an iterator for it. */
+       err = reftable_new_stack(&st1, dir, &opts);
+       EXPECT_ERR(err);
+       write_n_ref_tables(st1, 2);
+       EXPECT(st1->merged->readers_len == 2);
+       reftable_stack_init_ref_iterator(st1, &it);
+       err = reftable_iterator_seek_ref(&it, "");
+       EXPECT_ERR(err);
+
+       /* Set up a second stack for the same directory and compact it. */
+       err = reftable_new_stack(&st2, dir, &opts);
+       EXPECT_ERR(err);
+       EXPECT(st2->merged->readers_len == 2);
+       err = reftable_stack_compact_all(st2, NULL);
+       EXPECT_ERR(err);
+       EXPECT(st2->merged->readers_len == 1);
+
+       /*
+        * Verify that we can continue to use the old iterator even after we
+        * have reloaded its stack.
+        */
+       err = reftable_stack_reload(st1);
+       EXPECT_ERR(err);
+       EXPECT(st1->merged->readers_len == 1);
+       err = reftable_iterator_next_ref(&it, &rec);
+       EXPECT_ERR(err);
+       EXPECT(!strcmp(rec.refname, "refs/heads/branch-0000"));
+       err = reftable_iterator_next_ref(&it, &rec);
+       EXPECT_ERR(err);
+       EXPECT(!strcmp(rec.refname, "refs/heads/branch-0001"));
+       err = reftable_iterator_next_ref(&it, &rec);
+       EXPECT(err > 0);
+
+       reftable_ref_record_release(&rec);
+       reftable_iterator_destroy(&it);
+       reftable_stack_destroy(st1);
+       reftable_stack_destroy(st2);
+       clear_dir(dir);
+}
+
 int stack_test_main(int argc, const char *argv[])
 {
        RUN_TEST(test_empty_add);
@@ -1098,6 +1147,7 @@ int stack_test_main(int argc, const char *argv[])
        RUN_TEST(test_reftable_stack_auto_compaction_fails_gracefully);
        RUN_TEST(test_reftable_stack_update_index_check);
        RUN_TEST(test_reftable_stack_uptodate);
+       RUN_TEST(test_reftable_stack_read_across_reload);
        RUN_TEST(test_suggest_compaction_segment);
        RUN_TEST(test_suggest_compaction_segment_nothing);
        return 0;