test: add ThreadSanitizer verification for lock-free Channel<T>

Add a contended SPSC stress suite (tests/test_channel_stress.cpp) that
actually exercises the ring's memory-ordering pairing and spin/futex/
lost-wakeup logic, plus the CMake and CI plumbing to run it under TSan:

- KPN_SANITIZER cache var + kpn_sanitizer_flags() helper (no-op when unset)
- kpn_tests_stress executable, labelled "stress" for CTest
- reusable tsan.yaml workflow (gcc:14 builder image, already ships libtsan)
- ci.yaml gains a tsan job on the same code/dockerfile triggers as test

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-14 19:17:19 +02:00
co-authored by Claude Opus 4.8
parent 66feb91821
commit 50032ffce3
5 changed files with 407 additions and 0 deletions
+21
View File
@@ -43,6 +43,27 @@ target_link_libraries(kpn_tests PRIVATE
GTest::gtest
)
# ── Channel stress suite (separate executable) ────────────────────────────────
# Contended SPSC tests for the lock-free Channel<T>. Kept out of kpn_tests
# because each case runs many reps / tens of thousands of items and is slow.
# Most valuable under -DKPN_SANITIZER=thread, but correct (and run) without it.
add_executable(kpn_tests_stress test_channel_stress.cpp)
target_link_libraries(kpn_tests_stress PRIVATE kpn Catch2::Catch2WithMain)
# ── Sanitizer flags ───────────────────────────────────────────────────────────
# kpn_sanitizer_flags() is defined in the top-level CMakeLists and is a no-op
# unless -DKPN_SANITIZER=... is set. Sanitizer must be on both compile and link.
kpn_sanitizer_flags(_kpn_san)
if(_kpn_san)
foreach(_t kpn_tests kpn_tests_stress)
target_compile_options(${_t} PRIVATE ${_kpn_san})
target_link_options(${_t} PRIVATE ${_kpn_san})
endforeach()
endif()
include(CTest)
include(Catch)
catch_discover_tests(kpn_tests)
# Register the stress suite under its own label so CI can run / time it
# separately from the fast unit tests.
catch_discover_tests(kpn_tests_stress PROPERTIES LABELS "stress")