diff --git a/ConflictSet.cpp b/ConflictSet.cpp index 13fdda3..4ab790b 100644 --- a/ConflictSet.cpp +++ b/ConflictSet.cpp @@ -2892,8 +2892,10 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl { void addWrites(const WriteRange *writes, int count, int64_t writeVersion) { assert(writeVersion >= newestVersionFullPrecision); - // TODO allow this condition - assert(writeVersion < newestVersionFullPrecision + kNominalVersionWindow); + if (writeVersion > newestVersionFullPrecision + kNominalVersionWindow) { + destroyTree(root); + init(writeVersion - kNominalVersionWindow); + } newestVersionFullPrecision = writeVersion; setOldestVersion( @@ -2985,14 +2987,18 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl { keyUpdates = gcScanStep(keyUpdates); } - explicit Impl(int64_t oldestVersion) - : oldestVersion(oldestVersion), oldestVersionFullPrecision(oldestVersion), - oldestExtantVersion(oldestVersion), - oldestVersionAtGcBegin(oldestVersion), - newestVersionFullPrecision(oldestVersion) { -#if DEBUG_VERBOSE - fprintf(stderr, "radix_tree: create\n"); -#endif + int64_t getBytes() const { return totalBytes; } + + void init(int64_t oldestVersion) { + this->oldestVersion = InternalVersionT(oldestVersion); + oldestVersionFullPrecision = oldestExtantVersion = oldestVersionAtGcBegin = + newestVersionFullPrecision = oldestVersion; + + allocators = {}; + + removalKeyArena = Arena{}; + removalKey = {}; + keyUpdates = 10; // Insert "" root = allocators.node0.allocate(0); @@ -3009,26 +3015,22 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl { root->entry.rangeVersion = this->oldestVersion; InternalVersionT::zero = this->oldestVersion; + + // Intentionally not resetting totalBytes } - ~Impl() { -#if DEBUG_VERBOSE - fprintf(stderr, "radix_tree: destroy\n"); -#endif - destroyTree(root); - } + + explicit Impl(int64_t oldestVersion) { init(oldestVersion); } + ~Impl() { destroyTree(root); } NodeAllocators allocators; Arena removalKeyArena; std::span removalKey; - int64_t keyUpdates = 10; + int64_t keyUpdates; Node *root; InternalVersionT rootMaxVersion; InternalVersionT oldestVersion; - // TODO this doesn't fully mitigate the 32-bit precision issue, since we still - // need to make sure we clean up versions in the tree before they fall out of - // the `kMaxCorrectVersionWindow` window. int64_t oldestVersionFullPrecision; int64_t oldestExtantVersion; int64_t oldestVersionAtGcBegin; @@ -3163,7 +3165,7 @@ void internal_destroy(ConflictSet::Impl *impl) { safe_free(impl, sizeof(ConflictSet::Impl)); } -int64_t internal_getBytes(ConflictSet::Impl *impl) { return impl->totalBytes; } +int64_t internal_getBytes(ConflictSet::Impl *impl) { return impl->getBytes(); } // ==================== END IMPLEMENTATION ==================== diff --git a/Internal.h b/Internal.h index daf810b..b4a47db 100644 --- a/Internal.h +++ b/Internal.h @@ -598,6 +598,7 @@ template struct TestDriver { // Call until it returns true, for "done". Check internal invariants etc // between calls to next. bool next() { + assert(cs.getBytes() >= 0); if (!arbitrary.hasEntropy()) { return true; } @@ -605,7 +606,9 @@ template struct TestDriver { { int numPointWrites = arbitrary.bounded(100); int numRangeWrites = arbitrary.bounded(100); - int64_t v = (writeVersion += arbitrary.bounded(2e9)); + int64_t v = + (writeVersion += arbitrary.bounded(10) == 0 ? arbitrary.bounded(10) + : arbitrary.next()); auto *writes = new (arena) ConflictSet::WriteRange[numPointWrites + numRangeWrites]; auto keys = set(arena); diff --git a/test_conflict_set.py b/test_conflict_set.py index 34cdf2c..ea046ee 100644 --- a/test_conflict_set.py +++ b/test_conflict_set.py @@ -84,6 +84,14 @@ def test_two_billion_versions(): cs.check(read(1, b"\x00", b"\xff")) +def test_positive_bytes(): + with DebugConflictSet() as cs: + cs.addWrites(1, write(b"hello")) + assert cs.getBytes() > 0 + cs.addWrites(1 + 2**32) + assert cs.getBytes() > 0 + + def test_decrease_capacity(): # make a Node48, then a Node256 for count in (17, 49):