From 20103895c27e74a6a30e614dc973f278db5e176b Mon Sep 17 00:00:00 2001 From: Jakob Leben Date: Sat, 15 Nov 2025 22:09:55 -0800 Subject: [PATCH 1/3] Modernize build process --- .github/workflows/test.yml | 45 +++++++++++++++++++++---------------- CMakeLists.txt | 16 ++++++++++--- CMakePresets.json | 37 ++++++++++++++++++++++++++++++ stitch/hazard_pointers.h | 4 ++-- tests/CMakeLists.txt | 4 ++-- tests/test_atom.cpp | 3 +-- tests/test_lockfree_set.cpp | 22 +++++++++--------- 7 files changed, 92 insertions(+), 39 deletions(-) create mode 100644 CMakePresets.json diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index db12988..fbd346e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -14,44 +14,51 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-latest] + os: + - ubuntu-24.04 + # Not yet supported + # - macos-26 include: - - os: ubuntu-latest - platform: Linux + - os: ubuntu-24.04 + platform: linux + arch: x64 + cpus: 4 + # - os: macos-26 + # platform: macos + # arch: arm64 + # cpus: 3 steps: + - name: Display Build Info + run: | + echo "Platform: ${{ matrix.platform }}" + echo "OS: ${{ matrix.os }}" + cmake --version + + - name: Install dependencies + if: ${{ matrix.platform == 'linux' }} + run: sudo apt-get update && sudo apt-get install -y libc++-dev + - name: Checkout code uses: actions/checkout@v4 with: submodules: recursive - - name: Install CMake - uses: jwlawson/actions-setup-cmake@v2 - with: - cmake-version: '3.27.x' - - name: Configure CMake run: | - cmake -B build \ - -DSTITCH_BUILD_TESTS=ON \ - -DCMAKE_BUILD_TYPE=Release + cmake --preset ${{ matrix.platform }}-clang - name: Build - run: cmake --build build --config Release + run: cmake --build --preset ${{ matrix.platform }}-clang -j${{ matrix.cpus }} - name: Run Tests - working-directory: build/tests + working-directory: build/${{ matrix.platform }}-clang/ run: | set +e - ./tester 2>&1 | tee /dev/stderr | grep -q "The following tests failed:" + ./tests/tester 2>&1 | tee /dev/stderr | grep -q "The following tests failed:" if [ $? -eq 0 ]; then echo "Tests failed!" exit 1 fi set -e - - name: Display Build Info - run: | - echo "Platform: ${{ matrix.platform }}" - echo "OS: ${{ matrix.os }}" - cmake --version diff --git a/CMakeLists.txt b/CMakeLists.txt index 730aa80..5b3b5cd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,13 +1,23 @@ -cmake_minimum_required(VERSION 3.1) +cmake_minimum_required(VERSION 3.27) -project(stitch) +project(stitch LANGUAGES CXX) option(STITCH_BUILD_DOCUMENTATION "Build Stitch documentation." OFF) option(STITCH_BUILD_TESTS "Build Stitch tests." OFF) option(STITCH_STATIC_LIB "Build Stitch as a static library." OFF) +include(CheckCompilerFlag) + add_compile_options(-Wall -Wextra -Wno-unused-parameter -Wno-sign-compare -Werror=return-type) +check_compiler_flag(CXX -mcx16 cxx_flag_mcx16_available) +if(${cxx_flag_mcx16_available}) + message(STATUS "Using compiler flag -mcx16") + add_compile_options(-mcx16) +else() + message(STATUS "Compiler flag -mcx16 not available.") +endif() + set(sources stitch/hazard_pointers.cpp stitch/linux/events.cpp @@ -25,7 +35,7 @@ else() add_library(stitch SHARED ${sources}) endif() -set_property(TARGET stitch PROPERTY CXX_STANDARD 14) +set_property(TARGET stitch PROPERTY CXX_STANDARD 23) if (STITCH_BUILD_DOCUMENTATION) add_subdirectory(doc) diff --git a/CMakePresets.json b/CMakePresets.json new file mode 100644 index 0000000..3360894 --- /dev/null +++ b/CMakePresets.json @@ -0,0 +1,37 @@ +{ + "version": 7, + "cmakeMinimumRequired": { + "major": 3, + "minor": 27, + "patch": 0 + }, + "configurePresets": [ + { + "name": "linux-clang", + "displayName": "Linux-Clang", + "description": "Configuration for Linux, Clang", + "binaryDir": "${sourceDir}/build/linux-clang", + "cacheVariables": { + "STITCH_BUILD_TESTS": "ON", + "CMAKE_CXX_COMPILER": "clang++", + "CMAKE_CXX_FLAGS": "-stdlib=libc++" + } + }, + { + "name": "macos-clang", + "displayName": "MacOS-Clang", + "description": "Configuration for MacOS, Clang", + "inherits": "linux-clang" + } + ], + "buildPresets": [ + { + "name": "linux-clang", + "configurePreset": "linux-clang" + }, + { + "name": "macos-clang", + "configurePreset": "macos-clang" + } + ] +} diff --git a/stitch/hazard_pointers.h b/stitch/hazard_pointers.h index d2c7629..dea7196 100644 --- a/stitch/hazard_pointers.h +++ b/stitch/hazard_pointers.h @@ -39,10 +39,10 @@ class Hazard_Pointers int i = d_pointer_alloc_hint.load(); int j = i; int m = H-1; - int c = 0; + // int c = 0; do { - ++c; + // ++c; j = (j + 1) & m; if (d_pointers[j].acquire()) { diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 8803791..794ab9a 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -7,12 +7,12 @@ if(NOT EXISTS ${CMAKE_SOURCE_DIR}/testing/testing.cpp) endif() add_library(testing STATIC ../testing/testing.cpp) -set_property(TARGET testing PROPERTY CXX_STANDARD 14) +set_property(TARGET testing PROPERTY CXX_STANDARD 23) function(make_test name src) add_executable(${name} ${src}) target_link_libraries(${name} testing stitch pthread atomic) - set_property(TARGET ${name} PROPERTY CXX_STANDARD 14) + set_property(TARGET ${name} PROPERTY CXX_STANDARD 23) endfunction() set(test_sources diff --git a/tests/test_atom.cpp b/tests/test_atom.cpp index c0bf125..da8196b 100644 --- a/tests/test_atom.cpp +++ b/tests/test_atom.cpp @@ -320,8 +320,7 @@ static bool test_stress() Test_Set atom_tests() { return { - // TODO: figure out why this fails - // { "lockfree", test_lockfree }, + { "lockfree", test_lockfree }, { "default-value", test_default_value }, { "basic-store-load", test_basic_store_load }, { "single-writer-reader", test_single_writer_single_reader }, diff --git a/tests/test_lockfree_set.cpp b/tests/test_lockfree_set.cpp index b1b5aa3..529fb74 100644 --- a/tests/test_lockfree_set.cpp +++ b/tests/test_lockfree_set.cpp @@ -11,7 +11,7 @@ using namespace Testing; using namespace Stitch; using namespace std; -static bool empty() +static bool test_empty() { Test test; @@ -26,7 +26,7 @@ static bool empty() return test.success(); } -static bool contains() +static bool test_contains() { Test test; @@ -63,7 +63,7 @@ static bool contains() return test.success(); } -static bool iteration() +static bool test_iteration() { Test test; @@ -139,7 +139,7 @@ static bool iteration() return test.success(); } -static bool reclamation() +static bool test_reclamation() { using Detail::Hazard_Pointers; @@ -279,7 +279,7 @@ static bool test_destructor() return test.success(); } -static bool stress() +static bool test_stress() { Test test; @@ -340,7 +340,7 @@ static bool stress() // FIXME: This test is invalid. // It is possible for set.for_each // to iterate over the same value multiple times, - // if the value was remove and reinserted in the meantime. + // if the value was removed and reinserted in the meantime. bool is_unique; tie(ignore, is_unique) = unique_elements.emplace(e); @@ -364,12 +364,12 @@ Test_Set lockfree_set_tests() { return { - { "empty", empty }, - { "contains", contains }, - { "iteration", iteration }, + { "empty", test_empty }, + { "contains", test_contains }, + { "iteration", test_iteration }, { "removal-during-iteration", test_removal_during_iteration }, { "destructor", test_destructor }, - { "reclamation", reclamation }, - { "stress", stress }, + { "reclamation", test_reclamation }, + { "stress", test_stress }, }; } From 93f92ada9742f1d3e1df7b4e9b2bac7de4e100bb Mon Sep 17 00:00:00 2001 From: Jakob Leben Date: Sat, 15 Nov 2025 22:20:57 -0800 Subject: [PATCH 2/3] Disable a flaky test --- tests/test_lockfree_set.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_lockfree_set.cpp b/tests/test_lockfree_set.cpp index 529fb74..6a1be21 100644 --- a/tests/test_lockfree_set.cpp +++ b/tests/test_lockfree_set.cpp @@ -370,6 +370,7 @@ Test_Set lockfree_set_tests() { "removal-during-iteration", test_removal_during_iteration }, { "destructor", test_destructor }, { "reclamation", test_reclamation }, - { "stress", test_stress }, + // FIXME: This test is flaky - see comment in test_stress. + // { "stress", test_stress }, }; } From a805fe580d86a7b3a9fdeab4436600b30075bf34 Mon Sep 17 00:00:00 2001 From: Jakob Leben Date: Sat, 15 Nov 2025 23:02:45 -0800 Subject: [PATCH 3/3] Expand README --- README.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/README.md b/README.md index e1a3fcf..1806809 100644 --- a/README.md +++ b/README.md @@ -169,17 +169,43 @@ State updater and observers communicate the latest state of a value. thread observer2_thread(observer_func); +## Building Stitch +Stitch comes with a CMake based build system. +CMake presets are available for building the Stitch shared library and tests. For example, on Linux: + cmake --preset linux-clang + cmake --build --preset linux-clang -j 4 +This will generate build outputs under `build/linux-clang`: +- libstitch.so - shared library +- tests/tester - test executable +It is **advised to run the tests** on any system where you intend to use Stitch. The test suite includes **tests for lock-freedom and wait-freedom** of certain operations which can depend on the specific machine where Stitch is running. +Test executable options: +- `tester` - run all tests +- `tester -v` - run tests with verbose output +- `tester -l`- list all test names +- `tests ` - only run tests matching the regular expression `` +## Limitations +Stich relies on operations on `std::atomic` with a double-word template type being lock-free. On 64-bit systems, this requires the use of atomic 128-bit instructions in machine code. Though most modern processors support such instructions, most compilers do not readily generate them (presumably to maximize compatibility with older systems). That causes those `std::atomic` operations to not be lock-free. This page provides lots of useful insights into this issue and possible workarounds for different compilers: https://timur.audio/dwcas-in-c + +One build configuration known to satisfy all lock freedom requirements is: + +- Intel 64-bit architecture +- Linux +- Clang compiler +- `libc++` standard library +- Explicitly using the `-mcx16` compiler flag. + +This configuration is encoded in the `linux-clang` CMake preset and used to run tests in the CI pipeline (including tests for lock freedom and wait freedom). ## Comparison with Related Software