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:
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user