Compare commits

..
6 Commits
Author SHA1 Message Date
andrew 46d1f05333 Avoid signed overflow
This only happens at implausibly high version numbers, but the fix is straightforward so we'll just do it anyway.

Closes #84
2026-09-23 11:36:12 -04:00
andrew 8a16f9a6d6 Merge pull request 'Fix broken endInsertionPoint forwarding chain resolution in DEBUG_VERBOSE print' (#83) from weaselbot/conflict-set:weaselbot/issue-82 into main
Reviewed-on: weaselab/conflict-set#83
2026-08-31 01:14:10 +00:00
weaselbot 9a2c64d31c Fix broken endInsertionPoint forwarding chain resolution in DEBUG_VERBOSE print
In the Phase 2 debug block of ConflictSet::Impl::interleavedWrites, the
loop resolving the end insertion point's releaseDeferred/forwardTo chain
followed the begin node's chain instead (e = b->forwardTo). Once b is
resolved, b->forwardTo aliases the Entry union member, so e was assigned
garbage derived from version bytes and the loop dereferenced it,
causing a SEGV or hang whenever DEBUG_VERBOSE builds traced writes.

Use the end node's own forwarding chain (e = e->forwardTo), matching
the production code in the same loop body and in Phase 3.
2026-08-30 20:17:22 -04:00
andrew ec1b476977 Merge pull request 'Fix NEON type mismatch in conflictMask16 for gcc/aarch64 + USE_64_BIT=1' (#81) from weaselbot/conflict-set:weaselbot/issue-80 into main
Reviewed-on: http://git.weaselab.dev/weaselab/conflict-set/pulls/81
2026-08-18 17:39:48 +00:00
weaselbot 8c55e835c4 Fix NEON type mismatch in conflictMask16 for gcc/aarch64 + USE_64_BIT=1
vcgtq_s64 returns uint64x2_t but vmovn_s64 requires int64x2_t. clang accepts
the implicit conversion via lax vector conversions, but gcc does not. Add an
explicit vreinterpretq_s64_u64 so the code compiles with gcc on aarch64 when
USE_64_BIT=1. This path is otherwise untested in CI (aarch64 matrices use
USE_64_BIT=0; the 64-bit-versions matrix uses amd64 AVX compare16).

Closes #80
2026-08-16 21:16:23 -04:00
andrew af76c4d623 Merge pull request 'Fix hash_table setOldestVersion infinite loop on empty map' (#79) from weaselbot/conflict-set:weaselbot/issue-78 into main
Reviewed-on: http://git.weaselab.dev/weaselab/conflict-set/pulls/79
2026-08-17 00:25:27 +00:00
2 changed files with 27 additions and 3 deletions
+4 -3
View File
@@ -2135,7 +2135,8 @@ inline uint8x16_t conflictMask16(const InternalVersionT *vs,
int32x2_t r32[8]; int32x2_t r32[8];
const auto *vsp = reinterpret_cast<const int64_t *>(vs); const auto *vsp = reinterpret_cast<const int64_t *>(vs);
for (int j = 0; j < 8; ++j) { for (int j = 0; j < 8; ++j) {
r32[j] = vmovn_s64(vcgtq_s64(vld1q_s64(vsp + 2 * j), rvVec)); r32[j] = vmovn_s64(
vreinterpretq_s64_u64(vcgtq_s64(vld1q_s64(vsp + 2 * j), rvVec)));
} }
uint32x4_t w4[4]; uint32x4_t w4[4];
for (int k = 0; k < 4; ++k) { for (int k = 0; k < 4; ++k) {
@@ -4980,7 +4981,7 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
} }
if (e != nullptr) { if (e != nullptr) {
while (e->releaseDeferred) { while (e->releaseDeferred) {
e = b->forwardTo; e = e->forwardTo;
} }
} }
fprintf(stderr, "search path: %s, begin: %s\n", fprintf(stderr, "search path: %s, begin: %s\n",
@@ -5090,7 +5091,7 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
if (oldestExtantVersion < writeVersion - kMaxCorrectVersionWindow) if (oldestExtantVersion < writeVersion - kMaxCorrectVersionWindow)
[[unlikely]] { [[unlikely]] {
if (writeVersion > newestVersionFullPrecision + kNominalVersionWindow) { if (writeVersion - kNominalVersionWindow > newestVersionFullPrecision) {
eraseTree(rootParent->children[0], &writeContext); eraseTree(rootParent->children[0], &writeContext);
init(writeVersion - kNominalVersionWindow); init(writeVersion - kNominalVersionWindow);
} }
+23
View File
@@ -2,6 +2,7 @@
#include <cassert> #include <cassert>
#include <cstdio> #include <cstdio>
#include <limits>
using namespace weaselab; using namespace weaselab;
@@ -23,6 +24,28 @@ int main(void) {
int64_t bytes = cs.getBytes(); int64_t bytes = cs.getBytes();
assert(bytes > 0); assert(bytes > 0);
// Regression: a write just below INT64_MAX must not overflow the nominal
// version window comparison and erase entries that reads still observe.
// Versions are only required to be >= 0, so this is valid input.
{
const int64_t imax = std::numeric_limits<int64_t>::max();
const int64_t v0 = imax - int64_t(1000000000);
ConflictSet nearMax(0);
ConflictSet::WriteRange w2;
w2.begin.p = (const uint8_t *)"key";
w2.begin.len = 3;
w2.end.len = 0;
nearMax.addWrites(&w2, 1, v0);
nearMax.addWrites(&w2, 0, imax);
ConflictSet::ReadRange r2;
r2.begin.p = (const uint8_t *)"key";
r2.begin.len = 3;
r2.end.len = 0;
r2.readVersion = v0 - 1;
nearMax.check(&r2, &result, 1);
assert(result == ConflictSet::Conflict);
}
ConflictSet::MetricsV1 *metrics; ConflictSet::MetricsV1 *metrics;
int metricsCount; int metricsCount;
cs.getMetricsV1(&metrics, &metricsCount); cs.getMetricsV1(&metrics, &metricsCount);