Skip to content

Commit 92d008a

Browse files
authored
Cleanup unused bits and pieces, add ASAN and TSAN. (Geenz#11)
* Remove Tracy profiler and ServiceLocator integration * Add sanitizer options and improve graph thread safety Introduces ENABLE_SANITIZERS and ENABLE_TSAN options in CMake for easier debugging with sanitizers. NodeScheduler now takes a graph mutex and uses shared locking for safe DAG access. TimerService::stop() now exits early if the contract group is destroyed, improving robustness. * Refactor singleton implementations for thread safety * Fix advisory lock acquisition in submitSerialized Caught by TSAN in CI * Refactor WorkContractGroup ownership in TimerService Changed _workContractGroup from std::unique_ptr to raw pointer with manual reference counting. This allows WorkService to manage shared ownership and ensures proper cleanup when all references are released. * Improve WorkContractGroup removal safety The removal process now stops the group before acquiring the mutex, waits for in-flight executions to finish after removal, and ensures concurrency provider is cleared inside the lock. This prevents new work selection and ensures no worker is using the group before releasing its reference. * Refactor work group selection logic in WorkService Simplifies the worker loop by removing the intermediate snapshot of work groups and directly using a shared lock for concurrent access. The scheduling and execution flow is streamlined, improving efficiency and reducing redundant checks for stopped groups. Soft failure handling and sleep logic are also clarified for better maintainability. * Shorten the condition variable's timeouts. * Refactor submitSerialized for clearer locking logic Simplifies and clarifies the locking and fallback logic in submitSerialized. The new implementation separates backend scope acquisition, error handling, and VFS advisory locking, improving readability and maintainability. * Refactor VFS advisory lock acquisition logic Replaces std::unique_lock with direct use of try_lock_for on the shared timed_mutex and introduces a custom RAII guard to ensure proper unlocking. This simplifies lock management and clarifies the advisory lock's usage in submitSerialized. * One more crack at this before I move to Linux proper to more directly debug. * Another TSAN + ASAN pass on Linux with varying settings. * Make TSAN and ASAN + UBSAN steps non-blocking on failure, log issues instead. * Roll back to GCC-14 * More ASAN and TSAN changes. * Update VirtualFileSystem.cpp
1 parent bdc0ee0 commit 92d008a

25 files changed

+521
-798
lines changed

.github/workflows/ci.yml

Lines changed: 146 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ jobs:
6767
path: EntropyCore-Windows-x64/**
6868

6969
build-and-test-linux:
70-
name: Build and test (Linux, GCC)
70+
name: Build and test (Linux, GCC 14)
7171
runs-on: ubuntu-24.04
7272
steps:
7373
- name: Checkout
@@ -76,7 +76,13 @@ jobs:
7676
- name: Install prerequisites
7777
run: |
7878
sudo apt-get update
79-
sudo apt-get install -y ninja-build g++ cmake
79+
sudo apt-get install -y ninja-build gcc-14 g++-14 cmake
80+
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-14 100
81+
sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-14 100
82+
sudo update-alternatives --install /usr/bin/cc cc /usr/bin/gcc-14 100
83+
sudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/g++-14 100
84+
gcc --version
85+
g++ --version
8086
8187
- name: Setup vcpkg
8288
uses: lukka/run-vcpkg@v11
@@ -270,3 +276,141 @@ jobs:
270276
with:
271277
name: EntropyCore-macOS-universal
272278
path: EntropyCore-macOS-universal/**
279+
280+
sanitizer-asan-ubsan:
281+
name: Sanitizer Tests (ASAN + UBSAN, GCC 14)
282+
runs-on: ubuntu-24.04
283+
continue-on-error: true # Don't fail CI, but report issues
284+
steps:
285+
- name: Checkout
286+
uses: actions/checkout@v4
287+
288+
- name: Install prerequisites
289+
run: |
290+
sudo apt-get update
291+
sudo apt-get install -y ninja-build gcc-14 g++-14 cmake
292+
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-14 100
293+
sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-14 100
294+
sudo update-alternatives --install /usr/bin/cc cc /usr/bin/gcc-14 100
295+
sudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/g++-14 100
296+
gcc --version
297+
g++ --version
298+
299+
- name: Setup vcpkg
300+
uses: lukka/run-vcpkg@v11
301+
with:
302+
vcpkgGitCommitId: 'c59b04f668d20c7dd83f478835e8266a3cc51270'
303+
304+
- name: Configure CMake with ASAN/UBSAN
305+
run: >
306+
cmake -S . -B build-asan -G Ninja
307+
-DCMAKE_BUILD_TYPE=Debug
308+
-DENTROPY_ENABLE_TESTS=ON
309+
-DENABLE_SANITIZERS=ON
310+
-DCMAKE_TOOLCHAIN_FILE=${{ github.workspace }}/vcpkg/scripts/buildsystems/vcpkg.cmake
311+
312+
- name: Build tests with ASAN/UBSAN
313+
run: cmake --build build-asan --target EntropyCoreTests
314+
315+
- name: Run tests with ASAN/UBSAN
316+
working-directory: build-asan
317+
run: ./EntropyCoreTests --gtest_brief=1 2>&1 | tee asan-ubsan-output.log
318+
continue-on-error: true # Capture failures but don't stop
319+
env:
320+
ASAN_OPTIONS: detect_leaks=1:check_initialization_order=1:detect_stack_use_after_return=1:strict_string_checks=1:detect_invalid_pointer_pairs=2:halt_on_error=1:abort_on_error=1
321+
UBSAN_OPTIONS: halt_on_error=1:abort_on_error=1:print_stacktrace=1
322+
323+
- name: Check for ASAN/UBSAN issues
324+
if: always()
325+
working-directory: build-asan
326+
run: |
327+
echo "## ASAN/UBSAN Results" >> $GITHUB_STEP_SUMMARY
328+
if grep -q "SUMMARY.*Sanitizer" asan-ubsan-output.log 2>/dev/null; then
329+
echo "⚠️ **Issues detected!**" >> $GITHUB_STEP_SUMMARY
330+
echo "" >> $GITHUB_STEP_SUMMARY
331+
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
332+
grep -A 5 "SUMMARY.*Sanitizer" asan-ubsan-output.log | head -20 >> $GITHUB_STEP_SUMMARY
333+
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
334+
echo "" >> $GITHUB_STEP_SUMMARY
335+
echo "📋 See full logs in artifacts: \`sanitizer-asan-ubsan-results\`" >> $GITHUB_STEP_SUMMARY
336+
else
337+
echo "✅ **No issues detected**" >> $GITHUB_STEP_SUMMARY
338+
fi
339+
340+
- name: Upload ASAN/UBSAN results
341+
if: always() # Always upload, even on success
342+
uses: actions/upload-artifact@v4
343+
with:
344+
name: sanitizer-asan-ubsan-results
345+
path: |
346+
build-asan/asan-ubsan-output.log
347+
build-asan/**/*.log
348+
349+
sanitizer-tsan:
350+
name: Sanitizer Tests (TSAN, GCC 14)
351+
runs-on: ubuntu-24.04
352+
continue-on-error: true # Don't fail CI, but report issues
353+
steps:
354+
- name: Checkout
355+
uses: actions/checkout@v4
356+
357+
- name: Install prerequisites
358+
run: |
359+
sudo apt-get update
360+
sudo apt-get install -y ninja-build gcc-14 g++-14 cmake
361+
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-14 100
362+
sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-14 100
363+
sudo update-alternatives --install /usr/bin/cc cc /usr/bin/gcc-14 100
364+
sudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/g++-14 100
365+
gcc --version
366+
g++ --version
367+
368+
- name: Setup vcpkg
369+
uses: lukka/run-vcpkg@v11
370+
with:
371+
vcpkgGitCommitId: 'c59b04f668d20c7dd83f478835e8266a3cc51270'
372+
373+
- name: Configure CMake with TSAN
374+
run: >
375+
cmake -S . -B build-tsan -G Ninja
376+
-DCMAKE_BUILD_TYPE=Debug
377+
-DENTROPY_ENABLE_TESTS=ON
378+
-DENABLE_SANITIZERS=ON
379+
-DENABLE_TSAN=ON
380+
-DCMAKE_TOOLCHAIN_FILE=${{ github.workspace }}/vcpkg/scripts/buildsystems/vcpkg.cmake
381+
382+
- name: Build tests with TSAN
383+
run: cmake --build build-tsan --target EntropyCoreTests
384+
385+
- name: Run tests with TSAN
386+
working-directory: build-tsan
387+
run: ./EntropyCoreTests --gtest_brief=1 2>&1 | tee tsan-output.log
388+
continue-on-error: true # Capture failures but don't stop
389+
env:
390+
TSAN_OPTIONS: detect_deadlocks=1:second_deadlock_stack=1:history_size=7:io_sync=1:halt_on_error=1:abort_on_error=1
391+
392+
- name: Check for TSAN issues
393+
if: always()
394+
working-directory: build-tsan
395+
run: |
396+
echo "## TSAN Results" >> $GITHUB_STEP_SUMMARY
397+
if grep -q "WARNING.*ThreadSanitizer" tsan-output.log 2>/dev/null; then
398+
echo "⚠️ **Issues detected!**" >> $GITHUB_STEP_SUMMARY
399+
echo "" >> $GITHUB_STEP_SUMMARY
400+
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
401+
grep -E "(WARNING.*ThreadSanitizer|SUMMARY.*ThreadSanitizer)" tsan-output.log | head -20 >> $GITHUB_STEP_SUMMARY
402+
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
403+
echo "" >> $GITHUB_STEP_SUMMARY
404+
echo "📋 See full logs in artifacts: \`sanitizer-tsan-results\`" >> $GITHUB_STEP_SUMMARY
405+
else
406+
echo "✅ **No issues detected**" >> $GITHUB_STEP_SUMMARY
407+
fi
408+
409+
- name: Upload TSAN results
410+
if: always() # Always upload, even on success
411+
uses: actions/upload-artifact@v4
412+
with:
413+
name: sanitizer-tsan-results
414+
path: |
415+
build-tsan/tsan-output.log
416+
build-tsan/**/*.log

.github/workflows/prerelease.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,6 @@ jobs:
6969
### 📦 Available Builds
7070
7171
- **Linux GCC 14**: `EntropyCore-Linux-gcc-14.tar.gz` / `.zip`
72-
- **Linux Clang 18**: `EntropyCore-Linux-clang-18.tar.gz` / `.zip`
7372
- **macOS Universal**: `EntropyCore-macOS-universal.tar.gz` / `.zip` (Intel + Apple Silicon)
7473
- **Windows x64**: `EntropyCore-Windows-x64.tar.gz` / `.zip`
7574

.github/workflows/release.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,6 @@ jobs:
7878
Download the appropriate archive for your platform:
7979
8080
- **Linux GCC 14**: `EntropyCore-Linux-gcc-14.tar.gz` / `.zip`
81-
- **Linux Clang 18**: `EntropyCore-Linux-clang-18.tar.gz` / `.zip`
8281
- **macOS Universal**: `EntropyCore-macOS-universal.tar.gz` / `.zip` (Intel + Apple Silicon)
8382
- **Windows x64**: `EntropyCore-Windows-x64.tar.gz` / `.zip`
8483

CMakeLists.txt

Lines changed: 24 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ option(ENTROPY_ENABLE_TESTS "Enable/build tests for EntropyCore" OFF)
1515
option(ENTROPY_BUILD_EXAMPLES "Build EntropyCore example executables" OFF)
1616
# Entropy does not depend on C++ RTTI. You may optionally disable RTTI for these targets if desired.
1717
option(ENTROPY_REQUIRE_NO_RTTI "Disable C++ RTTI for Entropy targets" OFF)
18+
# Enable sanitizers for debugging and testing (OFF by default - enable explicitly in CI/testing)
19+
option(ENABLE_SANITIZERS "Enable AddressSanitizer and UndefinedBehaviorSanitizer" OFF)
1820

1921
# Default behavior: on single-config generators, enable tests in Debug/RelWithDebInfo
2022
# No build-dir heuristics; on multi-config generators we do not auto-enable by default.
@@ -31,17 +33,35 @@ set(ENTROPY_BUILD_TESTS ${ENTROPY_ENABLE_TESTS} CACHE BOOL "Build tests for Entr
3133
set(CMAKE_CXX_STANDARD 20)
3234
set(CMAKE_CXX_STANDARD_REQUIRED ON)
3335
set(CMAKE_CXX_EXTENSIONS OFF)
36+
37+
# Configure sanitizers
38+
if(ENABLE_SANITIZERS)
39+
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU")
40+
# ThreadSanitizer is mutually exclusive with AddressSanitizer
41+
# To use TSAN instead: cmake -DENABLE_TSAN=ON
42+
if(ENABLE_TSAN)
43+
set(SANITIZER_FLAGS "-fsanitize=thread -fno-omit-frame-pointer -g")
44+
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${SANITIZER_FLAGS}")
45+
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${SANITIZER_FLAGS}")
46+
message(STATUS "ThreadSanitizer (TSAN) enabled")
47+
else()
48+
set(SANITIZER_FLAGS "-fsanitize=address -fsanitize=undefined -fno-omit-frame-pointer -g")
49+
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${SANITIZER_FLAGS}")
50+
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${SANITIZER_FLAGS}")
51+
message(STATUS "Sanitizers enabled: AddressSanitizer (ASAN) and UndefinedBehaviorSanitizer (UBSAN)")
52+
endif()
53+
else()
54+
message(WARNING "Sanitizers requested but not supported by ${CMAKE_CXX_COMPILER_ID}")
55+
endif()
56+
endif()
57+
3458
#
3559
# # Configure vcpkg
3660
if(DEFINED CMAKE_TOOLCHAIN_FILE)
3761
message(STATUS "Using vcpkg toolchain: ${CMAKE_TOOLCHAIN_FILE}")
3862
endif()
3963

4064
# Find packages
41-
option(ENTROPY_WITH_TRACY "Enable Tracy profiler integration" OFF)
42-
if(ENTROPY_WITH_TRACY)
43-
find_package(Tracy CONFIG QUIET)
44-
endif()
4565
find_package(Boost REQUIRED COMPONENTS type_index)
4666
find_package(efsw CONFIG REQUIRED)
4767

@@ -91,7 +111,6 @@ set(ENTROPY_CORE_HEADERS
91111
src/Core/EntropyInterop.h
92112
src/Core/entropy_c_api.h
93113
src/EntropyCore.h
94-
src/ServiceLocator.h
95114
src/Core/EntropyService.h
96115
src/Core/EntropyServiceRegistry.h
97116
src/Core/EntropyApplication.h
@@ -105,7 +124,6 @@ set(ENTROPY_CORE_HEADERS
105124
src/Graph/AcyclicNodeHandle.h
106125
src/Debug/INamed.h
107126
src/Debug/DebugUtilities.h
108-
src/Debug/Profiling.h
109127
src/Debug/Debug.h
110128
src/Logging/LogLevel.h
111129
src/Logging/LogEntry.h
@@ -217,13 +235,6 @@ target_link_libraries(EntropyCore
217235
target_compile_definitions(EntropyCore PRIVATE ENTROPYCORE_BUILDING)
218236
# EntropyCore is always static - no ENTROPYCORE_SHARED definition needed
219237

220-
# Link Tracy if enabled and available
221-
if(ENTROPY_WITH_TRACY)
222-
if(TARGET Tracy::TracyClient)
223-
target_link_libraries(EntropyCore PUBLIC Tracy::TracyClient)
224-
endif()
225-
endif()
226-
227238
if(ENTROPY_BUILD_EXAMPLES)
228239
add_executable(WorkContractExample Examples/WorkContractExample.cpp)
229240
target_link_libraries(WorkContractExample PRIVATE EntropyCore)
@@ -307,16 +318,6 @@ if(ENTROPY_BUILD_EXAMPLES)
307318
# Custom AppDelegate example with main thread work pump
308319
add_executable(CustomAppDelegateExample Examples/CustomAppDelegateExample.cpp)
309320
target_link_libraries(CustomAppDelegateExample PRIVATE EntropyCore)
310-
311-
# Link Tracy to examples if available
312-
if(ENTROPY_WITH_TRACY)
313-
if(TARGET Tracy::TracyClient)
314-
target_link_libraries(WorkContractExample PRIVATE Tracy::TracyClient)
315-
target_link_libraries(WorkGraphYieldableExample PRIVATE Tracy::TracyClient)
316-
target_link_libraries(EntropyObjectExample PRIVATE Tracy::TracyClient)
317-
target_link_libraries(EntropyObjectHandleExample PRIVATE Tracy::TracyClient)
318-
endif()
319-
endif()
320321
endif()
321322

322323
# Platform and compiler-specific settings
@@ -361,14 +362,6 @@ else()
361362
endif()
362363

363364
# Define preprocessor macros
364-
if(ENTROPY_WITH_TRACY AND TARGET Tracy::TracyClient)
365-
target_compile_definitions(EntropyCore
366-
PUBLIC
367-
TRACY_ENABLE
368-
TRACY_ON_DEMAND
369-
)
370-
endif()
371-
372365
# Add EntropyDebug macro for Debug and RelWithDebInfo builds via generator expression
373366
target_compile_definitions(EntropyCore PUBLIC $<$<OR:$<CONFIG:Debug>,$<CONFIG:RelWithDebInfo>>:EntropyDebug>)
374367

Examples/ConcurrencyAPIExample.c

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ void reentrant_work(void* user_data) {
107107
if (status == ENTROPY_OK) {
108108
entropy_work_contract_schedule(left, &status);
109109
}
110+
entropy_work_contract_handle_destroy(left);
110111

111112
entropy_WorkContractHandle right = entropy_work_contract_group_create_contract(
112113
ctx->group, reentrant_work, right_child, ENTROPY_EXEC_ANY_THREAD, &status
@@ -115,6 +116,7 @@ void reentrant_work(void* user_data) {
115116
if (status == ENTROPY_OK) {
116117
entropy_work_contract_schedule(right, &status);
117118
}
119+
entropy_work_contract_handle_destroy(right);
118120
}
119121
} else {
120122
ENTROPY_LOG_DEBUG_CAT_F("Reentrant",
@@ -264,12 +266,15 @@ int main(void) {
264266
entropy_work_contract_schedule(root_handle, &status);
265267
ENTROPY_LOG_INFO_CAT_F("Example", "Root task scheduled - it will spawn children recursively");
266268
}
267-
}
268269

269-
// Wait for the recursive tree to complete
270-
ENTROPY_LOG_INFO_CAT_F("Example", "Waiting for re-entrant task tree to complete...");
271-
entropy_work_contract_group_wait(group, &status);
272-
ENTROPY_LOG_INFO_CAT_F("Example", "Re-entrant task tree completed");
270+
// Wait for the recursive tree to complete
271+
ENTROPY_LOG_INFO_CAT_F("Example", "Waiting for re-entrant task tree to complete...");
272+
entropy_work_contract_group_wait(group, &status);
273+
ENTROPY_LOG_INFO_CAT_F("Example", "Re-entrant task tree completed");
274+
275+
// Destroy the root handle
276+
entropy_work_contract_handle_destroy(root_handle);
277+
}
273278

274279
// 9. Print statistics
275280
ENTROPY_LOG_INFO_CAT_F("Example", "Step 9: Final statistics:");
@@ -287,6 +292,15 @@ int main(void) {
287292
entropy_work_service_stop(service, &status);
288293
ENTROPY_LOG_DEBUG_CAT_F("Example", "Service stopped");
289294

295+
// Destroy handle wrappers
296+
ENTROPY_LOG_DEBUG_CAT_F("Example", "Destroying work contract handles...");
297+
for (int i = 0; i < 10; i++) {
298+
entropy_work_contract_handle_destroy(handles[i]);
299+
}
300+
for (int i = 0; i < 3; i++) {
301+
entropy_work_contract_handle_destroy(main_handles[i]);
302+
}
303+
290304
entropy_work_service_destroy(service);
291305
ENTROPY_LOG_DEBUG_CAT_F("Example", "Service destroyed");
292306

Tests/TestWorkServiceSingleton.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,9 @@ class TestWorkServiceSingleton {
5050
TestWorkServiceSingleton& operator=(const TestWorkServiceSingleton&) = delete;
5151
};
5252

53-
// Static member definitions
54-
std::unique_ptr<Concurrency::WorkService> TestWorkServiceSingleton::instance = nullptr;
55-
std::mutex TestWorkServiceSingleton::mutex;
53+
// Static member definitions - marked inline to avoid ODR violations in header
54+
inline std::unique_ptr<Concurrency::WorkService> TestWorkServiceSingleton::instance = nullptr;
55+
inline std::mutex TestWorkServiceSingleton::mutex;
5656

5757
} // namespace Testing
5858
} // namespace Core

cmake/EntropyCoreConfig.cmake.in

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,6 @@ include(CMakeFindDependencyMacro)
66
find_dependency(Boost REQUIRED COMPONENTS type_index)
77
find_dependency(efsw CONFIG REQUIRED)
88

9-
# Find optional dependencies based on how the package was built
10-
set(ENTROPY_WITH_TRACY @ENTROPY_WITH_TRACY@)
11-
if(ENTROPY_WITH_TRACY)
12-
find_dependency(Tracy CONFIG REQUIRED)
13-
endif()
14-
159
# Include the exported targets
1610
include("${CMAKE_CURRENT_LIST_DIR}/EntropyCoreTargets.cmake")
1711

0 commit comments

Comments
 (0)