Reimplemented Brent's cycle detection on top of mtree traversal

This is a bit tricky because our tortoise state is now quite large
thanks to how we are nesting traversals:

- Current mdir pair
- Current mtree block+trunk
- Current btree block+trunk? (TODO)
- Others? (TODO)

This also raises some questions about what constitutes a cycle in our
btrees. Since they are strictly CoW, they should be strictly DAGs worst
case. But is that still true when considering that btree nodes can
contain multiple trunk versions?

To be safe, I'm currently including the trunks in our tortoise state,
but it may be possible to relax this in the future.
This commit is contained in:
Christopher Haster
2023-05-27 17:14:25 -05:00
parent 6a96866737
commit 1631ca8d78
+34 -1
View File
@@ -4314,6 +4314,7 @@ typedef struct lfsr_btree_traversal {
#define LFSR_BTREE_TRAVERSAL_INIT ((lfsr_btree_traversal_t){ \
.bid = 0, \
.rid = 0, \
.branch.block = 0, \
.branch.trunk = 0, \
.branch.weight = 0, \
})
@@ -5511,9 +5512,17 @@ static int lfsr_mdir_commit(lfs_t *lfs, lfsr_mdir_t *mdir, lfs_ssize_t *rid,
// incremental mtree traversal
typedef struct lfsr_mtree_traversal {
uint8_t flags;
// core traversal state
lfsr_mdir_t mdir;
lfsr_btree_traversal_t mtraversal;
uint8_t flags;
// cycle detection state
uint8_t tortoise_power;
lfs_size_t tortoise_step;
lfsr_mpair_t tortoise_mpair;
lfs_block_t tortoise_mtree_block;
lfs_size_t tortoise_mtree_trunk;
} lfsr_mtree_traversal_t;
enum {
@@ -5524,16 +5533,40 @@ enum {
.flags = _flags, \
.mdir.rbyd.trunk = 0, \
.mtraversal = LFSR_BTREE_TRAVERSAL_INIT, \
.tortoise_power = 0, \
.tortoise_step = 0, \
})
static int lfsr_mtree_traversal_next(lfs_t *lfs,
lfsr_mtree_traversal_t *traversal,
lfs_size_t *mid_, lfsr_tag_t *tag_, lfsr_data_t *data_) {
// detect cycles with Brent's algorithm
if (traversal->mdir.rbyd.trunk != 0
&& lfsr_mpair_eq(lfsr_mdir_mpair(&traversal->mdir),
traversal->tortoise_mpair)
&& traversal->mtraversal.branch.block
== traversal->tortoise_mtree_block
&& traversal->mtraversal.branch.trunk
== traversal->tortoise_mtree_trunk) {
LFS_ERROR("Cycle detected during mtree traversal");
return LFS_ERR_CORRUPT;
}
if (traversal->tortoise_step
== ((lfs_size_t)1 << traversal->tortoise_power)) {
traversal->tortoise_mpair = lfsr_mdir_mpair(&traversal->mdir);
traversal->tortoise_mtree_block = traversal->mtraversal.branch.block;
traversal->tortoise_mtree_trunk = traversal->mtraversal.branch.trunk;
traversal->tortoise_step = 0;
traversal->tortoise_power += 1;
}
traversal->tortoise_step += 1;
// new traversal? start with 0x{0,1}
//
// note we make sure to include fake mroots!
//
if (traversal->mdir.rbyd.trunk == 0) {
// fetch the first mroot 0x{0,1}
int err = lfsr_mdir_fetch(lfs, &traversal->mdir,
-1, LFSR_MPAIR(0, 1), NULL);
if (err) {