Add forwarding pointers + deferred release

This commit is contained in:
2024-10-28 12:34:20 -07:00
parent cf25b8626c
commit 8ff7a112b7

View File

@@ -262,7 +262,11 @@ private:
struct Node { struct Node {
/* begin section that's copied to the next node */ /* begin section that's copied to the next node */
Entry entry; union {
Entry entry;
/* Set to the forwarding point for this node if deferredReleased is set */
Node *forwardTo;
};
Node *parent; Node *parent;
int32_t partialKeyLen; int32_t partialKeyLen;
int16_t numChildren; int16_t numChildren;
@@ -272,9 +276,19 @@ struct Node {
uint8_t parentsIndex; uint8_t parentsIndex;
/* end section that's copied to the next node */ /* end section that's copied to the next node */
/* If set, this node has been replaced and the next node in the forwarding
* chain is `forwardTo`*/
bool deferredReleased;
uint8_t *partialKey(); uint8_t *partialKey();
Type getType() const { return type; } Type getType() const {
int32_t getCapacity() const { return partialKeyCapacity; } assert(!deferredReleased);
return type;
}
int32_t getCapacity() const {
assert(!deferredReleased);
return partialKeyCapacity;
}
private: private:
template <class T> friend struct BoundedFreeListAllocator; template <class T> friend struct BoundedFreeListAllocator;
@@ -305,7 +319,10 @@ constexpr int kNodeCopySize =
struct Node0 : Node { struct Node0 : Node {
constexpr static auto kType = Type_Node0; constexpr static auto kType = Type_Node0;
uint8_t *partialKey() { return (uint8_t *)(this + 1); } uint8_t *partialKey() {
assert(!deferredReleased);
return (uint8_t *)(this + 1);
}
void copyChildrenAndKeyFrom(const Node0 &other); void copyChildrenAndKeyFrom(const Node0 &other);
void copyChildrenAndKeyFrom(const struct Node3 &other); void copyChildrenAndKeyFrom(const struct Node3 &other);
size_t size() const { return sizeof(Node0) + getCapacity(); } size_t size() const { return sizeof(Node0) + getCapacity(); }
@@ -320,7 +337,10 @@ struct Node3 : Node {
// Sorted // Sorted
uint8_t index[kMaxNodes]; uint8_t index[kMaxNodes];
uint8_t *partialKey() { return (uint8_t *)(this + 1); } uint8_t *partialKey() {
assert(!deferredReleased);
return (uint8_t *)(this + 1);
}
void copyChildrenAndKeyFrom(const Node0 &other); void copyChildrenAndKeyFrom(const Node0 &other);
void copyChildrenAndKeyFrom(const Node3 &other); void copyChildrenAndKeyFrom(const Node3 &other);
void copyChildrenAndKeyFrom(const struct Node16 &other); void copyChildrenAndKeyFrom(const struct Node16 &other);
@@ -336,7 +356,10 @@ struct Node16 : Node {
// Sorted // Sorted
uint8_t index[kMaxNodes]; uint8_t index[kMaxNodes];
uint8_t *partialKey() { return (uint8_t *)(this + 1); } uint8_t *partialKey() {
assert(!deferredReleased);
return (uint8_t *)(this + 1);
}
void copyChildrenAndKeyFrom(const Node3 &other); void copyChildrenAndKeyFrom(const Node3 &other);
void copyChildrenAndKeyFrom(const Node16 &other); void copyChildrenAndKeyFrom(const Node16 &other);
void copyChildrenAndKeyFrom(const struct Node48 &other); void copyChildrenAndKeyFrom(const struct Node48 &other);
@@ -358,7 +381,10 @@ struct Node48 : Node {
uint8_t reverseIndex[kMaxNodes]; uint8_t reverseIndex[kMaxNodes];
int8_t index[256]; int8_t index[256];
uint8_t *partialKey() { return (uint8_t *)(this + 1); } uint8_t *partialKey() {
assert(!deferredReleased);
return (uint8_t *)(this + 1);
}
void copyChildrenAndKeyFrom(const Node16 &other); void copyChildrenAndKeyFrom(const Node16 &other);
void copyChildrenAndKeyFrom(const Node48 &other); void copyChildrenAndKeyFrom(const Node48 &other);
void copyChildrenAndKeyFrom(const struct Node256 &other); void copyChildrenAndKeyFrom(const struct Node256 &other);
@@ -378,7 +404,10 @@ struct Node256 : Node {
InternalVersionT childMaxVersion[kMaxNodes]; InternalVersionT childMaxVersion[kMaxNodes];
InternalVersionT maxOfMax[kMaxOfMaxTotalPages]; InternalVersionT maxOfMax[kMaxOfMaxTotalPages];
uint8_t *partialKey() { return (uint8_t *)(this + 1); } uint8_t *partialKey() {
assert(!deferredReleased);
return (uint8_t *)(this + 1);
}
void copyChildrenAndKeyFrom(const Node48 &other); void copyChildrenAndKeyFrom(const Node48 &other);
void copyChildrenAndKeyFrom(const Node256 &other); void copyChildrenAndKeyFrom(const Node256 &other);
size_t size() const { return sizeof(Node256) + getCapacity(); } size_t size() const { return sizeof(Node256) + getCapacity(); }
@@ -679,6 +708,7 @@ template <class T> struct BoundedFreeListAllocator {
T *allocate(int partialKeyCapacity) { T *allocate(int partialKeyCapacity) {
T *result = allocate_helper(partialKeyCapacity); T *result = allocate_helper(partialKeyCapacity);
result->endOfRange = false; result->endOfRange = false;
result->deferredReleased = false;
if constexpr (!std::is_same_v<T, Node0>) { if constexpr (!std::is_same_v<T, Node0>) {
memset(result->children, 0, sizeof(result->children)); memset(result->children, 0, sizeof(result->children));
const auto z = InternalVersionT::zero; const auto z = InternalVersionT::zero;
@@ -821,7 +851,42 @@ struct WriteContext {
} }
} }
// Place in a list to be released in the next call to releaseDeferred.
void deferRelease(Node *n) {
n->parent = deferredList;
deferredList = n;
}
// Release all nodes passed to deferRelease since the last call to
// releaseDeferred.
void releaseDeferred() {
for (Node *n = std::exchange(deferredList, nullptr); n != nullptr;) {
auto *tmp = n;
n = n->parent;
switch (tmp->getType()) {
case Type_Node0:
release(static_cast<Node0 *>(tmp));
break;
case Type_Node3:
release(static_cast<Node3 *>(tmp));
break;
case Type_Node16:
release(static_cast<Node16 *>(tmp));
break;
case Type_Node48:
release(static_cast<Node48 *>(tmp));
break;
case Type_Node256:
release(static_cast<Node256 *>(tmp));
break;
default: // GCOVR_EXCL_LINE
__builtin_unreachable(); // GCOVR_EXCL_LINE
}
}
}
private: private:
Node *deferredList = nullptr;
BoundedFreeListAllocator<Node0> node0; BoundedFreeListAllocator<Node0> node0;
BoundedFreeListAllocator<Node3> node3; BoundedFreeListAllocator<Node3> node3;
BoundedFreeListAllocator<Node16> node16; BoundedFreeListAllocator<Node16> node16;