main
16
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
e02dd41f74 |
Address review: drop unused bti c landing pads, fix rationale comment
CI / pre-commit (pull_request) Successful in 1m59s
CI / release (arm64, , ubuntu-latest-arm64) (pull_request) Successful in 2m14s
CI / test (-DCMAKE_BUILD_TYPE=Debug -DMSAN_TOOLCHAIN_PATH=/opt/msan, debug) (pull_request) Successful in 3m50s
CI / test (-DCMAKE_CXX_FLAGS=-DUSE_64_BIT=1, 64-bit-versions) (pull_request) Successful in 3m17s
CI / test (-DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++, gcc) (pull_request) Successful in 3m14s
CI / test (-DUSE_SIMD_FALLBACK=ON, simd-fallback) (pull_request) Successful in 3m16s
CI / release (amd64, -DMSAN_TOOLCHAIN_PATH=/opt/msan, ubuntu-latest-amd64) (pull_request) Successful in 5m33s
CI / coverage (pull_request) Successful in 3m44s
|
||
|
|
d040ed63a4 |
Move aarch64 Node16 SIMD index loads into assembly
CI / release (arm64, , ubuntu-latest-arm64) (pull_request) Successful in 2m18s
CI / pre-commit (pull_request) Successful in 1m58s
CI / test (-DCMAKE_BUILD_TYPE=Debug -DMSAN_TOOLCHAIN_PATH=/opt/msan, debug) (pull_request) Successful in 3m47s
CI / test (-DCMAKE_CXX_FLAGS=-DUSE_64_BIT=1, 64-bit-versions) (pull_request) Successful in 3m16s
CI / test (-DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++, gcc) (pull_request) Successful in 3m12s
CI / test (-DUSE_SIMD_FALLBACK=ON, simd-fallback) (pull_request) Successful in 3m17s
CI / release (amd64, -DMSAN_TOOLCHAIN_PATH=/opt/msan, ubuntu-latest-amd64) (pull_request) Successful in 5m32s
CI / coverage (pull_request) Successful in 3m42s
The aarch64 NEON paths in getNodeIndex/getNodeIndexExists,
getChildGeq(Node16*), scan16, and checkMaxBetweenExclusiveImpl<Node16>
loaded the full 16-element Node16::index array (and the Node48
reverseIndex array via scan16) with NEON intrinsics, then masked the
result down to [0, numChildren). Only the in-use slots are initialized;
the unused bytes are indeterminate, so the wide loads were undefined
behavior in C++ (per [basic.indet]) even though the trailing lanes were
discarded. MSan reports this on x86-64; on aarch64 it is the same UB but
MSan's imprecise modeling doesn't flag it at -O0, so there is no red->green
test.
Mirror the existing x86-64 fix (commit
|
||
|
|
6b1f597d77 |
Fix ARM NEON scan helpers truncating 64-bit versions
CI / release (arm64, , ubuntu-latest-arm64) (pull_request) Successful in 2m13s
CI / pre-commit (pull_request) Successful in 2m2s
CI / test (-DCMAKE_BUILD_TYPE=Debug -DMSAN_TOOLCHAIN_PATH=/opt/msan, debug) (pull_request) Successful in 3m47s
CI / test (-DCMAKE_CXX_FLAGS=-DUSE_64_BIT=1, 64-bit-versions) (pull_request) Successful in 3m15s
CI / test (-DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++, gcc) (pull_request) Successful in 3m12s
CI / test (-DUSE_SIMD_FALLBACK=ON, simd-fallback) (pull_request) Successful in 3m17s
CI / release (amd64, -DMSAN_TOOLCHAIN_PATH=/opt/msan, ubuntu-latest-amd64) (pull_request) Successful in 5m34s
CI / coverage (pull_request) Successful in 3m43s
When USE_64_BIT=1, InternalVersionT stores an int64_t, but the aarch64 NEON fast paths in scan16 (both the indexed Node48 variant and the Node256 variant) and checkMaxBetweenExclusiveImpl(Node16) only copied the low 32 bits of each version into uint32x4_t lanes and compared against a truncated 32-bit readVersion. This produced wrong conflict/commit decisions once any version exceeded 2^32 - 1. Extract the per-version "greater than readVersion" computation into a shared conflictMask16 helper. For 64-bit versions it uses vcgtq_s64 on the full int64 values and narrows the resulting 16-byte mask into the same nibble-packed layout the callers already consume. The 32-bit path is preserved unchanged. Closes #69 |
||
|
|
70630649d9 |
ci: run MSan on arm64
CI / test (arm64, -DCMAKE_BUILD_TYPE=Debug -DMSAN_TOOLCHAIN_PATH=/opt/msan, 22, https://minio.weaselab.dev/public/aarch64/msan-toolchain-22.1.8.tar.zst, debug-arm64, ubuntu-latest-arm64) (pull_request) Successful in 3m53s
CI / release (arm64, , ubuntu-latest-arm64) (pull_request) Successful in 2m36s
CI / pre-commit (pull_request) Successful in 2m0s
CI / test (amd64, -DCMAKE_BUILD_TYPE=Debug -DMSAN_TOOLCHAIN_PATH=/opt/msan, 21, https://minio.weaselab.dev/public/x86_64/msan-toolchain-21.1.8.tar.zst, debug, ubuntu-latest-amd64) (pull_request) Successful in 3m50s
CI / test (amd64, -DCMAKE_CXX_FLAGS=-DUSE_64_BIT=1, 21, , 64-bit-versions, ubuntu-latest-amd64) (pull_request) Successful in 3m18s
CI / test (amd64, -DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++, 21, , gcc, ubuntu-latest-amd64) (pull_request) Successful in 3m13s
CI / test (amd64, -DUSE_SIMD_FALLBACK=ON, 21, , simd-fallback, ubuntu-latest-amd64) (pull_request) Successful in 3m18s
CI / release (amd64, -DMSAN_TOOLCHAIN_PATH=/opt/msan, ubuntu-latest-amd64) (pull_request) Successful in 5m35s
CI / coverage (pull_request) Successful in 3m43s
Add a debug-arm64 matrix entry to the test job that runs the MSan fuzz tests on an arm64 runner. The arm instrumented libc++ toolchain (22.1.8) is built against LLVM 22, so this entry installs clang-22 from apt.llvm.org while the existing amd64 entries keep clang-21. Generalize the test job matrix with runner/arch/llvm_version/msan_url fields so the runner, apt cache key, LLVM toolchain version, and MSan toolchain download URL are selected per matrix entry. Closes #73 |
||
|
|
0b12a037c4 |
Correct rationale in simd_x86_64.S comment
CI / release (arm64, , ubuntu-latest-arm64) (pull_request) Successful in 2m14s
CI / pre-commit (pull_request) Successful in 1m59s
CI / test (-DCMAKE_BUILD_TYPE=Debug -DMSAN_TOOLCHAIN_PATH=/opt/msan, debug) (pull_request) Successful in 3m47s
CI / test (-DCMAKE_CXX_FLAGS=-DUSE_64_BIT=1, 64-bit-versions) (pull_request) Successful in 3m14s
CI / test (-DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++, gcc) (pull_request) Successful in 3m13s
CI / test (-DUSE_SIMD_FALLBACK=ON, simd-fallback) (pull_request) Successful in 3m17s
CI / release (amd64, -DMSAN_TOOLCHAIN_PATH=/opt/msan, ubuntu-latest-amd64) (pull_request) Successful in 5m27s
CI / coverage (pull_request) Successful in 3m42s
The SIMD operations are written in assembly because loading and operating on indeterminate values is UB in C++ but well-defined in assembly. msan not tracking the loads is a side effect, not the reason. Closes #71 |
||
|
|
2231c093df |
Fix null pointers passed to memcmp/memcpy in skip_list
CI / pre-commit (pull_request) Successful in 2m14s
CI / release (arm64, ubuntu-latest-arm64) (pull_request) Successful in 3m32s
CI / test (-DCMAKE_BUILD_TYPE=Debug, debug) (pull_request) Successful in 3m38s
CI / test (-DCMAKE_CXX_FLAGS=-DUSE_64_BIT=1, 64-bit-versions) (pull_request) Successful in 3m29s
CI / test (-DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++, gcc) (pull_request) Successful in 3m39s
CI / test (-DUSE_SIMD_FALLBACK=ON, simd-fallback) (pull_request) Successful in 3m30s
CI / release (amd64, ubuntu-latest-amd64) (pull_request) Successful in 5m1s
CI / coverage (pull_request) Successful in 3m38s
In addWrites, the sizing constructor std::vector<KeyInfo>(count * 2) was used instead of reserve(count * 2), leaving count*2 default- constructed (null/empty key) entries that were then compared via operator<, passing nullptr to memcmp. Switch to reserve so only real entries exist. Additionally guard the memcmp/memcpy calls in operator<, SkipList::less, and copyToArena against empty spans (where data() may be nullptr), which is reachable on the first setOldestVersion with an empty removal key. Closes #64 |
||
|
|
dee3a8f640 |
Return 0 instead of -1 from hash_table getBytes()
CI / release (arm64, ubuntu-latest-arm64) (pull_request) Successful in 3m25s
CI / pre-commit (pull_request) Successful in 3m40s
CI / test (-DCMAKE_BUILD_TYPE=Debug, debug) (pull_request) Successful in 5m8s
CI / test (-DCMAKE_CXX_FLAGS=-DUSE_64_BIT=1, 64-bit-versions) (pull_request) Successful in 4m50s
CI / test (-DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++, gcc) (pull_request) Successful in 4m57s
CI / test (-DUSE_SIMD_FALLBACK=ON, simd-fallback) (pull_request) Successful in 4m42s
CI / release (amd64, ubuntu-latest-amd64) (pull_request) Successful in 6m31s
CI / coverage (pull_request) Successful in 4m51s
The hash_table implementation cannot accurately track memory usage and was returning -1 from ConflictSet::getBytes() and ConflictSet_getBytes(). That violates the API contract that getBytes() returns a non-negative byte count. Change both entry points to return 0 and document in ConflictSet.h that implementations which do not track memory usage may return 0. Add a regression test in test_conflict_set.py that loads the hash_table implementation and verifies getBytes() is non-negative. Closes #62 |
||
|
|
4fcdc5d7e9 |
Fix RealDataBench subspan and version API contract violations
CI / release (arm64, ubuntu-latest-arm64) (pull_request) Successful in 3m33s
CI / pre-commit (pull_request) Successful in 2m4s
CI / test (-DCMAKE_BUILD_TYPE=Debug, debug) (pull_request) Successful in 3m38s
CI / test (-DCMAKE_CXX_FLAGS=-DUSE_64_BIT=1, 64-bit-versions) (pull_request) Successful in 3m29s
CI / test (-DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++, gcc) (pull_request) Successful in 3m38s
CI / test (-DUSE_SIMD_FALLBACK=ON, simd-fallback) (pull_request) Successful in 3m30s
CI / release (amd64, ubuntu-latest-amd64) (pull_request) Successful in 4m57s
CI / coverage (pull_request) Successful in 3m40s
- Use subspan count `line.size() - 2` instead of `line.size()` to avoid reading past the line bounds, and guard lines shorter than the two-byte prefix. - Clamp `readVersion` and `setOldestVersion` arguments to `0` so the conflict set never receives negative, non-monotonic versions during the warmup phase. Fixes #58. |
||
|
|
63f9a139da |
Fix undefined behavior on empty input in strinc() and prefixRange()
CI / pre-commit (pull_request) Successful in 2m5s
CI / release (arm64, ubuntu-latest-arm64) (pull_request) Successful in 3m29s
CI / test (-DCMAKE_BUILD_TYPE=Debug, debug) (pull_request) Successful in 3m34s
CI / test (-DCMAKE_CXX_FLAGS=-DUSE_64_BIT=1, 64-bit-versions) (pull_request) Successful in 3m28s
CI / test (-DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++, gcc) (pull_request) Successful in 3m38s
CI / test (-DUSE_SIMD_FALLBACK=ON, simd-fallback) (pull_request) Successful in 3m27s
CI / release (amd64, ubuntu-latest-amd64) (pull_request) Successful in 4m51s
CI / coverage (pull_request) Successful in 3m37s
strinc() in ConflictSet.cpp used std::string_view::size() (size_t) and subtracted 1 without first checking for an empty string. For the root node, getSearchPath() returns the empty string, so every debug correctness check underflowed size_t and relied on implementation-defined conversion to signed int. prefixRange() in Bench.cpp had the same loop shape. Although TrivialSpan::size() returns int, on an empty (or all-0xff) key the function then asserted and continued executing, allocating a zero-length buffer and writing before its start. Changes: - In strinc(), initialize index as signed int(str.size()) - 1 so the loop is skipped for empty input, and return ok=false cleanly. - In prefixRange(), initialize index the same way and call std::abort() after the assert so invalid input cannot fall through to an out-of-bounds write. - Replace C-style uint8_t casts with explicit static_casts. Closes #59 |
||
|
|
971deb477c |
Keep Python key buffers alive in WriteRange/ReadRange
CI / release (arm64, ubuntu-latest-arm64) (pull_request) Successful in 3m22s
CI / pre-commit (pull_request) Successful in 2m5s
CI / test (-DCMAKE_BUILD_TYPE=Debug, debug) (pull_request) Successful in 3m38s
CI / test (-DCMAKE_CXX_FLAGS=-DUSE_64_BIT=1, 64-bit-versions) (pull_request) Successful in 3m32s
CI / test (-DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++, gcc) (pull_request) Successful in 3m40s
CI / test (-DUSE_SIMD_FALLBACK=ON, simd-fallback) (pull_request) Successful in 3m28s
CI / release (amd64, ubuntu-latest-amd64) (pull_request) Successful in 4m53s
CI / coverage (pull_request) Successful in 3m40s
`write()` and `read()` created _Key objects from ephemeral ctypes arrays backed by local bytearray objects. Once the helpers returned, those local variables were freed, leaving the C library with dangling pointers when addWrites()/check() later read the keys. Store the backing bytearray on the returned WriteRange/ReadRange objects as private `_begin_buf` / `_end_buf` attributes. Python keeps them alive for the lifetime of the range object, so the C pointer is always valid. Closes #42 |
||
|
|
ff0722728a |
Include <sys/syscall.h> in ServerBench.cpp for SYS_perf_event_open
CI / release (arm64, ubuntu-latest-arm64) (pull_request) Successful in 3m23s
CI / pre-commit (pull_request) Successful in 2m6s
CI / test (-DCMAKE_BUILD_TYPE=Debug, debug) (pull_request) Successful in 3m36s
CI / test (-DCMAKE_CXX_FLAGS=-DUSE_64_BIT=1, 64-bit-versions) (pull_request) Successful in 3m28s
CI / test (-DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++, gcc) (pull_request) Successful in 3m37s
CI / test (-DUSE_SIMD_FALLBACK=ON, simd-fallback) (pull_request) Successful in 3m27s
CI / release (amd64, ubuntu-latest-amd64) (pull_request) Successful in 4m51s
CI / coverage (pull_request) Successful in 3m38s
`ServerBench.cpp` calls `syscall(SYS_perf_event_open, ...)` but did not include `<sys/syscall.h>`, relying on `<unistd.h>` to transitively provide the `SYS_*` constants. On toolchains where that does not happen, the build fails with `SYS_perf_event_open` not declared. Add the missing include so `server_bench` compiles on any platform that provides `perf_event_open`. Resolves weaselab/conflict-set#41. |
||
|
|
60881419b8 |
Assert return values in test_update_zero_should_commit / conflict
CI / release (arm64, ubuntu-latest-arm64) (pull_request) Successful in 3m22s
CI / pre-commit (pull_request) Successful in 2m13s
CI / test (-DCMAKE_BUILD_TYPE=Debug, debug) (pull_request) Successful in 3m36s
CI / test (-DCMAKE_CXX_FLAGS=-DUSE_64_BIT=1, 64-bit-versions) (pull_request) Successful in 3m28s
CI / test (-DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++, gcc) (pull_request) Successful in 3m37s
CI / test (-DUSE_SIMD_FALLBACK=ON, simd-fallback) (pull_request) Successful in 3m27s
CI / release (amd64, ubuntu-latest-amd64) (pull_request) Successful in 4m53s
CI / coverage (pull_request) Successful in 3m37s
Add the missing [Result.COMMIT] / [Result.CONFLICT] assertions so that the two regression tests actually verify the expected conflict-set outcome, not just that the calls do not crash. |
||
|
|
4515af3662 |
CMake: add target-level dependencies for conflict-set-object
CI / release (arm64, ubuntu-latest-arm64) (pull_request) Successful in 3m25s
CI / pre-commit (pull_request) Successful in 2m5s
CI / test (-DCMAKE_BUILD_TYPE=Debug, debug) (pull_request) Successful in 3m36s
CI / test (-DCMAKE_CXX_FLAGS=-DUSE_64_BIT=1, 64-bit-versions) (pull_request) Successful in 3m28s
CI / test (-DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++, gcc) (pull_request) Successful in 3m35s
CI / test (-DUSE_SIMD_FALLBACK=ON, simd-fallback) (pull_request) Successful in 3m28s
CI / release (amd64, ubuntu-latest-amd64) (pull_request) Successful in 4m51s
CI / coverage (pull_request) Successful in 3m36s
The custom command that links conflict-set.o depends on $<TARGET_OBJECTS:conflict-set-object>, but that generator expression does not create a target-level dependency. With the Unix Makefiles generator, parallel builds can start building the consuming libraries before the object library's build rule is available, producing: gmake[3]: *** No rule to make target 'CMakeFiles/conflict-set-object.dir/ConflictSet.cpp.o', needed by 'conflict-set.o'. Stop. Add add_dependencies() so that conflict-set and conflict-set-static cannot build until conflict-set-object has produced its object files. Closes #47 |
||
|
|
4dc5f7f75c |
Fix self-move-assignment and leak in ConflictSet move-assignment
CI / release (arm64, ubuntu-latest-arm64) (pull_request) Successful in 3m23s
CI / pre-commit (pull_request) Successful in 2m5s
CI / test (-DCMAKE_BUILD_TYPE=Debug, debug) (pull_request) Successful in 3m36s
CI / test (-DCMAKE_CXX_FLAGS=-DUSE_64_BIT=1, 64-bit-versions) (pull_request) Successful in 3m28s
CI / test (-DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++, gcc) (pull_request) Successful in 3m38s
CI / test (-DUSE_SIMD_FALLBACK=ON, simd-fallback) (pull_request) Successful in 3m28s
CI / release (amd64, ubuntu-latest-amd64) (pull_request) Successful in 4m53s
CI / coverage (pull_request) Successful in 3m38s
The user-declared move-assignment operator overwrote `impl` without first destroying the existing implementation object, leaking all memory and resources owned by the left-hand side. Self-move-assignment also set `impl` to nullptr, leaving the object invalid and leaking the old state. Fix all three implementations (ConflictSet.cpp, SkipList.cpp, HashTable.cpp) to guard against self-assignment and to destroy/free the old `impl` before taking ownership of `other.impl`. |
||
|
|
7eaac2a184 |
Make ConflictSet non-copyable in C++98/C++03
CI / release (arm64, ubuntu-latest-arm64) (pull_request) Successful in 3m20s
CI / pre-commit (pull_request) Successful in 2m5s
CI / test (-DCMAKE_BUILD_TYPE=Debug, debug) (pull_request) Successful in 3m37s
CI / test (-DCMAKE_CXX_FLAGS=-DUSE_64_BIT=1, 64-bit-versions) (pull_request) Successful in 3m27s
CI / test (-DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++, gcc) (pull_request) Successful in 3m37s
CI / test (-DUSE_SIMD_FALLBACK=ON, simd-fallback) (pull_request) Successful in 3m27s
CI / release (amd64, ubuntu-latest-amd64) (pull_request) Successful in 4m52s
CI / coverage (pull_request) Successful in 3m37s
`ConflictSet(const ConflictSet&)` and `operator=(const ConflictSet&)` were only deleted for C++11 and later. In C++98/C++03 the compiler implicitly generated public copy operations, so copying a ConflictSet shared the opaque `Impl*` and caused a double-free on destruction. Declare both operations private and leave them undefined when `__cplusplus <= 199711L`, matching the standard pre-C++11 idiom for move-only types. Guard the declarations with `defined(__cplusplus)` so they are not exposed to C90 compilation units. Closes #48 |
||
|
|
742d920aa1 |
Set restype=None for void-returning C functions in conflict_set.py
CI / release (arm64, ubuntu-latest-arm64) (pull_request) Successful in 3m28s
CI / pre-commit (pull_request) Successful in 2m14s
CI / test (-DCMAKE_BUILD_TYPE=Debug, debug) (pull_request) Successful in 3m39s
CI / test (-DCMAKE_CXX_FLAGS=-DUSE_64_BIT=1, 64-bit-versions) (pull_request) Successful in 3m33s
CI / test (-DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++, gcc) (pull_request) Successful in 3m44s
CI / test (-DUSE_SIMD_FALLBACK=ON, simd-fallback) (pull_request) Successful in 3m32s
CI / release (amd64, ubuntu-latest-amd64) (pull_request) Failing after 4m25s
CI / coverage (pull_request) Failing after 3m20s
ConflictSet_check, ConflictSet_addWrites, ConflictSet_setOldestVersion, and ConflictSet_destroy return void in C, but the Python wrapper left their ctypes restype at the default c_int. Set restype = None for each to match the C API contract and avoid undefined behavior from reading the return register of void functions. |