Skip over point writes in phase 3
This commit is contained in:
@@ -4115,12 +4115,25 @@ struct Job {
|
||||
// original key, and there is existing node in the tree further along the search
|
||||
// path of the original key
|
||||
struct Result {
|
||||
Result() = default;
|
||||
Result(Node *insertionPoint, TrivialSpan remaining)
|
||||
: insertionPoint(insertionPoint), remaining(remaining),
|
||||
endInsertionPoint(nullptr) {}
|
||||
Result(Node *insertionPoint, TrivialSpan remaining, Node *endInsertionPoint,
|
||||
TrivialSpan endRemaining)
|
||||
: insertionPoint(insertionPoint), remaining(remaining),
|
||||
endInsertionPoint(endInsertionPoint), endRemaining(endRemaining) {}
|
||||
|
||||
Node *insertionPoint;
|
||||
TrivialSpan remaining;
|
||||
|
||||
Node *endInsertionPoint; // Range write only
|
||||
TrivialSpan endRemaining; // Range write only
|
||||
|
||||
Result *nextRangeWrite; // Linked list to skip over point writes in phase 3.
|
||||
// Populated in phase 2
|
||||
};
|
||||
static_assert(std::is_trivial_v<Result>);
|
||||
|
||||
// State relevant to every insertion
|
||||
struct Context {
|
||||
@@ -4167,12 +4180,12 @@ template <class NodeT> void pointIter(Job *job, Context *context) {
|
||||
TaggedNodePointer *child =
|
||||
getChildUpdatingMaxVersion(n, job->remaining, context->writeVersion);
|
||||
if (child == nullptr) [[unlikely]] {
|
||||
context->results[job->index] = {job->n, job->remaining, nullptr, {}};
|
||||
context->results[job->index] = {job->n, job->remaining};
|
||||
MUSTTAIL return complete(job, context);
|
||||
}
|
||||
job->n = *child;
|
||||
if (job->remaining.size() == 0) [[unlikely]] {
|
||||
context->results[job->index] = {job->n, job->remaining, nullptr, {}};
|
||||
context->results[job->index] = {job->n, job->remaining};
|
||||
MUSTTAIL return complete(job, context);
|
||||
}
|
||||
++context->iterations;
|
||||
@@ -4315,7 +4328,7 @@ pointWrite:
|
||||
remaining = TrivialSpan(context->writes[index].begin.p,
|
||||
context->writes[index].begin.len);
|
||||
if (remaining.size() == 0) [[unlikely]] {
|
||||
context->results[index] = {n, remaining, nullptr, {}};
|
||||
context->results[index] = {n, remaining};
|
||||
continuation = complete;
|
||||
} else {
|
||||
continuation = pointIterTable[n->getType()];
|
||||
@@ -4895,6 +4908,9 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
|
||||
// old nodes get forwarding pointers installed and are released after
|
||||
// phase 2. The search path of a node does not change in this phase.
|
||||
|
||||
interleaved_insert::Result *firstRangeWrite = nullptr;
|
||||
interleaved_insert::Result *lastRangeWrite;
|
||||
|
||||
for (int i = 0; i < count; ++i) {
|
||||
|
||||
#if DEBUG_VERBOSE && !defined(NDEBUG)
|
||||
@@ -4927,6 +4943,13 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
|
||||
context.results[i].remaining, writeVersion,
|
||||
&writeContext);
|
||||
} else {
|
||||
if (firstRangeWrite == nullptr) {
|
||||
firstRangeWrite = context.results + i;
|
||||
} else {
|
||||
lastRangeWrite->nextRangeWrite = context.results + i;
|
||||
}
|
||||
lastRangeWrite = context.results + i;
|
||||
lastRangeWrite->nextRangeWrite = nullptr;
|
||||
while (context.results[i].endInsertionPoint->releaseDeferred) {
|
||||
context.results[i].endInsertionPoint =
|
||||
context.results[i].endInsertionPoint->forwardTo;
|
||||
@@ -4943,18 +4966,17 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
|
||||
// Phase 3: Erase nodes within written ranges. Going left to right ensures
|
||||
// that nothing later is on the search path of anything earlier, so we don't
|
||||
// encounter invalidated nodes.
|
||||
for (int i = 0; i < count; ++i) {
|
||||
if (context.results[i].endInsertionPoint != nullptr) {
|
||||
while (context.results[i].insertionPoint->releaseDeferred) {
|
||||
context.results[i].insertionPoint =
|
||||
context.results[i].insertionPoint->forwardTo;
|
||||
for (auto *iter = firstRangeWrite; iter != nullptr;
|
||||
iter = iter->nextRangeWrite) {
|
||||
if (iter->endInsertionPoint != nullptr) {
|
||||
while (iter->insertionPoint->releaseDeferred) {
|
||||
iter->insertionPoint = iter->insertionPoint->forwardTo;
|
||||
}
|
||||
while (context.results[i].endInsertionPoint->releaseDeferred) {
|
||||
context.results[i].endInsertionPoint =
|
||||
context.results[i].endInsertionPoint->forwardTo;
|
||||
while (iter->endInsertionPoint->releaseDeferred) {
|
||||
iter->endInsertionPoint = iter->endInsertionPoint->forwardTo;
|
||||
}
|
||||
eraseInRange(context.results[i].insertionPoint,
|
||||
context.results[i].endInsertionPoint, &writeContext, this);
|
||||
eraseInRange(iter->insertionPoint, iter->endInsertionPoint,
|
||||
&writeContext, this);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -645,7 +645,8 @@ struct TestDriver {
|
||||
|
||||
bool ok = true;
|
||||
|
||||
const int prefixLen = arbitrary->bounded(512);
|
||||
// const int prefixLen = arbitrary->bounded(512);
|
||||
const int prefixLen = 0;
|
||||
const int prefixByte = arbitrary->randT<uint8_t>();
|
||||
|
||||
// Call until it returns true, for "done". Check internal invariants etc
|
||||
|
Reference in New Issue
Block a user