reftable/reader: keep readers alive during iteration
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>
Patrick Steinhardt committed
Aug 23, 2024 at 16:12 UTC
89eada4ea1f7a9c0a5f9b9e29592daa0847a79fc
2 files changed
+52
reftable/reader.c
+2
@@ -175,6 +175,7 @@ static int table_iter_init(struct table_iter *ti, struct reftable_reader *r)
175
{
176
struct block_iter bi = BLOCK_ITER_INIT;
177
memset(ti, 0, sizeof(*ti));
178
+ reftable_reader_incref(r);
179
ti->r = r;
180
ti->bi = bi;
181
return 0;
@@ -262,6 +263,7 @@ static void table_iter_close(struct table_iter *ti)
263
{
264
table_iter_block_done(ti);
265
block_iter_close(&ti->bi);
266
+ reftable_reader_decref(ti->r);
267
}
268
269
static int table_iter_next_block(struct table_iter *ti)
reftable/stack_test.c
+50
@@ -1076,6 +1076,55 @@ static void test_reftable_stack_compaction_concurrent_clean(void)
1076
clear_dir(dir);
1077
}
1078
1079
+static void test_reftable_stack_read_across_reload(void)
1080
+{
1081
+ struct reftable_write_options opts = { 0 };
1082
+ struct reftable_stack *st1 = NULL, *st2 = NULL;
1083
+ struct reftable_ref_record rec = { 0 };
1084
+ struct reftable_iterator it = { 0 };
1085
+ char *dir = get_tmp_dir(__LINE__);
1086
+ int err;
1087
+
1088
+ /* Create a first stack and set up an iterator for it. */
1089
+ err = reftable_new_stack(&st1, dir, &opts);
1090
+ EXPECT_ERR(err);
1091
+ write_n_ref_tables(st1, 2);
1092
+ EXPECT(st1->merged->readers_len == 2);
1093
+ reftable_stack_init_ref_iterator(st1, &it);
1094
+ err = reftable_iterator_seek_ref(&it, "");
1095
+ EXPECT_ERR(err);
1096
+
1097
+ /* Set up a second stack for the same directory and compact it. */
1098
+ err = reftable_new_stack(&st2, dir, &opts);
1099
+ EXPECT_ERR(err);
1100
+ EXPECT(st2->merged->readers_len == 2);
1101
+ err = reftable_stack_compact_all(st2, NULL);
1102
+ EXPECT_ERR(err);
1103
+ EXPECT(st2->merged->readers_len == 1);
1104
+
1105
+ /*
1106
+ * Verify that we can continue to use the old iterator even after we
1107
+ * have reloaded its stack.
1108
+ */
1109
+ err = reftable_stack_reload(st1);
1110
+ EXPECT_ERR(err);
1111
+ EXPECT(st1->merged->readers_len == 1);
1112
+ err = reftable_iterator_next_ref(&it, &rec);
1113
+ EXPECT_ERR(err);
1114
+ EXPECT(!strcmp(rec.refname, "refs/heads/branch-0000"));
1115
+ err = reftable_iterator_next_ref(&it, &rec);
1116
+ EXPECT_ERR(err);
1117
+ EXPECT(!strcmp(rec.refname, "refs/heads/branch-0001"));
1118
+ err = reftable_iterator_next_ref(&it, &rec);
1119
+ EXPECT(err > 0);
1120
+
1121
+ reftable_ref_record_release(&rec);
1122
+ reftable_iterator_destroy(&it);
1123
+ reftable_stack_destroy(st1);
1124
+ reftable_stack_destroy(st2);
1125
+ clear_dir(dir);
1126
+}
1127
+
1128
int stack_test_main(int argc, const char *argv[])
1129
{
1130
RUN_TEST(test_empty_add);
@@ -1098,6 +1147,7 @@ int stack_test_main(int argc, const char *argv[])
1147
RUN_TEST(test_reftable_stack_auto_compaction_fails_gracefully);
1148
RUN_TEST(test_reftable_stack_update_index_check);
1149
RUN_TEST(test_reftable_stack_uptodate);
1150
+ RUN_TEST(test_reftable_stack_read_across_reload);
1151
RUN_TEST(test_suggest_compaction_segment);
1152
RUN_TEST(test_suggest_compaction_segment_nothing);
1153
return 0;