31 Commits

Author SHA1 Message Date
13d447c9fe Use version.txt instead of version.tex
All checks were successful
Tests / Clang total: 1130, passed: 1130
Clang |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |0|0|0|0|:clap:
Tests / SIMD fallback total: 1130, passed: 1130
Tests / Release [gcc] total: 1130, passed: 1130
GNU C Compiler (gcc) |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |0|0|0|0|:clap:
Tests / Release [gcc,aarch64] total: 844, passed: 844
Tests / Coverage total: 848, passed: 848
weaselab/conflict-set/pipeline/head This commit looks good
latexmk seemed to have some trouble with it being a tex file
2024-06-12 13:47:16 -07:00
da7523c5cf Add version to paper
Some checks failed
Tests / Clang total: 1130, passed: 1130
Clang |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |0|0|0|0|:clap:
Tests / SIMD fallback total: 1130, passed: 1130
Tests / Release [gcc] total: 1130, passed: 1130
GNU C Compiler (gcc) |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |0|0|0|0|:clap:
weaselab/conflict-set/pipeline/head There was a failure building this commit
2024-06-12 13:34:35 -07:00
a074bc6f72 include(CTest) before BUILD_TESTING
All checks were successful
Tests / Clang total: 1130, passed: 1130
Clang |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |0|0|0|0|:clap:
Tests / SIMD fallback total: 1130, passed: 1130
Tests / Release [gcc] total: 1130, passed: 1130
GNU C Compiler (gcc) |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |0|0|0|0|:clap:
Tests / Release [gcc,aarch64] total: 844, passed: 844
Tests / Coverage total: 848, passed: 848
weaselab/conflict-set/pipeline/head This commit looks good
2024-06-11 16:21:38 -07:00
1553a44986 Make possible to use from FetchContent
Some checks failed
Tests / Clang total: 0
Clang |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |0|0|0|0|:clap:
Tests / SIMD fallback total: 0
Tests / Release [gcc] total: 0
GNU C Compiler (gcc) |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |0|0|0|0|:clap:
Tests / Release [gcc,aarch64] total: 0
Tests / Coverage total: 0
weaselab/conflict-set/pipeline/head There was a failure building this commit
2024-06-11 16:12:35 -07:00
859ac352e6 Bump version
All checks were successful
Tests / Clang total: 1130, passed: 1130
Clang |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |0|0|0|0|:clap:
Tests / SIMD fallback total: 1130, passed: 1130
Tests / Release [gcc] total: 1130, passed: 1130
GNU C Compiler (gcc) |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |0|0|0|0|:clap:
Tests / Release [gcc,aarch64] total: 844, passed: 844
Tests / Coverage total: 848, passed: 848
weaselab/conflict-set/pipeline/head This commit looks good
2024-06-11 13:13:19 -07:00
2eb461b8ea Fix build for llvm 18
All checks were successful
Tests / Clang total: 1130, passed: 1130
Clang |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |0|0|0|0|:clap:
Tests / SIMD fallback total: 1130, passed: 1130
Tests / Release [gcc] total: 1130, passed: 1130
GNU C Compiler (gcc) |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |0|0|0|0|:clap:
Tests / Release [gcc,aarch64] total: 844, passed: 844
Tests / Coverage total: 848, passed: 848
weaselab/conflict-set/pipeline/head This commit looks good
2024-06-11 11:38:55 -07:00
e2e92f4ef5 Address some feedback on paper
All checks were successful
Tests / Clang total: 1130, passed: 1130
Clang |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |0|0|0|0|:clap:
Tests / SIMD fallback total: 1130, passed: 1130
Tests / Release [gcc] total: 1130, passed: 1130
GNU C Compiler (gcc) |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |0|0|0|0|:clap:
Tests / Release [gcc,aarch64] total: 844, passed: 844
Tests / Coverage total: 848, passed: 848
weaselab/conflict-set/pipeline/head This commit looks good
2024-05-06 14:30:49 -07:00
f6f25cfcce Paper tweaks
All checks were successful
Tests / Clang total: 1130, passed: 1130
Clang |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |0|0|0|0|:clap:
Tests / SIMD fallback total: 1130, passed: 1130
Tests / Release [gcc] total: 1130, passed: 1130
GNU C Compiler (gcc) |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |0|0|0|0|:clap:
Tests / Release [gcc,aarch64] total: 844, passed: 844
Tests / Coverage total: 848, passed: 848
weaselab/conflict-set/pipeline/head This commit looks good
2024-04-22 15:26:00 -07:00
c13dc88ff4 Update corpus 2024-04-22 15:24:55 -07:00
aa5dbb2887 Explicitly allow writeVersion to be non-decreasing
Some checks failed
Tests / Clang total: 1162, passed: 1162
Clang |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |0|0|0|0|:clap:
Tests / SIMD fallback total: 1162, passed: 1162
Tests / Release [gcc] total: 1162, passed: 1162
GNU C Compiler (gcc) |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |0|0|0|0|:clap:
Tests / Release [gcc,aarch64] total: 868, passed: 868
Tests / Coverage total: 872, passed: 872
weaselab/conflict-set/pipeline/head There was a failure building this commit
Instead of strictly increasing.
2024-04-22 14:15:44 -07:00
ea76e04cda Fix weird-looking url in ubsan reference
Also use the exact html title
2024-04-19 15:19:39 -07:00
452007e079 Change paper title to emphasize usefulness outside fdb
All checks were successful
Tests / Clang total: 1162, passed: 1162
Clang |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |0|0|0|0|:clap:
Tests / SIMD fallback total: 1162, passed: 1162
Tests / Release [gcc] total: 1162, passed: 1162
GNU C Compiler (gcc) |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |0|0|0|0|:clap:
Tests / Release [gcc,aarch64] total: 868, passed: 868
Tests / Coverage total: 872, passed: 872
weaselab/conflict-set/pipeline/head This commit looks good
2024-04-19 14:53:31 -07:00
37c75f747b Draft Testing section
All checks were successful
Tests / Clang total: 1162, passed: 1162
Clang |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |0|0|0|0|:clap:
Tests / SIMD fallback total: 1162, passed: 1162
Tests / Release [gcc] total: 1162, passed: 1162
GNU C Compiler (gcc) |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |0|0|0|0|:clap:
Tests / Release [gcc,aarch64] total: 868, passed: 868
Tests / Coverage total: 872, passed: 872
weaselab/conflict-set/pipeline/head This commit looks good
2024-04-19 14:26:47 -07:00
c96d682483 Fix memory error when SHOW_MEMORY = 1
All checks were successful
Tests / Clang total: 1162, passed: 1162
Clang |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |0|0|0|0|:clap:
Tests / SIMD fallback total: 1162, passed: 1162
Tests / Release [gcc] total: 1162, passed: 1162
GNU C Compiler (gcc) |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |0|0|0|0|:clap:
Tests / Release [gcc,aarch64] total: 868, passed: 868
Tests / Coverage total: 872, passed: 872
weaselab/conflict-set/pipeline/head This commit looks good
2024-04-19 11:28:49 -07:00
6e63fd5126 Add internal entry points, with test coverage
Closes #25
2024-04-19 11:23:25 -07:00
f2678de811 Preserve version in clearConflictSet in fdb patch
Closes #24
2024-04-19 11:00:43 -07:00
4d7ad075b2 Bump version
All checks were successful
Tests / Clang total: 1162, passed: 1162
Clang |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |0|0|0|0|:clap:
Tests / SIMD fallback total: 1162, passed: 1162
Tests / Release [gcc] total: 1162, passed: 1162
GNU C Compiler (gcc) |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |0|0|0|0|:clap:
Tests / Release [gcc,aarch64] total: 868, passed: 868
Tests / Coverage total: 872, passed: 872
weaselab/conflict-set/pipeline/head This commit looks good
2024-04-18 14:32:51 -07:00
d2e1863593 Account for every uncovered line in the implementation
All checks were successful
Tests / Clang total: 1162, passed: 1162
Clang |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |0|0|0|0|:clap:
Tests / SIMD fallback total: 1162, passed: 1162
Tests / Release [gcc] total: 1162, passed: 1162
GNU C Compiler (gcc) |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |0|0|0|0|:clap:
Tests / Release [gcc,aarch64] total: 868, passed: 868
Tests / Coverage total: 872, passed: 872
weaselab/conflict-set/pipeline/head This commit looks good
Closes #23
2024-04-18 12:43:48 -07:00
bf91bca16d Include long common prefix in fuzz test 2024-04-18 12:43:48 -07:00
08ed17f47b Fail jenkins build if not 100% line coverage
Some checks failed
Tests / Clang total: 1093, passed: 1093
Clang |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |0|0|0|0|:clap:
Tests / SIMD fallback total: 1093, passed: 1093
Tests / Release [gcc] total: 1093, passed: 1093
GNU C Compiler (gcc) |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |0|0|0|0|:clap:
Tests / Release [gcc,aarch64] total: 817, passed: 817
Tests / Coverage total: 820, passed: 820
weaselab/conflict-set/pipeline/head There was a failure building this commit
2024-04-18 12:43:48 -07:00
76a45f16ad Exercise freelist size limiting code
All checks were successful
Tests / Clang total: 1093, passed: 1093
Clang |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |0|0|0|0|:clap:
Tests / SIMD fallback total: 1093, passed: 1093
Tests / Release [gcc] total: 1093, passed: 1093
GNU C Compiler (gcc) |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |0|0|0|0|:clap:
Tests / Release [gcc,aarch64] total: 817, passed: 817
Tests / Coverage total: 820, passed: 820
weaselab/conflict-set/pipeline/head This commit looks good
2024-04-17 18:27:01 -07:00
c15d296432 Exercise copyChildrenAndKeyFrom for Node{48,256} to itself
All checks were successful
Tests / Clang total: 1092, passed: 1092
Clang |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |0|0|0|0|:clap:
Tests / SIMD fallback total: 1092, passed: 1092
Tests / Release [gcc] total: 1092, passed: 1092
GNU C Compiler (gcc) |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |0|0|0|0|:clap:
Tests / Release [gcc,aarch64] total: 817, passed: 817
Tests / Coverage total: 819, passed: 819
weaselab/conflict-set/pipeline/head This commit looks good
2024-04-17 17:13:22 -07:00
64a98c529c Fix conflict_set.py bug, and add full inner words test
All checks were successful
Tests / Clang total: 1091, passed: 1091
Clang |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |0|0|0|0|:clap:
Tests / SIMD fallback total: 1091, passed: 1091
Tests / Release [gcc] total: 1091, passed: 1091
GNU C Compiler (gcc) |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |0|0|0|0|:clap:
Tests / Release [gcc,aarch64] total: 817, passed: 817
Tests / Coverage total: 818, passed: 818
weaselab/conflict-set/pipeline/head This commit looks good
Apparently all bytes were 0
2024-04-17 14:11:26 -07:00
ed1388ed21 Disable script tests when cross compiling
All checks were successful
Tests / Clang total: 1090, passed: 1090
Clang |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |0|0|0|0|:clap:
Tests / SIMD fallback total: 1090, passed: 1090
Tests / Release [gcc] total: 1090, passed: 1090
GNU C Compiler (gcc) |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |0|0|0|0|:clap:
Tests / Release [gcc,aarch64] total: 817, passed: 817
Tests / Coverage total: 817, passed: 817
weaselab/conflict-set/pipeline/head This commit looks good
2024-04-17 12:25:16 -07:00
309d315956 Add DebugConflictSet, which asserts using skip list as a reference
Some checks failed
Tests / Clang total: 1090, passed: 1090
Clang |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |0|0|0|0|:clap:
Tests / SIMD fallback total: 1090, passed: 1090
Tests / Release [gcc] total: 1090, passed: 1090
GNU C Compiler (gcc) |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |0|0|0|0|:clap:
Tests / Release [gcc,aarch64] total: 818, failed: 1, passed: 817
Tests / Coverage total: 817, passed: 817
weaselab/conflict-set/pipeline/head There was a failure building this commit
CC #23
2024-04-17 12:08:39 -07:00
eab2e46a56 Mention that we modified SkipList.cpp 2024-04-17 12:08:33 -07:00
85db1a8786 Allow to choose implementation in python wrapper
And fix a few minor bugs to make the python tests pass for skip_list.

CC #23
2024-04-17 12:08:28 -07:00
717f9d6829 Remove ScriptTest.cpp, replace with test_conflict_set.py
CC #23
2024-04-17 12:08:08 -07:00
fd93300ce8 Tweaks and add more planned sections
All checks were successful
Tests / Clang total: 1096, passed: 1096
Clang |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |0|0|0|0|:clap:
Tests / SIMD fallback total: 1096, passed: 1096
Tests / Release [gcc] total: 1096, passed: 1096
GNU C Compiler (gcc) |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |0|0|0|0|:clap:
Tests / Release [gcc,aarch64] total: 824, passed: 824
Tests / Coverage total: 823, passed: 823
weaselab/conflict-set/pipeline/head This commit looks good
2024-04-16 17:36:29 -07:00
b7e16b31ff Fill out empty subsections
All checks were successful
Tests / Clang total: 1096, passed: 1096
Clang |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |0|0|0|0|:clap:
Tests / SIMD fallback total: 1096, passed: 1096
Tests / Release [gcc] total: 1096, passed: 1096
GNU C Compiler (gcc) |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |0|0|0|0|:clap:
Tests / Release [gcc,aarch64] total: 824, passed: 824
Tests / Coverage total: 823, passed: 823
weaselab/conflict-set/pipeline/head This commit looks good
2024-04-16 12:57:57 -07:00
a324d31518 Second pass at "Checking range reads" 2024-04-16 12:14:04 -07:00
572 changed files with 683 additions and 1228 deletions

View File

@@ -1,14 +1,16 @@
cmake_minimum_required(VERSION 3.18) cmake_minimum_required(VERSION 3.18)
project( project(
conflict-set conflict-set
VERSION 0.0.4 VERSION 0.0.6
DESCRIPTION DESCRIPTION
"A data structure for optimistic concurrency control on ranges of bitwise-lexicographically-ordered keys." "A data structure for optimistic concurrency control on ranges of bitwise-lexicographically-ordered keys."
HOMEPAGE_URL "https://git.weaselab.dev/weaselab/conflict-set" HOMEPAGE_URL "https://git.weaselab.dev/weaselab/conflict-set"
LANGUAGES C CXX) LANGUAGES C CXX)
set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD 20)
file(WRITE ${CMAKE_BINARY_DIR}/version.txt ${PROJECT_VERSION}) file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/version.txt ${PROJECT_VERSION})
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/version.txt.in
${CMAKE_CURRENT_SOURCE_DIR}/paper/version.txt)
include(CMakePushCheckState) include(CMakePushCheckState)
include(CheckCXXCompilerFlag) include(CheckCXXCompilerFlag)
@@ -47,7 +49,8 @@ if(HAS_FULL_RELRO)
endif() endif()
cmake_pop_check_state() cmake_pop_check_state()
set(version_script_flags LINKER:--version-script=${CMAKE_SOURCE_DIR}/linker.map) set(version_script_flags
LINKER:--version-script=${CMAKE_CURRENT_SOURCE_DIR}/linker.map)
cmake_push_check_state() cmake_push_check_state()
list(APPEND CMAKE_REQUIRED_LINK_OPTIONS ${version_script_flags}) list(APPEND CMAKE_REQUIRED_LINK_OPTIONS ${version_script_flags})
check_cxx_source_compiles("int main(){}" HAS_VERSION_SCRIPT FAIL_REGEX check_cxx_source_compiles("int main(){}" HAS_VERSION_SCRIPT FAIL_REGEX
@@ -59,7 +62,7 @@ option(USE_SIMD_FALLBACK
# This is encouraged according to # This is encouraged according to
# https://valgrind.org/docs/manual/manual-core-adv.html#manual-core-adv.clientreq # https://valgrind.org/docs/manual/manual-core-adv.html#manual-core-adv.clientreq
include_directories(SYSTEM ${CMAKE_SOURCE_DIR}/third_party/valgrind) include_directories(SYSTEM ${CMAKE_CURRENT_SOURCE_DIR}/third_party/valgrind)
add_compile_options($<$<COMPILE_LANGUAGE:CXX>:-Wno-invalid-offsetof>) add_compile_options($<$<COMPILE_LANGUAGE:CXX>:-Wno-invalid-offsetof>)
@@ -106,19 +109,20 @@ add_library(${PROJECT_NAME}-object OBJECT ConflictSet.cpp)
target_compile_options(${PROJECT_NAME}-object PRIVATE -fno-exceptions target_compile_options(${PROJECT_NAME}-object PRIVATE -fno-exceptions
-fvisibility=hidden) -fvisibility=hidden)
target_include_directories(${PROJECT_NAME}-object target_include_directories(${PROJECT_NAME}-object
PRIVATE ${CMAKE_SOURCE_DIR}/include) PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include)
add_library(${PROJECT_NAME} SHARED $<TARGET_OBJECTS:${PROJECT_NAME}-object>) add_library(${PROJECT_NAME} SHARED $<TARGET_OBJECTS:${PROJECT_NAME}-object>)
set_target_properties( set_target_properties(
${PROJECT_NAME} PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${PROJECT_NAME} PROPERTIES LIBRARY_OUTPUT_DIRECTORY
"${CMAKE_BINARY_DIR}/radix_tree") "${CMAKE_CURRENT_BINARY_DIR}/radix_tree")
if(NOT CMAKE_BUILD_TYPE STREQUAL Debug) if(NOT CMAKE_BUILD_TYPE STREQUAL Debug)
set_target_properties(${PROJECT_NAME} PROPERTIES LINKER_LANGUAGE C) set_target_properties(${PROJECT_NAME} PROPERTIES LINKER_LANGUAGE C)
endif() endif()
if(HAS_VERSION_SCRIPT) if(HAS_VERSION_SCRIPT)
target_link_options(${PROJECT_NAME} PRIVATE target_link_options(
LINKER:--version-script=${CMAKE_SOURCE_DIR}/linker.map) ${PROJECT_NAME} PRIVATE
LINKER:--version-script=${CMAKE_CURRENT_SOURCE_DIR}/linker.map)
endif() endif()
add_library(${PROJECT_NAME}-static STATIC add_library(${PROJECT_NAME}-static STATIC
@@ -131,7 +135,7 @@ if(APPLE)
add_custom_command( add_custom_command(
TARGET ${PROJECT_NAME}-static TARGET ${PROJECT_NAME}-static
PRE_LINK PRE_LINK
COMMAND ${CMAKE_SOURCE_DIR}/privatize_symbols_macos.sh COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/privatize_symbols_macos.sh
$<TARGET_OBJECTS:${PROJECT_NAME}-object>) $<TARGET_OBJECTS:${PROJECT_NAME}-object>)
else() else()
add_custom_command( add_custom_command(
@@ -139,21 +143,22 @@ else()
POST_BUILD POST_BUILD
COMMAND COMMAND
${CMAKE_OBJCOPY} ${CMAKE_OBJCOPY}
--keep-global-symbols=${CMAKE_SOURCE_DIR}/symbol-exports.txt --keep-global-symbols=${CMAKE_CURRENT_SOURCE_DIR}/symbol-exports.txt
$<TARGET_FILE:${PROJECT_NAME}-static> || echo $<TARGET_FILE:${PROJECT_NAME}-static> || echo
"Proceeding with all symbols global in static library") "Proceeding with all symbols global in static library")
endif() endif()
set(TEST_FLAGS -Wall -Wextra -Wunreachable-code -Wpedantic -UNDEBUG)
include(CTest) include(CTest)
if(BUILD_TESTING) # disable tests if this is being used through e.g. FetchContent
if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR AND BUILD_TESTING)
set(TEST_FLAGS -Wall -Wextra -Wunreachable-code -Wpedantic -UNDEBUG)
# corpus tests, which are tests curated by libfuzzer. The goal is to get broad # corpus tests, which are tests curated by libfuzzer. The goal is to get broad
# coverage with a small number of tests. # coverage with a small number of tests.
file(GLOB CORPUS_TESTS ${CMAKE_SOURCE_DIR}/corpus/*) file(GLOB CORPUS_TESTS ${CMAKE_CURRENT_SOURCE_DIR}/corpus/*)
# extra testing that relies on shared libraries, which aren't available with # extra testing that relies on shared libraries, which aren't available with
# wasm # wasm
@@ -162,9 +167,11 @@ if(BUILD_TESTING)
add_library(skip_list SHARED SkipList.cpp) add_library(skip_list SHARED SkipList.cpp)
target_compile_options(skip_list PRIVATE -fno-exceptions target_compile_options(skip_list PRIVATE -fno-exceptions
-fvisibility=hidden) -fvisibility=hidden)
target_include_directories(skip_list PUBLIC ${CMAKE_SOURCE_DIR}/include) target_include_directories(skip_list
set_target_properties(skip_list PROPERTIES LIBRARY_OUTPUT_DIRECTORY PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
"${CMAKE_BINARY_DIR}/skip_list") set_target_properties(
skip_list PROPERTIES LIBRARY_OUTPUT_DIRECTORY
"${CMAKE_CURRENT_BINARY_DIR}/skip_list")
set_target_properties(skip_list PROPERTIES OUTPUT_NAME ${PROJECT_NAME}) set_target_properties(skip_list PROPERTIES OUTPUT_NAME ${PROJECT_NAME})
set_target_properties( set_target_properties(
skip_list PROPERTIES VERSION ${PROJECT_VERSION} SOVERSION skip_list PROPERTIES VERSION ${PROJECT_VERSION} SOVERSION
@@ -175,10 +182,11 @@ if(BUILD_TESTING)
add_library(hash_table SHARED HashTable.cpp) add_library(hash_table SHARED HashTable.cpp)
target_compile_options(hash_table PRIVATE -fno-exceptions target_compile_options(hash_table PRIVATE -fno-exceptions
-fvisibility=hidden) -fvisibility=hidden)
target_include_directories(hash_table PUBLIC ${CMAKE_SOURCE_DIR}/include) target_include_directories(hash_table
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
set_target_properties( set_target_properties(
hash_table PROPERTIES LIBRARY_OUTPUT_DIRECTORY hash_table PROPERTIES LIBRARY_OUTPUT_DIRECTORY
"${CMAKE_BINARY_DIR}/hash_table") "${CMAKE_CURRENT_BINARY_DIR}/hash_table")
set_target_properties(hash_table PROPERTIES OUTPUT_NAME ${PROJECT_NAME}) set_target_properties(hash_table PROPERTIES OUTPUT_NAME ${PROJECT_NAME})
set_target_properties( set_target_properties(
hash_table PROPERTIES VERSION ${PROJECT_VERSION} SOVERSION hash_table PROPERTIES VERSION ${PROJECT_VERSION} SOVERSION
@@ -261,14 +269,26 @@ if(BUILD_TESTING)
# scripted tests. Written manually to fill in anything libfuzzer couldn't # scripted tests. Written manually to fill in anything libfuzzer couldn't
# find. # find.
add_executable(script_test ScriptTest.cpp) if(NOT CMAKE_CROSSCOMPILING)
target_compile_options(script_test PRIVATE ${TEST_FLAGS}) find_package(Python3 REQUIRED COMPONENTS Interpreter)
target_link_libraries(script_test PRIVATE ${PROJECT_NAME}) set_property(
file(GLOB SCRIPT_TESTS ${CMAKE_SOURCE_DIR}/script_tests/*) DIRECTORY
foreach(TEST ${SCRIPT_TESTS}) APPEND
get_filename_component(name ${TEST} NAME) PROPERTY CMAKE_CONFIGURE_DEPENDS
add_test(NAME conflict_set_script_${name} COMMAND script_test ${TEST}) ${CMAKE_CURRENT_SOURCE_DIR}/test_conflict_set.py)
endforeach() execute_process(
COMMAND ${Python3_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/test_conflict_set.py list
OUTPUT_VARIABLE SCRIPT_TESTS)
foreach(TEST ${SCRIPT_TESTS})
add_test(
NAME script_test_${TEST}
COMMAND
${Python3_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/test_conflict_set.py test ${TEST}
--build-dir ${CMAKE_CURRENT_BINARY_DIR})
endforeach()
endif()
find_program(VALGRIND_EXE valgrind) find_program(VALGRIND_EXE valgrind)
if(VALGRIND_EXE AND NOT CMAKE_CROSSCOMPILING) if(VALGRIND_EXE AND NOT CMAKE_CROSSCOMPILING)
@@ -300,25 +320,26 @@ if(BUILD_TESTING)
# symbol visibility tests # symbol visibility tests
if(NOT WASM AND NOT CMAKE_BUILD_TYPE STREQUAL Debug) if(NOT WASM AND NOT CMAKE_BUILD_TYPE STREQUAL Debug)
if(APPLE) if(APPLE)
set(symbol_exports ${CMAKE_SOURCE_DIR}/apple-symbol-exports.txt) set(symbol_exports ${CMAKE_CURRENT_SOURCE_DIR}/apple-symbol-exports.txt)
set(symbol_imports ${CMAKE_SOURCE_DIR}/apple-symbol-imports.txt) set(symbol_imports ${CMAKE_CURRENT_SOURCE_DIR}/apple-symbol-imports.txt)
else() else()
set(symbol_exports ${CMAKE_SOURCE_DIR}/symbol-exports.txt) set(symbol_exports ${CMAKE_CURRENT_SOURCE_DIR}/symbol-exports.txt)
if(CMAKE_SYSTEM_PROCESSOR STREQUAL aarch64) if(CMAKE_SYSTEM_PROCESSOR STREQUAL aarch64)
set(symbol_imports ${CMAKE_SOURCE_DIR}/aarch64-symbol-imports.txt) set(symbol_imports
${CMAKE_CURRENT_SOURCE_DIR}/aarch64-symbol-imports.txt)
else() else()
set(symbol_imports ${CMAKE_SOURCE_DIR}/symbol-imports.txt) set(symbol_imports ${CMAKE_CURRENT_SOURCE_DIR}/symbol-imports.txt)
endif() endif()
endif() endif()
add_test( add_test(
NAME conflict_set_shared_symbols NAME conflict_set_shared_symbols
COMMAND COMMAND
${CMAKE_SOURCE_DIR}/test_symbols.sh $<TARGET_FILE:${PROJECT_NAME}> ${CMAKE_CURRENT_SOURCE_DIR}/test_symbols.sh
${symbol_exports} ${symbol_imports}) $<TARGET_FILE:${PROJECT_NAME}> ${symbol_exports} ${symbol_imports})
add_test( add_test(
NAME conflict_set_static_symbols NAME conflict_set_static_symbols
COMMAND COMMAND
${CMAKE_SOURCE_DIR}/test_symbols.sh ${CMAKE_CURRENT_SOURCE_DIR}/test_symbols.sh
$<TARGET_FILE:${PROJECT_NAME}-static> ${symbol_exports} $<TARGET_FILE:${PROJECT_NAME}-static> ${symbol_exports}
${symbol_imports}) ${symbol_imports})
endif() endif()
@@ -361,13 +382,13 @@ set(CMAKE_OSX_DEPLOYMENT_TARGET 11.0)
if(APPLE) if(APPLE)
find_program(PANDOC_EXE pandoc) find_program(PANDOC_EXE pandoc)
if(PANDOC_EXE) if(PANDOC_EXE)
execute_process(COMMAND ${PANDOC_EXE} ${CMAKE_SOURCE_DIR}/README.md -o execute_process(COMMAND ${PANDOC_EXE} ${CMAKE_CURRENT_SOURCE_DIR}/README.md
${CMAKE_BINARY_DIR}/README.txt) -o ${CMAKE_CURRENT_BINARY_DIR}/README.txt)
set(CPACK_RESOURCE_FILE_README ${CMAKE_BINARY_DIR}/README.txt) set(CPACK_RESOURCE_FILE_README ${CMAKE_CURRENT_BINARY_DIR}/README.txt)
endif() endif()
configure_file(${CMAKE_SOURCE_DIR}/LICENSE ${CMAKE_BINARY_DIR}/LICENSE.txt configure_file(${CMAKE_CURRENT_SOURCE_DIR}/LICENSE
COPYONLY) ${CMAKE_CURRENT_BINARY_DIR}/LICENSE.txt COPYONLY)
set(CPACK_RESOURCE_FILE_LICENSE ${CMAKE_BINARY_DIR}/LICENSE.txt) set(CPACK_RESOURCE_FILE_LICENSE ${CMAKE_CURRENT_BINARY_DIR}/LICENSE.txt)
endif() endif()
include(CPack) include(CPack)

View File

@@ -770,7 +770,7 @@ template <class NodeT> int getChildGeqSimd(NodeT *self, int child) {
// cachegrind says the plain loop is fewer instructions and more mis-predicted // cachegrind says the plain loop is fewer instructions and more mis-predicted
// branches. Microbenchmark says plain loop is faster. It's written in this // branches. Microbenchmark says plain loop is faster. It's written in this
// weird "generic" way though in case someday we can use the simd // weird "generic" way though so that someday we can use the simd
// implementation easily if we want. // implementation easily if we want.
if constexpr (std::is_same_v<NodeT, Node3>) { if constexpr (std::is_same_v<NodeT, Node3>) {
Node3 *n = (Node3 *)self; Node3 *n = (Node3 *)self;
@@ -1146,8 +1146,14 @@ void freeAndMakeCapacityAtLeast(Node *&self, int capacity,
// capacity. // capacity.
void maybeDecreaseCapacity(Node *&self, NodeAllocators *allocators, void maybeDecreaseCapacity(Node *&self, NodeAllocators *allocators,
ConflictSet::Impl *impl) { ConflictSet::Impl *impl) {
const int maxCapacity = const int maxCapacity =
(self->numChildren + int(self->entryPresent)) * (self->partialKeyLen + 1); (self->numChildren + int(self->entryPresent)) * (self->partialKeyLen + 1);
#if DEBUG_VERBOSE && !defined(NDEBUG)
fprintf(stderr, "maybeDecreaseCapacity: current: %d, max: %d, key: %s\n",
self->getCapacity(), maxCapacity,
getSearchPathPrintable(self).c_str());
#endif
if (self->getCapacity() <= maxCapacity) { if (self->getCapacity() <= maxCapacity) {
return; return;
} }
@@ -2389,7 +2395,11 @@ Iterator firstGeq(Node *n, const std::span<const uint8_t> key) {
} else { } else {
n = nextSibling(n); n = nextSibling(n);
if (n == nullptr) { if (n == nullptr) {
return {nullptr, 1}; // This line is genuinely unreachable from any entry point of the
// final library, since we can't remove a key without introducing a
// key after it, and the only production caller of firstGeq is for
// resuming the setOldestVersion scan.
return {nullptr, 1}; // GCOVR_EXCL_LINE
} }
goto downLeftSpine; goto downLeftSpine;
} }
@@ -2510,6 +2520,10 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
} }
explicit Impl(int64_t oldestVersion) : oldestVersion(oldestVersion) { explicit Impl(int64_t oldestVersion) : oldestVersion(oldestVersion) {
#if DEBUG_VERBOSE
fprintf(stderr, "radix_tree: create\n");
#endif
// Insert "" // Insert ""
root = allocators.node0.allocate(0); root = allocators.node0.allocate(0);
root->numChildren = 0; root->numChildren = 0;
@@ -2524,7 +2538,12 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
root->entry.pointVersion = oldestVersion; root->entry.pointVersion = oldestVersion;
root->entry.rangeVersion = oldestVersion; root->entry.rangeVersion = oldestVersion;
} }
~Impl() { destroyTree(root); } ~Impl() {
#if DEBUG_VERBOSE
fprintf(stderr, "radix_tree: destroy\n");
#endif
destroyTree(root);
}
NodeAllocators allocators; NodeAllocators allocators;
@@ -2578,17 +2597,16 @@ Node *&getInTree(Node *n, ConflictSet::Impl *impl) {
: getChildExists(n->parent, n->parentsIndex); : getChildExists(n->parent, n->parentsIndex);
} }
// ==================== END IMPLEMENTATION ==================== // Internal entry points. Public entry points should just delegate to these
// GCOVR_EXCL_START void internal_check(ConflictSet::Impl *impl,
const ConflictSet::ReadRange *reads,
void ConflictSet::check(const ReadRange *reads, Result *results, ConflictSet::Result *results, int count) {
int count) const { impl->check(reads, results, count);
return impl->check(reads, results, count);
} }
void internal_addWrites(ConflictSet::Impl *impl,
void ConflictSet::addWrites(const WriteRange *writes, int count, const ConflictSet::WriteRange *writes, int count,
int64_t writeVersion) { int64_t writeVersion) {
mallocBytesDelta = 0; mallocBytesDelta = 0;
impl->addWrites(writes, count, writeVersion); impl->addWrites(writes, count, writeVersion);
impl->totalBytes += mallocBytesDelta; impl->totalBytes += mallocBytesDelta;
@@ -2599,7 +2617,7 @@ void ConflictSet::addWrites(const WriteRange *writes, int count,
#endif #endif
} }
void ConflictSet::setOldestVersion(int64_t oldestVersion) { void internal_setOldestVersion(ConflictSet::Impl *impl, int64_t oldestVersion) {
mallocBytesDelta = 0; mallocBytesDelta = 0;
impl->setOldestVersion(oldestVersion); impl->setOldestVersion(oldestVersion);
impl->totalBytes += mallocBytesDelta; impl->totalBytes += mallocBytesDelta;
@@ -2609,19 +2627,47 @@ void ConflictSet::setOldestVersion(int64_t oldestVersion) {
} }
#endif #endif
} }
ConflictSet::Impl *internal_create(int64_t oldestVersion) {
mallocBytesDelta = 0;
auto *result = new (safe_malloc(sizeof(ConflictSet::Impl)))
ConflictSet::Impl{oldestVersion};
result->totalBytes += mallocBytesDelta;
return result;
}
int64_t ConflictSet::getBytes() const { return impl->totalBytes; } void internal_destroy(ConflictSet::Impl *impl) {
impl->~Impl();
safe_free(impl, sizeof(ConflictSet::Impl));
}
int64_t internal_getBytes(ConflictSet::Impl *impl) { return impl->totalBytes; }
// ==================== END IMPLEMENTATION ====================
// GCOVR_EXCL_START
void ConflictSet::check(const ReadRange *reads, Result *results,
int count) const {
internal_check(impl, reads, results, count);
}
void ConflictSet::addWrites(const WriteRange *writes, int count,
int64_t writeVersion) {
internal_addWrites(impl, writes, count, writeVersion);
}
void ConflictSet::setOldestVersion(int64_t oldestVersion) {
internal_setOldestVersion(impl, oldestVersion);
}
int64_t ConflictSet::getBytes() const { return internal_getBytes(impl); }
ConflictSet::ConflictSet(int64_t oldestVersion) ConflictSet::ConflictSet(int64_t oldestVersion)
: impl((mallocBytesDelta = 0, : impl(internal_create(oldestVersion)) {}
new(safe_malloc(sizeof(Impl))) Impl{oldestVersion})) {
impl->totalBytes += mallocBytesDelta;
}
ConflictSet::~ConflictSet() { ConflictSet::~ConflictSet() {
if (impl) { if (impl) {
impl->~Impl(); internal_destroy(impl);
safe_free(impl, sizeof(*impl));
} }
} }
@@ -2642,40 +2688,27 @@ extern "C" {
__attribute__((__visibility__("default"))) void __attribute__((__visibility__("default"))) void
ConflictSet_check(void *cs, const ConflictSet_ReadRange *reads, ConflictSet_check(void *cs, const ConflictSet_ReadRange *reads,
ConflictSet_Result *results, int count) { ConflictSet_Result *results, int count) {
((ConflictSet::Impl *)cs)->check(reads, results, count); internal_check((ConflictSet::Impl *)cs, reads, results, count);
} }
__attribute__((__visibility__("default"))) void __attribute__((__visibility__("default"))) void
ConflictSet_addWrites(void *cs, const ConflictSet_WriteRange *writes, int count, ConflictSet_addWrites(void *cs, const ConflictSet_WriteRange *writes, int count,
int64_t writeVersion) { int64_t writeVersion) {
auto *impl = ((ConflictSet::Impl *)cs); internal_addWrites((ConflictSet::Impl *)cs, writes, count, writeVersion);
mallocBytesDelta = 0;
impl->addWrites(writes, count, writeVersion);
impl->totalBytes += mallocBytesDelta;
} }
__attribute__((__visibility__("default"))) void __attribute__((__visibility__("default"))) void
ConflictSet_setOldestVersion(void *cs, int64_t oldestVersion) { ConflictSet_setOldestVersion(void *cs, int64_t oldestVersion) {
auto *impl = ((ConflictSet::Impl *)cs); internal_setOldestVersion((ConflictSet::Impl *)cs, oldestVersion);
mallocBytesDelta = 0;
impl->setOldestVersion(oldestVersion);
impl->totalBytes += mallocBytesDelta;
} }
__attribute__((__visibility__("default"))) void * __attribute__((__visibility__("default"))) void *
ConflictSet_create(int64_t oldestVersion) { ConflictSet_create(int64_t oldestVersion) {
mallocBytesDelta = 0; return internal_create(oldestVersion);
auto *result = new (safe_malloc(sizeof(ConflictSet::Impl)))
ConflictSet::Impl{oldestVersion};
result->totalBytes += mallocBytesDelta;
return result;
} }
__attribute__((__visibility__("default"))) void ConflictSet_destroy(void *cs) { __attribute__((__visibility__("default"))) void ConflictSet_destroy(void *cs) {
using Impl = ConflictSet::Impl; internal_destroy((ConflictSet::Impl *)cs);
((Impl *)cs)->~Impl();
safe_free(cs, sizeof(Impl));
} }
__attribute__((__visibility__("default"))) int64_t __attribute__((__visibility__("default"))) int64_t
ConflictSet_getBytes(void *cs) { ConflictSet_getBytes(void *cs) {
using Impl = ConflictSet::Impl; return internal_getBytes((ConflictSet::Impl *)cs);
return ((Impl *)cs)->totalBytes;
} }
} }
@@ -2911,10 +2944,6 @@ Iterator firstGeq(Node *n, std::string_view key) {
} // namespace } // namespace
namespace std {
void __throw_length_error(const char *) { __builtin_unreachable(); }
} // namespace std
#if SHOW_MEMORY #if SHOW_MEMORY
int64_t nodeBytes = 0; int64_t nodeBytes = 0;
@@ -2995,6 +3024,7 @@ void removeKey(Node *n) {
struct __attribute__((visibility("default"))) PeakPrinter { struct __attribute__((visibility("default"))) PeakPrinter {
~PeakPrinter() { ~PeakPrinter() {
printf("--- radix_tree ---\n");
printf("malloc bytes: %g\n", double(mallocBytes)); printf("malloc bytes: %g\n", double(mallocBytes));
printf("Peak malloc bytes: %g\n", double(peakMallocBytes)); printf("Peak malloc bytes: %g\n", double(peakMallocBytes));
printf("Node bytes: %g\n", double(nodeBytes)); printf("Node bytes: %g\n", double(nodeBytes));

View File

@@ -99,8 +99,7 @@ __attribute__((always_inline)) inline void safe_free(void *p, size_t s) {
mallocBytesDelta -= s; mallocBytesDelta -= s;
#if SHOW_MEMORY #if SHOW_MEMORY
mallocBytes -= s; mallocBytes -= s;
free(p); #endif
#else
#ifndef NDEBUG #ifndef NDEBUG
(char *&)p -= kMallocHeaderSize; (char *&)p -= kMallocHeaderSize;
size_t expected; size_t expected;
@@ -108,7 +107,6 @@ __attribute__((always_inline)) inline void safe_free(void *p, size_t s) {
assert(s == expected); assert(s == expected);
#endif #endif
free(p); free(p);
#endif
} }
// ==================== BEGIN ARENA IMPL ==================== // ==================== BEGIN ARENA IMPL ====================
@@ -257,10 +255,65 @@ template <class T> struct ArenaAlloc {
void deallocate(T *, size_t) noexcept {} void deallocate(T *, size_t) noexcept {}
}; };
template <class T> using Vector = std::vector<T, ArenaAlloc<T>>; template <class T> struct Vector {
template <class T> auto vector(Arena &arena) { static_assert(std::is_trivially_destructible_v<T>);
return Vector<T>(ArenaAlloc<T>(&arena)); static_assert(std::is_trivially_copyable_v<T>);
}
explicit Vector(Arena *arena)
: arena(arena), t(nullptr), size_(0), capacity(0) {}
void append(std::span<const T> slice) {
if (size_ + int(slice.size()) > capacity) {
grow(std::max<int>(size_ + slice.size(), capacity * 2));
}
if (slice.size() > 0) {
memcpy(const_cast<std::remove_const_t<T> *>(t) + size_, slice.data(),
slice.size() * sizeof(T));
}
size_ += slice.size();
}
void push_back(const T &t) { append(std::span<const T>(&t, 1)); }
T *begin() { return t; }
T *end() { return t + size_; }
T *data() { return t; }
T &back() {
assert(size_ > 0);
return t[size_ - 1];
}
T &operator[](int i) {
assert(i >= 0 && i < size_);
return t[i];
}
void pop_back() {
assert(size_ > 0);
--size_;
}
int size() const { return size_; }
operator std::span<const T>() const { return std::span(t, size_); }
private:
void grow(int newCapacity) {
capacity = newCapacity;
auto old = std::span<const T>(*this);
t = (T *)new (std::align_val_t(alignof(T)), *arena)
uint8_t[capacity * sizeof(T)];
size_ = 0;
append(old);
}
Arena *arena;
T *t;
int size_;
int capacity;
};
template <class T> auto vector(Arena &arena) { return Vector<T>(&arena); }
template <class T, class C> using Set = std::set<T, C, ArenaAlloc<T>>; template <class T, class C> using Set = std::set<T, C, ArenaAlloc<T>>;
template <class T, class C = std::less<T>> auto set(Arena &arena) { template <class T, class C = std::less<T>> auto set(Arena &arena) {
return Set<T, C>(ArenaAlloc<T>(&arena)); return Set<T, C>(ArenaAlloc<T>(&arena));
@@ -525,15 +578,18 @@ template <class ConflictSetImpl> struct TestDriver {
explicit TestDriver(const uint8_t *data, size_t size) explicit TestDriver(const uint8_t *data, size_t size)
: arbitrary({data, size}) {} : arbitrary({data, size}) {}
int64_t writeVersion = 0; int64_t writeVersion = 100;
int64_t oldestVersion = 0; int64_t oldestVersion = 0;
ConflictSetImpl cs{oldestVersion}; ConflictSetImpl cs{oldestVersion};
ReferenceImpl refImpl{oldestVersion}; ReferenceImpl refImpl{oldestVersion};
constexpr static auto kMaxKeyLen = 8; constexpr static auto kMaxKeySuffixLen = 8;
bool ok = true; bool ok = true;
const int prefixLen = arbitrary.bounded(512);
const int prefixByte = arbitrary.randT<uint8_t>();
// Call until it returns true, for "done". Check internal invariants etc // Call until it returns true, for "done". Check internal invariants etc
// between calls to next. // between calls to next.
bool next() { bool next() {
@@ -544,7 +600,7 @@ template <class ConflictSetImpl> struct TestDriver {
{ {
int numPointWrites = arbitrary.bounded(100); int numPointWrites = arbitrary.bounded(100);
int numRangeWrites = arbitrary.bounded(100); int numRangeWrites = arbitrary.bounded(100);
int64_t v = ++writeVersion; int64_t v = (writeVersion += arbitrary.bounded(10));
auto *writes = auto *writes =
new (arena) ConflictSet::WriteRange[numPointWrites + numRangeWrites]; new (arena) ConflictSet::WriteRange[numPointWrites + numRangeWrites];
auto keys = set<std::string_view>(arena); auto keys = set<std::string_view>(arena);
@@ -552,9 +608,10 @@ template <class ConflictSetImpl> struct TestDriver {
if (!arbitrary.hasEntropy()) { if (!arbitrary.hasEntropy()) {
return true; return true;
} }
int keyLen = arbitrary.bounded(kMaxKeyLen); int keyLen = prefixLen + arbitrary.bounded(kMaxKeySuffixLen);
auto *begin = new (arena) uint8_t[keyLen]; auto *begin = new (arena) uint8_t[keyLen];
arbitrary.randomBytes(begin, keyLen); memset(begin, prefixByte, prefixLen);
arbitrary.randomBytes(begin + prefixLen, keyLen - prefixLen);
keys.insert(std::string_view((const char *)begin, keyLen)); keys.insert(std::string_view((const char *)begin, keyLen));
} }
@@ -603,8 +660,8 @@ template <class ConflictSetImpl> struct TestDriver {
refImpl.addWrites(writes, numPointWrites + numRangeWrites, v); refImpl.addWrites(writes, numPointWrites + numRangeWrites, v);
oldestVersion = std::max<int64_t>(writeVersion - arbitrary.bounded(10), oldestVersion =
oldestVersion); std::min(writeVersion - 10, oldestVersion + arbitrary.bounded(10));
cs.setOldestVersion(oldestVersion); cs.setOldestVersion(oldestVersion);
refImpl.setOldestVersion(oldestVersion); refImpl.setOldestVersion(oldestVersion);
} }
@@ -619,9 +676,10 @@ template <class ConflictSetImpl> struct TestDriver {
if (!arbitrary.hasEntropy()) { if (!arbitrary.hasEntropy()) {
return true; return true;
} }
int keyLen = arbitrary.bounded(kMaxKeyLen); int keyLen = prefixLen + arbitrary.bounded(kMaxKeySuffixLen);
auto *begin = new (arena) uint8_t[keyLen]; auto *begin = new (arena) uint8_t[keyLen];
arbitrary.randomBytes(begin, keyLen); memset(begin, prefixByte, prefixLen);
arbitrary.randomBytes(begin + prefixLen, keyLen - prefixLen);
keys.insert(std::string_view((const char *)begin, keyLen)); keys.insert(std::string_view((const char *)begin, keyLen));
} }

3
Jenkinsfile vendored
View File

@@ -111,6 +111,9 @@ pipeline {
gcovr -f ConflictSet.cpp --cobertura > build/coverage.xml gcovr -f ConflictSet.cpp --cobertura > build/coverage.xml
''' '''
cobertura autoUpdateHealth: false, autoUpdateStability: false, coberturaReportFile: 'build/coverage.xml', conditionalCoverageTargets: '70, 0, 0', failUnhealthy: false, failUnstable: false, lineCoverageTargets: '80, 0, 0', maxNumberOfBuilds: 0, methodCoverageTargets: '80, 0, 0', onlyStable: false, sourceEncoding: 'ASCII', zoomCoverageChart: false cobertura autoUpdateHealth: false, autoUpdateStability: false, coberturaReportFile: 'build/coverage.xml', conditionalCoverageTargets: '70, 0, 0', failUnhealthy: false, failUnstable: false, lineCoverageTargets: '80, 0, 0', maxNumberOfBuilds: 0, methodCoverageTargets: '80, 0, 0', onlyStable: false, sourceEncoding: 'ASCII', zoomCoverageChart: false
sh '''
gcovr -f ConflictSet.cpp --fail-under-line 100 > /dev/null
'''
} }
} }
} }

View File

@@ -1,155 +0,0 @@
#include "Internal.h"
#include <ConflictSet.h>
#include <chrono>
#include <cstring>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#include <vector>
inline size_t getPageSize() {
static size_t kPageSize = sysconf(_SC_PAGESIZE);
return kPageSize;
}
/// Helper for rounding up to page size (or some other alignment)
constexpr inline size_t rightAlign(size_t offset, size_t alignment) {
return offset % alignment == 0 ? offset
: ((offset / alignment) + 1) * alignment;
}
using StringView = std::basic_string_view<uint8_t>;
inline StringView operator"" _v(const char *str, size_t size) {
return {reinterpret_cast<const uint8_t *>(str), size};
}
int main(int argc, const char **argv) {
ConflictSet cs{0};
ReferenceImpl ref{0};
for (int i = 1; i < argc; ++i) {
int fd = open(argv[i], O_RDONLY);
struct stat st;
if (fstat(fd, &st) == -1) {
int err = errno;
fprintf(stderr, "stat error %s - %s\n", argv[i], strerror(err));
fflush(stderr);
abort();
}
int64_t size = rightAlign(st.st_size, getPageSize());
const uint8_t *begin =
(uint8_t *)mmap(0, size, PROT_READ, MAP_PRIVATE, fd, 0);
madvise((void *)begin, size, MADV_SEQUENTIAL);
auto *const mapOriginal = begin;
const auto sizeOriginal = size;
StringView b;
StringView e;
int64_t v = 0;
int64_t lastWriteVersion = 0;
int64_t lastOldestVersion = 0;
std::vector<ConflictSet::WriteRange> writeRanges;
std::vector<ConflictSet::ReadRange> readRanges;
std::vector<ConflictSet::Result> results;
for (uint8_t *end = (uint8_t *)memchr(begin, '\n', size); end != nullptr;) {
StringView line{begin, static_cast<size_t>(end - begin)};
size -= end - begin + 1;
begin = end + 1;
end = (uint8_t *)memchr(begin, '\n', size);
if (line.starts_with("begin"_v)) {
b = line.substr("begin "_v.size(), line.size());
printf("b <- %.*s\n", int(b.size()), b.data());
} else if (line.starts_with("end"_v)) {
e = line.substr("end "_v.size(), line.size());
printf("e <- %.*s\n", int(e.size()), e.data());
} else if (line.starts_with("version"_v)) {
line = line.substr("version "_v.size(), line.size());
v = 0;
for (auto c : line) {
v = v * 10 + int(c) - int('0');
}
printf("v <- %" PRId64 "\n", v);
} else if (line.starts_with("pointread"_v)) {
printf("pointread\n");
ConflictSet::ReadRange r;
r.begin.p = b.data();
r.begin.len = b.size();
r.end.len = 0;
r.readVersion = v;
readRanges.push_back(r);
} else if (line.starts_with("pointwrite"_v)) {
printf("pointwrite\n");
assert(writeRanges.empty() ||
(writeRanges.back().end.len == 0 ? writeRanges.back().begin
: writeRanges.back().end) < b);
ConflictSet::WriteRange w;
w.begin.p = b.data();
w.begin.len = b.size();
w.end.len = 0;
writeRanges.push_back(w);
} else if (line.starts_with("rangeread"_v)) {
printf("rangeread\n");
ConflictSet::ReadRange r;
r.begin.p = b.data();
r.begin.len = b.size();
r.end.p = e.data();
r.end.len = e.size();
r.readVersion = v;
readRanges.push_back(r);
} else if (line.starts_with("rangewrite"_v)) {
printf("rangewrite\n");
assert(b < e);
assert(writeRanges.empty() ||
(writeRanges.back().end.len == 0 ? writeRanges.back().begin
: writeRanges.back().end) < b);
ConflictSet::WriteRange w;
w.begin.p = b.data();
w.begin.len = b.size();
w.end.p = e.data();
w.end.len = e.size();
writeRanges.push_back(w);
} else if (line.starts_with("check"_v)) {
printf("check\n");
Arena arena;
auto *expected = new (arena) ConflictSet::Result[readRanges.size()];
auto *actual = new (arena) ConflictSet::Result[readRanges.size()];
ref.check(readRanges.data(), expected, readRanges.size());
cs.check(readRanges.data(), actual, readRanges.size());
for (int i = 0; i < int(readRanges.size()); ++i) {
if (expected[i] != actual[i]) {
fprintf(stderr, "Expected %s, got %s at index %d\n",
resultToStr(expected[i]), resultToStr(actual[i]), i);
return 1;
}
}
readRanges = {};
} else if (line.starts_with("addwrites"_v)) {
printf("addwrites\n");
assert(v > lastWriteVersion);
lastWriteVersion = v;
cs.addWrites(writeRanges.data(), writeRanges.size(), v);
ref.addWrites(writeRanges.data(), writeRanges.size(), v);
writeRanges = {};
} else if (line.starts_with("setoldest"_v)) {
printf("setoldest\n");
assert(v > lastOldestVersion);
lastOldestVersion = v;
cs.setOldestVersion(v);
ref.setOldestVersion(v);
} else if (line.empty() || line.starts_with(";"_v)) {
// skip
} else {
printf("Unrecognized line: %.*s\n", int(line.size()), line.data());
}
}
munmap((void *)mapOriginal, sizeOriginal);
close(fd);
}
}

View File

@@ -16,6 +16,8 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*
* This source code is modified to compile outside of FoundationDB
*/ */
#include "ConflictSet.h" #include "ConflictSet.h"
@@ -268,13 +270,21 @@ public:
} }
explicit SkipList(Version version = 0) { explicit SkipList(Version version = 0) {
#if DEBUG_VERBOSE
fprintf(stderr, "skip_list: create\n");
#endif
header = Node::create(StringRef(), MaxLevels - 1); header = Node::create(StringRef(), MaxLevels - 1);
for (int l = 0; l < MaxLevels; l++) { for (int l = 0; l < MaxLevels; l++) {
header->setNext(l, nullptr); header->setNext(l, nullptr);
header->setMaxVersion(l, version); header->setMaxVersion(l, version);
} }
} }
~SkipList() { destroy(); } ~SkipList() {
#if DEBUG_VERBOSE
fprintf(stderr, "skip_list: destroy\n");
#endif
destroy();
}
SkipList(SkipList &&other) noexcept : header(other.header) { SkipList(SkipList &&other) noexcept : header(other.header) {
other.header = nullptr; other.header = nullptr;
} }
@@ -603,6 +613,10 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
for (int s = stripes - 1; s >= 0; s--) { for (int s = stripes - 1; s >= 0; s--) {
for (int i = 0; i * 2 < ss; ++i) { for (int i = 0; i * 2 < ss; ++i) {
const auto &w = writes[s * stripeSize / 2 + i]; const auto &w = writes[s * stripeSize / 2 + i];
#if DEBUG_VERBOSE
printf("Write begin: %s\n", printable(w.begin).c_str());
fflush(stdout);
#endif
values[i * 2] = {w.begin.p, size_t(w.begin.len)}; values[i * 2] = {w.begin.p, size_t(w.begin.len)};
values[i * 2 + 1] = w.end.len > 0 values[i * 2 + 1] = w.end.len > 0
? StringRef{w.end.p, size_t(w.end.len)} ? StringRef{w.end.p, size_t(w.end.len)}
@@ -637,13 +651,16 @@ private:
SkipList skipList; SkipList skipList;
}; };
void ConflictSet::check(const ReadRange *reads, Result *results, // Internal entry points. Public entry points should just delegate to these
int count) const {
void internal_check(ConflictSet::Impl *impl,
const ConflictSet::ReadRange *reads,
ConflictSet::Result *results, int count) {
impl->check(reads, results, count); impl->check(reads, results, count);
} }
void internal_addWrites(ConflictSet::Impl *impl,
void ConflictSet::addWrites(const WriteRange *writes, int count, const ConflictSet::WriteRange *writes, int count,
int64_t writeVersion) { int64_t writeVersion) {
mallocBytesDelta = 0; mallocBytesDelta = 0;
impl->addWrites(writes, count, writeVersion); impl->addWrites(writes, count, writeVersion);
impl->totalBytes += mallocBytesDelta; impl->totalBytes += mallocBytesDelta;
@@ -654,7 +671,7 @@ void ConflictSet::addWrites(const WriteRange *writes, int count,
#endif #endif
} }
void ConflictSet::setOldestVersion(int64_t oldestVersion) { void internal_setOldestVersion(ConflictSet::Impl *impl, int64_t oldestVersion) {
mallocBytesDelta = 0; mallocBytesDelta = 0;
impl->setOldestVersion(oldestVersion); impl->setOldestVersion(oldestVersion);
impl->totalBytes += mallocBytesDelta; impl->totalBytes += mallocBytesDelta;
@@ -664,19 +681,43 @@ void ConflictSet::setOldestVersion(int64_t oldestVersion) {
} }
#endif #endif
} }
ConflictSet::Impl *internal_create(int64_t oldestVersion) {
mallocBytesDelta = 0;
auto *result = new (safe_malloc(sizeof(ConflictSet::Impl)))
ConflictSet::Impl{oldestVersion};
result->totalBytes += mallocBytesDelta;
return result;
}
int64_t ConflictSet::getBytes() const { return impl->totalBytes; } void internal_destroy(ConflictSet::Impl *impl) {
impl->~Impl();
safe_free(impl, sizeof(ConflictSet::Impl));
}
int64_t internal_getBytes(ConflictSet::Impl *impl) { return impl->totalBytes; }
void ConflictSet::check(const ReadRange *reads, Result *results,
int count) const {
internal_check(impl, reads, results, count);
}
void ConflictSet::addWrites(const WriteRange *writes, int count,
int64_t writeVersion) {
internal_addWrites(impl, writes, count, writeVersion);
}
void ConflictSet::setOldestVersion(int64_t oldestVersion) {
internal_setOldestVersion(impl, oldestVersion);
}
int64_t ConflictSet::getBytes() const { return internal_getBytes(impl); }
ConflictSet::ConflictSet(int64_t oldestVersion) ConflictSet::ConflictSet(int64_t oldestVersion)
: impl((mallocBytesDelta = 0, : impl(internal_create(oldestVersion)) {}
new(safe_malloc(sizeof(Impl))) Impl{oldestVersion})) {
impl->totalBytes += mallocBytesDelta;
}
ConflictSet::~ConflictSet() { ConflictSet::~ConflictSet() {
if (impl) { if (impl) {
impl->~Impl(); internal_destroy(impl);
safe_free(impl, sizeof(Impl));
} }
} }
@@ -697,37 +738,34 @@ extern "C" {
__attribute__((__visibility__("default"))) void __attribute__((__visibility__("default"))) void
ConflictSet_check(void *cs, const ConflictSet_ReadRange *reads, ConflictSet_check(void *cs, const ConflictSet_ReadRange *reads,
ConflictSet_Result *results, int count) { ConflictSet_Result *results, int count) {
((ConflictSet::Impl *)cs)->check(reads, results, count); internal_check((ConflictSet::Impl *)cs, reads, results, count);
} }
__attribute__((__visibility__("default"))) void __attribute__((__visibility__("default"))) void
ConflictSet_addWrites(void *cs, const ConflictSet_WriteRange *writes, int count, ConflictSet_addWrites(void *cs, const ConflictSet_WriteRange *writes, int count,
int64_t writeVersion) { int64_t writeVersion) {
((ConflictSet::Impl *)cs)->addWrites(writes, count, writeVersion); internal_addWrites((ConflictSet::Impl *)cs, writes, count, writeVersion);
} }
__attribute__((__visibility__("default"))) void __attribute__((__visibility__("default"))) void
ConflictSet_setOldestVersion(void *cs, int64_t oldestVersion) { ConflictSet_setOldestVersion(void *cs, int64_t oldestVersion) {
((ConflictSet::Impl *)cs)->setOldestVersion(oldestVersion); internal_setOldestVersion((ConflictSet::Impl *)cs, oldestVersion);
} }
__attribute__((__visibility__("default"))) void * __attribute__((__visibility__("default"))) void *
ConflictSet_create(int64_t oldestVersion) { ConflictSet_create(int64_t oldestVersion) {
return new (safe_malloc(sizeof(ConflictSet::Impl))) return internal_create(oldestVersion);
ConflictSet::Impl{oldestVersion};
} }
__attribute__((__visibility__("default"))) void ConflictSet_destroy(void *cs) { __attribute__((__visibility__("default"))) void ConflictSet_destroy(void *cs) {
using Impl = ConflictSet::Impl; internal_destroy((ConflictSet::Impl *)cs);
((Impl *)cs)->~Impl();
safe_free(cs, sizeof(Impl));
} }
__attribute__((__visibility__("default"))) int64_t __attribute__((__visibility__("default"))) int64_t
ConflictSet_getBytes(void *cs) { ConflictSet_getBytes(void *cs) {
using Impl = ConflictSet::Impl; return internal_getBytes((ConflictSet::Impl *)cs);
return ((Impl *)cs)->totalBytes;
} }
} }
#if SHOW_MEMORY #if SHOW_MEMORY
struct __attribute__((visibility("default"))) PeakPrinter { struct __attribute__((visibility("default"))) PeakPrinter {
~PeakPrinter() { ~PeakPrinter() {
printf("--- skip_list ---\n");
printf("malloc bytes: %g\n", double(mallocBytes)); printf("malloc bytes: %g\n", double(mallocBytes));
printf("Peak malloc bytes: %g\n", double(peakMallocBytes)); printf("Peak malloc bytes: %g\n", double(peakMallocBytes));
} }

View File

@@ -4,22 +4,6 @@ import os
from typing import Optional from typing import Optional
_lib = None
for f in (
os.path.dirname(__file__) + "/build/radix_tree/libconflict-set.so.0",
os.path.dirname(__file__) + "/build/radix_tree/libconflict-set.0.dylib",
):
try:
_lib = ctypes.cdll.LoadLibrary(f)
except:
pass
if _lib is None:
import sys
print("Could not find libconflict-set", file=sys.stderr)
sys.exit(1)
class _Key(ctypes.Structure): class _Key(ctypes.Structure):
_fields_ = [("p", ctypes.POINTER(ctypes.c_ubyte)), ("len", ctypes.c_int)] _fields_ = [("p", ctypes.POINTER(ctypes.c_ubyte)), ("len", ctypes.c_int)]
@@ -37,31 +21,6 @@ class WriteRange(ctypes.Structure):
_fields_ = [("begin", _Key), ("end", _Key)] _fields_ = [("begin", _Key), ("end", _Key)]
_lib.ConflictSet_create.argtypes = (ctypes.c_int64,)
_lib.ConflictSet_create.restype = ctypes.c_void_p
_lib.ConflictSet_check.argtypes = (
ctypes.c_void_p,
ctypes.POINTER(ReadRange),
ctypes.POINTER(ctypes.c_int),
ctypes.c_int,
)
_lib.ConflictSet_addWrites.argtypes = (
ctypes.c_void_p,
ctypes.POINTER(WriteRange),
ctypes.c_int,
ctypes.c_int64,
)
_lib.ConflictSet_setOldestVersion.argtypes = (ctypes.c_void_p, ctypes.c_int64)
_lib.ConflictSet_destroy.argtypes = (ctypes.c_void_p,)
_lib.ConflictSet_getBytes.argtypes = (ctypes.c_void_p,)
_lib.ConflictSet_getBytes.restype = ctypes.c_int64
class Result(enum.Enum): class Result(enum.Enum):
COMMIT = 0 COMMIT = 0
CONFLICT = 1 CONFLICT = 1
@@ -69,58 +28,109 @@ class Result(enum.Enum):
def write(begin: bytes, end: Optional[bytes] = None) -> WriteRange: def write(begin: bytes, end: Optional[bytes] = None) -> WriteRange:
b = (ctypes.c_ubyte * len(begin))() b = (ctypes.c_ubyte * len(begin)).from_buffer(bytearray(begin))
b.value = begin
if end is None: if end is None:
e = (ctypes.c_ubyte * 0)() e = (ctypes.c_ubyte * 0)()
e.value = b""
else: else:
e = (ctypes.c_ubyte * len(end))() e = (ctypes.c_ubyte * len(end)).from_buffer(bytearray(end))
e.value = end
return WriteRange(_Key(b, len(b)), _Key(e, len(e))) return WriteRange(_Key(b, len(b)), _Key(e, len(e)))
def read(version: int, begin: bytes, end: Optional[bytes] = None) -> ReadRange: def read(version: int, begin: bytes, end: Optional[bytes] = None) -> ReadRange:
b = (ctypes.c_ubyte * len(begin))() b = (ctypes.c_ubyte * len(begin)).from_buffer(bytearray(begin))
b.value = begin
if end is None: if end is None:
e = (ctypes.c_ubyte * 0)() e = (ctypes.c_ubyte * 0)()
e.value = b""
else: else:
e = (ctypes.c_ubyte * len(end))() e = (ctypes.c_ubyte * len(end)).from_buffer(bytearray(end))
e.value = end
return ReadRange(_Key(b, len(b)), _Key(e, len(e)), version) return ReadRange(_Key(b, len(b)), _Key(e, len(e)), version)
class ConflictSet: class ConflictSet:
def __init__(self, version: int = 0) -> None: def __init__(
self.p = _lib.ConflictSet_create(version) self,
version: int = 0,
build_dir: Optional[str] = None,
implementation: Optional[str] = None,
) -> None:
self._lib = None
if build_dir is None:
build_dir = os.path.dirname(__file__) + "/build"
if implementation is None:
implementation = "radix_tree"
for f in (
build_dir + "/" + implementation + "/libconflict-set.so.0",
os.path.dirname(__file__)
+ "/build/"
+ implementation
+ "/libconflict-set.0.dylib",
):
try:
self._lib = ctypes.cdll.LoadLibrary(f)
except:
pass
if self._lib is None:
import sys
print(
"Could not find libconflict-set implementation " + implementation,
file=sys.stderr,
)
sys.exit(1)
self._lib.ConflictSet_create.argtypes = (ctypes.c_int64,)
self._lib.ConflictSet_create.restype = ctypes.c_void_p
self._lib.ConflictSet_check.argtypes = (
ctypes.c_void_p,
ctypes.POINTER(ReadRange),
ctypes.POINTER(ctypes.c_int),
ctypes.c_int,
)
self._lib.ConflictSet_addWrites.argtypes = (
ctypes.c_void_p,
ctypes.POINTER(WriteRange),
ctypes.c_int,
ctypes.c_int64,
)
self._lib.ConflictSet_setOldestVersion.argtypes = (
ctypes.c_void_p,
ctypes.c_int64,
)
self._lib.ConflictSet_destroy.argtypes = (ctypes.c_void_p,)
self._lib.ConflictSet_getBytes.argtypes = (ctypes.c_void_p,)
self._lib.ConflictSet_getBytes.restype = ctypes.c_int64
self.p = self._lib.ConflictSet_create(version)
def addWrites(self, version: int, *writes: WriteRange): def addWrites(self, version: int, *writes: WriteRange):
_lib.ConflictSet_addWrites( self._lib.ConflictSet_addWrites(
self.p, (WriteRange * len(writes))(*writes), len(writes), version self.p, (WriteRange * len(writes))(*writes), len(writes), version
) )
def check(self, *reads: ReadRange) -> list[Result]: def check(self, *reads: ReadRange) -> list[Result]:
r = (ctypes.c_int * len(reads))() r = (ctypes.c_int * len(reads))()
_lib.ConflictSet_check(self.p, *reads, r, 1) self._lib.ConflictSet_check(self.p, *reads, r, 1)
return [Result(x) for x in r] return [Result(x) for x in r]
def setOldestVersion(self, version: int) -> None: def setOldestVersion(self, version: int) -> None:
_lib.ConflictSet_setOldestVersion(self.p, version) self._lib.ConflictSet_setOldestVersion(self.p, version)
def getBytes(self) -> int: def getBytes(self) -> int:
return _lib.ConflictSet_getBytes(self.p) return self._lib.ConflictSet_getBytes(self.p)
def __enter__(self): def __enter__(self):
return self return self
def close(self) -> None: def close(self) -> None:
if self.p is not None: if self.p is not None:
_lib.ConflictSet_destroy(self.p) self._lib.ConflictSet_destroy(self.p)
self.p = None self.p = None
def __exit__(self, exception_type, exception_value, exception_traceback): def __exit__(self, exception_type, exception_value, exception_traceback):
if self.p is not None: self.close()
_lib.ConflictSet_destroy(self.p)
self.p = None

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More