Fix two node-copying bugs in update

This commit is contained in:
2024-05-14 17:44:01 -07:00
parent 862fc3297c
commit 67c8ca8f3a
4 changed files with 74 additions and 17 deletions

View File

@@ -136,7 +136,6 @@ struct Facade {
int64_t getVersion() const { return versioned.getVersion(); } int64_t getVersion() const { return versioned.getVersion(); }
int64_t getOldestVersion() const { return versioned.getOldestVersion(); } int64_t getOldestVersion() const { return versioned.getOldestVersion(); }
private:
std::vector<std::pair<String, String>> unversionedRead(const String &begin, std::vector<std::pair<String, String>> unversionedRead(const String &begin,
const String &end, const String &end,
int &limit, int &limit,

View File

@@ -1,4 +1,5 @@
#include "Facade.h" #include "Facade.h"
#include "PrintMutation.h"
#include "VersionedMap.h" #include "VersionedMap.h"
#include <inttypes.h> #include <inttypes.h>
@@ -24,6 +25,8 @@ weaselab::VersionedMap::Mutation clear(weaselab::VersionedMap::Key begin,
return {begin.p, end.p, begin.len, end.len, weaselab::VersionedMap::Clear}; return {begin.p, end.p, begin.len, end.len, weaselab::VersionedMap::Clear};
} }
void breakpoint_me() {}
int main() { int main() {
Facade f(0); Facade f(0);
int64_t version = 1; int64_t version = 1;
@@ -42,13 +45,14 @@ int main() {
} }
for (int64_t i = 0; i < version; ++i) { for (int64_t i = 0; i < version; ++i) {
printf("--- version %" PRId64 " ---\n", i); printf("--- version %" PRId64 " ---\n", i);
auto result = f.viewAt(i).rangeRead("a"_s, "d"_s, 10, false); printf("merged:\n");
auto result = f.viewAt(i).rangeRead("a"_s, "z"_s, 100, false);
for (const auto &[k, v] : result) { for (const auto &[k, v] : result) {
for (auto c : k) { for (auto c : k) {
if (isprint(c)) { if (isprint(c)) {
printf("%c", c); printf("%c", c);
} else { } else {
printf("0x%02x", c); printf("x%02x", c);
} }
} }
printf(" -> "); printf(" -> ");
@@ -56,10 +60,35 @@ int main() {
if (isprint(c)) { if (isprint(c)) {
printf("%c", c); printf("%c", c);
} else { } else {
printf("0x%02x", c); printf("x%02x", c);
} }
} }
printf("\n"); printf("\n");
} }
printf("unversioned:\n");
for (const auto &[k, v] : f.unversioned) {
for (auto c : k) {
if (isprint(c)) {
printf("%c", c);
} else {
printf("x%02x", c);
}
}
printf(" -> ");
for (auto c : v) {
if (isprint(c)) {
printf("%c", c);
} else {
printf("x%02x", c);
}
}
printf("\n");
}
breakpoint_me();
printf("versioned:\n");
for (auto iter = f.versioned.begin(i), end = f.versioned.end(i);
iter != end; ++iter) {
printMutation(*iter);
}
} }
} }

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include "VersionedMap.h" #include "VersionedMap.h"
#include <ctype.h>
#include <inttypes.h> #include <inttypes.h>
#include <stdio.h> #include <stdio.h>
@@ -10,22 +11,42 @@ printMutation(const weaselab::VersionedMap::Iterator::VersionedMutation &m) {
case weaselab::VersionedMap::Set: case weaselab::VersionedMap::Set:
printf("set "); printf("set ");
for (int i = 0; i < m.param1Len; ++i) { for (int i = 0; i < m.param1Len; ++i) {
printf("x%02x", m.param1[i]); auto c = m.param1[i];
if (isprint(c)) {
printf("%c", c);
} else {
printf("x%02x", c);
}
} }
printf(" -> '"); printf(" -> '");
for (int i = 0; i < m.param2Len; ++i) { for (int i = 0; i < m.param2Len; ++i) {
printf("x%02x", m.param2[i]); auto c = m.param2[i];
if (isprint(c)) {
printf("%c", c);
} else {
printf("x%02x", c);
}
} }
printf("' @ %" PRId64 "\n", m.version); printf("' @ %" PRId64 "\n", m.version);
break; break;
case weaselab::VersionedMap::Clear: case weaselab::VersionedMap::Clear:
printf("clear ["); printf("clear [");
for (int i = 0; i < m.param1Len; ++i) { for (int i = 0; i < m.param1Len; ++i) {
printf("x%02x", m.param1[i]); auto c = m.param1[i];
if (isprint(c)) {
printf("%c", c);
} else {
printf("x%02x", c);
}
} }
printf(", "); printf(", ");
for (int i = 0; i < m.param2Len; ++i) { for (int i = 0; i < m.param2Len; ++i) {
printf("x%02x", m.param2[i]); auto c = m.param2[i];
if (isprint(c)) {
printf("%c", c);
} else {
printf("x%02x", c);
}
} }
printf(") @ %" PRId64 "\n", m.version); printf(") @ %" PRId64 "\n", m.version);
break; break;

View File

@@ -638,7 +638,8 @@ struct __attribute__((__visibility__("hidden"))) VersionedMap::Impl {
auto &c = mm.base[copy]; auto &c = mm.base[copy];
c.entry = n.entry->addref(); c.entry = n.entry->addref();
c.pointer[which] = child; c.pointer[which] = child;
c.pointer[!which] = n.pointer[!which]; c.pointer[!which] =
this->child<std::memory_order_relaxed>(node, !which, latestVersion);
c.updated.store(false, std::memory_order_relaxed); c.updated.store(false, std::memory_order_relaxed);
c.updateVersion = version; c.updateVersion = version;
assert(copy == 0 || copy >= kMinAddressable); assert(copy == 0 || copy >= kMinAddressable);
@@ -649,11 +650,12 @@ struct __attribute__((__visibility__("hidden"))) VersionedMap::Impl {
// The reason these aren't data races is that concurrent readers are // The reason these aren't data races is that concurrent readers are
// reading < `version` // reading < `version`
if (updated && n.replacedPointer != which) { if (updated && n.replacedPointer != which) {
auto result = doCopy();
// We can't update n.replacedPointer without introducing a data race // We can't update n.replacedPointer without introducing a data race
// (unless we packed it into the atomic?) so we copy. pointer[2] becomes // (unless we packed it into the atomic?) so we copy. pointer[2] becomes
// unreachable, but need to tell the garbage collector. // unreachable, but need to tell the garbage collector.
n.pointer[2] = 0; n.pointer[2] = 0;
return doCopy(); return result;
} else if (updated) { } else if (updated) {
n.pointer[2] = child; n.pointer[2] = child;
} else { } else {
@@ -808,12 +810,13 @@ struct __attribute__((__visibility__("hidden"))) VersionedMap::Impl {
void remove(Finger &finger) { void remove(Finger &finger) {
#ifndef NDEBUG #ifndef NDEBUG
uint32_t expected; Entry *expected;
{ {
Finger copy; Finger copy;
finger.copyTo(copy); finger.copyTo(copy);
move<std::memory_order_relaxed>(copy, latestVersion, true); move<std::memory_order_relaxed>(copy, latestVersion, true);
expected = copy.searchPathSize() > 0 ? copy.backNode() : 0; expected =
copy.searchPathSize() > 0 ? mm.base[copy.backNode()].entry : nullptr;
} }
#endif #endif
@@ -850,7 +853,7 @@ struct __attribute__((__visibility__("hidden"))) VersionedMap::Impl {
// propagate up the search path, all the way to the root since we may have // propagate up the search path, all the way to the root since we may have
// more rotations to do even if an update doesn't change a node pointer // more rotations to do even if an update doesn't change a node pointer
auto node = finger.backNode(); auto node = finger.backNode();
const auto oldSize = finger.searchPathSize(); const auto oldSize = finger.searchPathSize() - (node == 0);
for (;;) { for (;;) {
if (finger.searchPathSize() == 1) { if (finger.searchPathSize() == 1) {
// Made it to the root // Made it to the root
@@ -873,12 +876,17 @@ struct __attribute__((__visibility__("hidden"))) VersionedMap::Impl {
finger.push(c, false); finger.push(c, false);
} }
} else { } else {
move<std::memory_order_relaxed>(finger, latestVersion, true); while (finger.searchPathSize() > 1 && finger.backDirection() == true) {
finger.pop();
}
finger.pop();
} }
#ifndef NDEBUG #ifndef NDEBUG
if (finger.searchPathSize() > 0) { if (finger.searchPathSize() > 0) {
assert(finger.backNode() == expected); assert(mm.base[finger.backNode()].entry == expected);
} else {
assert(expected == nullptr);
} }
#endif #endif
} }
@@ -1342,7 +1350,7 @@ void VersionedMap::Impl::printInOrderHelper(int64_t version, uint32_t node,
return; return;
} }
printInOrderHelper(version, printInOrderHelper(version,
child<std::memory_order_relaxed>(node, false, version), child<std::memory_order_relaxed>(node, true, version),
depth + 1); depth + 1);
for (int i = 0; i < depth; ++i) { for (int i = 0; i < depth; ++i) {
printf(" "); printf(" ");
@@ -1360,7 +1368,7 @@ void VersionedMap::Impl::printInOrderHelper(int64_t version, uint32_t node,
} }
printf("\n"); printf("\n");
VersionedMap::Impl::printInOrderHelper( VersionedMap::Impl::printInOrderHelper(
version, child<std::memory_order_relaxed>(node, true, version), version, child<std::memory_order_relaxed>(node, false, version),
depth + 1); depth + 1);
} }