Fix bugs found by exercising the code
This commit is contained in:
2
.clangd
2
.clangd
@@ -1,2 +1,2 @@
|
|||||||
CompileFlags:
|
CompileFlags:
|
||||||
Add: [-DENABLE_MAIN, -UNDEBUG, -DENABLE_FUZZ, -DTHREAD_TEST, -fexceptions]
|
Add: [-DENABLE_MAIN, -UNDEBUG, -DENABLE_FUZZ, -DTHREAD_TEST, -fexceptions, -DDEBUG_VERBOSE=1]
|
||||||
|
@@ -9,6 +9,10 @@
|
|||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
#include <xxhash.h>
|
#include <xxhash.h>
|
||||||
|
|
||||||
|
#ifndef DEBUG_VERBOSE
|
||||||
|
#define DEBUG_VERBOSE 0
|
||||||
|
#endif
|
||||||
|
|
||||||
void *mmapSafe(void *addr, size_t len, int prot, int flags, int fd,
|
void *mmapSafe(void *addr, size_t len, int prot, int flags, int fd,
|
||||||
off_t offset) {
|
off_t offset) {
|
||||||
void *result = mmap(addr, len, prot, flags, fd, offset);
|
void *result = mmap(addr, len, prot, flags, fd, offset);
|
||||||
@@ -254,6 +258,9 @@ struct MemManager {
|
|||||||
if (newFirstUnaddressable < firstUnaddressable) {
|
if (newFirstUnaddressable < firstUnaddressable) {
|
||||||
for (int i = newFirstUnaddressable; i < firstUnaddressable; ++i) {
|
for (int i = newFirstUnaddressable; i < firstUnaddressable; ++i) {
|
||||||
if (base[i].entry != nullptr) {
|
if (base[i].entry != nullptr) {
|
||||||
|
#if DEBUG_VERBOSE
|
||||||
|
printf("Collecting %u\n", i);
|
||||||
|
#endif
|
||||||
base[i].entry->delref();
|
base[i].entry->delref();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -269,6 +276,9 @@ struct MemManager {
|
|||||||
reachable.iterateAbsentApproxBackwards(
|
reachable.iterateAbsentApproxBackwards(
|
||||||
[&](uint32_t i) {
|
[&](uint32_t i) {
|
||||||
if (base[i].entry != nullptr) {
|
if (base[i].entry != nullptr) {
|
||||||
|
#if DEBUG_VERBOSE
|
||||||
|
printf("Collecting %u\n", i);
|
||||||
|
#endif
|
||||||
base[i].entry->delref();
|
base[i].entry->delref();
|
||||||
base[i].entry = nullptr;
|
base[i].entry = nullptr;
|
||||||
}
|
}
|
||||||
@@ -311,7 +321,7 @@ struct RootSet {
|
|||||||
/// Inform that there will be no calls to rootForVersion with a version less
|
/// Inform that there will be no calls to rootForVersion with a version less
|
||||||
/// than `oldestVersion`
|
/// than `oldestVersion`
|
||||||
void setOldestVersion(int64_t oldestVersion) {
|
void setOldestVersion(int64_t oldestVersion) {
|
||||||
const uint32_t firstToKeep = rootForVersion(oldestVersion);
|
const uint32_t firstToKeep = lastLeq(oldestVersion);
|
||||||
|
|
||||||
if (firstToKeep != 0) {
|
if (firstToKeep != 0) {
|
||||||
memmove(nodes, nodes + firstToKeep,
|
memmove(nodes, nodes + firstToKeep,
|
||||||
@@ -321,29 +331,12 @@ struct RootSet {
|
|||||||
end -= firstToKeep;
|
end -= firstToKeep;
|
||||||
}
|
}
|
||||||
assert(end > 0);
|
assert(end > 0);
|
||||||
assert(nodes[0] <= oldestVersion);
|
assert(versions[0] <= oldestVersion);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a root node that can correctly be used for `version`
|
/// Get a root node that can correctly be used for `version`
|
||||||
uint32_t rootForVersion(int64_t version) const {
|
uint32_t rootForVersion(int64_t version) const {
|
||||||
assert(end > 0);
|
return nodes[lastLeq(version)];
|
||||||
assert(nodes[0] <= version);
|
|
||||||
|
|
||||||
// Find the last version <= oldestVersion
|
|
||||||
int left = 0;
|
|
||||||
int right = end;
|
|
||||||
int result = 0;
|
|
||||||
while (left <= right) {
|
|
||||||
int mid = left + (right - left) / 2;
|
|
||||||
if (versions[mid] <= version) {
|
|
||||||
result = mid;
|
|
||||||
left = mid + 1;
|
|
||||||
} else {
|
|
||||||
right = mid - 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assert(result < end);
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const uint32_t *roots() const { return nodes; }
|
const uint32_t *roots() const { return nodes; }
|
||||||
@@ -364,6 +357,26 @@ struct RootSet {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
uint32_t lastLeq(int64_t version) const {
|
||||||
|
assert(end > 0);
|
||||||
|
assert(versions[0] <= version);
|
||||||
|
|
||||||
|
// Find the last version <= oldestVersion
|
||||||
|
int left = 1;
|
||||||
|
int right = end - 1;
|
||||||
|
int result = 0;
|
||||||
|
while (left <= right) {
|
||||||
|
int mid = left + (right - left) / 2;
|
||||||
|
if (versions[mid] <= version) {
|
||||||
|
result = mid;
|
||||||
|
left = mid + 1;
|
||||||
|
} else {
|
||||||
|
right = mid - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert(result < end);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
uint32_t *nodes;
|
uint32_t *nodes;
|
||||||
// versions[i] is the version of nodes[i]
|
// versions[i] is the version of nodes[i]
|
||||||
int64_t *versions;
|
int64_t *versions;
|
||||||
@@ -463,6 +476,27 @@ struct VersionedMap::Impl {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setOldestVersion(int64_t oldestVersion) {
|
||||||
|
roots.setOldestVersion(oldestVersion);
|
||||||
|
mm.gc(roots.roots(), roots.rootCount(), oldestVersion);
|
||||||
|
}
|
||||||
|
|
||||||
|
void printInOrder(int64_t version) {
|
||||||
|
printInOrderHelper(version, roots.rootForVersion(version));
|
||||||
|
}
|
||||||
|
|
||||||
|
void printInOrderHelper(int64_t version, uint32_t node) {
|
||||||
|
if (node == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
printInOrderHelper(version,
|
||||||
|
child<std::memory_order_relaxed>(node, false, version));
|
||||||
|
printf("%.*s\n", (int)mm.base[node].entry->keyLen,
|
||||||
|
mm.base[node].entry->getKey());
|
||||||
|
printInOrderHelper(version,
|
||||||
|
child<std::memory_order_relaxed>(node, true, version));
|
||||||
|
}
|
||||||
|
|
||||||
MemManager mm;
|
MemManager mm;
|
||||||
RootSet roots;
|
RootSet roots;
|
||||||
};
|
};
|
||||||
@@ -472,6 +506,23 @@ struct VersionedMap::Impl {
|
|||||||
#include <nanobench.h>
|
#include <nanobench.h>
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
|
|
||||||
|
{
|
||||||
|
weaselab::VersionedMap::Impl impl;
|
||||||
|
impl.roots.add(impl.newNode(1, (const uint8_t *)"a", 1, nullptr, -1, false),
|
||||||
|
1);
|
||||||
|
impl.roots.add(impl.newNode(2, (const uint8_t *)"b", 1, nullptr, -1, false),
|
||||||
|
2);
|
||||||
|
impl.roots.add(impl.newNode(3, (const uint8_t *)"c", 1, nullptr, -1, false),
|
||||||
|
3);
|
||||||
|
impl.printInOrder(0);
|
||||||
|
impl.printInOrder(1);
|
||||||
|
impl.printInOrder(2);
|
||||||
|
impl.printInOrder(3);
|
||||||
|
impl.setOldestVersion(3);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
|
||||||
ankerl::nanobench::Bench bench;
|
ankerl::nanobench::Bench bench;
|
||||||
bench.minEpochIterations(5000);
|
bench.minEpochIterations(5000);
|
||||||
weaselab::MemManager mm;
|
weaselab::MemManager mm;
|
||||||
|
Reference in New Issue
Block a user