Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 26 additions & 19 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
16 changes: 13 additions & 3 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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)
Expand Down
37 changes: 37 additions & 0 deletions CMakePresets.json
Original file line number Diff line number Diff line change
@@ -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"
}
]
}
26 changes: 26 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 <regex>` - only run tests matching the regular expression `<regex>`


## 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
Expand Down
4 changes: 2 additions & 2 deletions stitch/hazard_pointers.h

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just silencing some compiler warnings about an unused variable here.

Original file line number Diff line number Diff line change
Expand Up @@ -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())
{
Expand Down
4 changes: 2 additions & 2 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 1 addition & 2 deletions tests/test_atom.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 },
Expand Down
23 changes: 12 additions & 11 deletions tests/test_lockfree_set.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ using namespace Testing;
using namespace Stitch;
using namespace std;

static bool empty()
static bool test_empty()
{
Test test;

Expand All @@ -26,7 +26,7 @@ static bool empty()
return test.success();
}

static bool contains()
static bool test_contains()
{
Test test;

Expand Down Expand Up @@ -63,7 +63,7 @@ static bool contains()
return test.success();
}

static bool iteration()
static bool test_iteration()
{
Test test;

Expand Down Expand Up @@ -139,7 +139,7 @@ static bool iteration()
return test.success();
}

static bool reclamation()
static bool test_reclamation()
{
using Detail::Hazard_Pointers;

Expand Down Expand Up @@ -279,7 +279,7 @@ static bool test_destructor()
return test.success();
}

static bool stress()
static bool test_stress()
{
Test test;

Expand Down Expand Up @@ -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);
Expand All @@ -364,12 +364,13 @@ Test_Set lockfree_set_tests()
{
return
{
{ "empty", empty },
{ "contains", contains },
{ "iteration", iteration },
{ "empty", test_empty },

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test names needed to be changed when raising the C++ standard level - the empty symbol clashed with parts of standard library, causing a misleading error.

{ "contains", test_contains },
{ "iteration", test_iteration },
{ "removal-during-iteration", test_removal_during_iteration },
{ "destructor", test_destructor },
{ "reclamation", reclamation },
{ "stress", stress },
{ "reclamation", test_reclamation },
// FIXME: This test is flaky - see comment in test_stress.
// { "stress", test_stress },
};
}