From a94e05a4712b45cb04467f480983113c4f7372b0 Mon Sep 17 00:00:00 2001 From: Ambroise Leclerc Date: Fri, 16 Oct 2026 09:35:00 +0200 Subject: [PATCH 1/4] =?UTF-8?q?Masquer=20la=20d=C3=A9pr=C3=A9ciation=20des?= =?UTF-8?q?=20atomiques=20shared=5Fptr=20aussi=20pour=20Clang=20[#150]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - SinkRegistry : loadPublished/storePublished ajoutent une branche #pragma clang diagnostic, testée avant __GNUC__ que Clang définit aussi. La dépréciation vient de la bibliothèque standard : Clang la signale avec libstdc++ (défaut sous Linux) comme avec la STL MSVC (clang-cl). - Les branches GCC et MSVC n'ont plus besoin d'exclure __clang__. - CHANGELOG : entrée sous Unreleased. Vérifié localement : Clang 20.1.8 + libstdc++ (GCC 16.1), GCC 16.1 et Clang 21 + libc++, avertissements traités comme erreurs, CTest complet. --- CHANGELOG.md | 5 ++++ include/mddlog/adapter/SinkRegistry.cppm | 32 ++++++++++++++++-------- 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e0621b..b9fcb64 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,11 @@ is a certification, a validation for medical-device use, or a production-readine ## Unreleased +- Fix `mddlog::mddlog` failing to compile with Clang on libstdc++ (Clang's Linux default) under + warnings-as-errors: `SinkRegistry`'s deliberate use of the deprecated `shared_ptr` atomic free + functions is now silenced for Clang too, including clang-cl on MSVC's STL. The core was not + affected (#150). + - Propose the 1.0 compatibility commitments in [ADR-007](docs/adr/ADR-007-compatibility-and-distribution.md): surface tiers recorded in a checked [inventory](docs/api/public-surface.json), SemVer and deprecation without an ABI/BMI diff --git a/include/mddlog/adapter/SinkRegistry.cppm b/include/mddlog/adapter/SinkRegistry.cppm index faf3c00..b96bbde 100644 --- a/include/mddlog/adapter/SinkRegistry.cppm +++ b/include/mddlog/adapter/SinkRegistry.cppm @@ -498,36 +498,48 @@ private: // included - must go through them; a plain load/store on a shared_ptr is not thread-safe. // GCC 16.1's libstdc++ and MSVC's STL *do* have that specialization and deprecate these free // functions in favor of it; keeping them anyway is intentional portability, not an oversight, - // so the two helpers below silence that one diagnostic on those toolchains rather than letting + // so the two helpers below silence that one diagnostic on those libraries rather than letting // warnings-as-errors block a build whose real fix (migrating to std::atomic>) - // would drop libc++ 21 support. + // would drop libc++ 21 support. The deprecation belongs to the standard library, not the + // compiler: Clang reports it too when it uses libstdc++ (its Linux default) or MSVC's STL + // (clang-cl), so Clang gets its own branch, tested first because it also defines __GNUC__. [[nodiscard]] static std::shared_ptr loadPublished(const std::shared_ptr& target, std::memory_order order) noexcept { -#if defined(__GNUC__) && !defined(__clang__) +#if defined(__clang__) + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wdeprecated-declarations" +#elif defined(__GNUC__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" -#elif defined(_MSC_VER) && !defined(__clang__) +#elif defined(_MSC_VER) #pragma warning(push) #pragma warning(disable : 4996) #endif return std::atomic_load_explicit(&target, order); -#if defined(__GNUC__) && !defined(__clang__) +#if defined(__clang__) + #pragma clang diagnostic pop +#elif defined(__GNUC__) #pragma GCC diagnostic pop -#elif defined(_MSC_VER) && !defined(__clang__) +#elif defined(_MSC_VER) #pragma warning(pop) #endif } static void storePublished(std::shared_ptr& target, std::shared_ptr value, std::memory_order order) noexcept { -#if defined(__GNUC__) && !defined(__clang__) +#if defined(__clang__) + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wdeprecated-declarations" +#elif defined(__GNUC__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" -#elif defined(_MSC_VER) && !defined(__clang__) +#elif defined(_MSC_VER) #pragma warning(push) #pragma warning(disable : 4996) #endif std::atomic_store_explicit(&target, std::move(value), order); -#if defined(__GNUC__) && !defined(__clang__) +#if defined(__clang__) + #pragma clang diagnostic pop +#elif defined(__GNUC__) #pragma GCC diagnostic pop -#elif defined(_MSC_VER) && !defined(__clang__) +#elif defined(_MSC_VER) #pragma warning(pop) #endif } From 7f070f8b4f8c0710e60561ac4cd7c6214841d531 Mon Sep 17 00:00:00 2001 From: Ambroise Leclerc Date: Fri, 16 Oct 2026 21:52:07 +0200 Subject: [PATCH 2/4] =?UTF-8?q?Simplifier=20les=20directives=20du=20pr?= =?UTF-8?q?=C3=A9processeur=20pour=20clang-tidy=20[#150]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/mddlog/adapter/SinkRegistry.cppm | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/include/mddlog/adapter/SinkRegistry.cppm b/include/mddlog/adapter/SinkRegistry.cppm index b96bbde..7ad322b 100644 --- a/include/mddlog/adapter/SinkRegistry.cppm +++ b/include/mddlog/adapter/SinkRegistry.cppm @@ -504,42 +504,42 @@ private: // compiler: Clang reports it too when it uses libstdc++ (its Linux default) or MSVC's STL // (clang-cl), so Clang gets its own branch, tested first because it also defines __GNUC__. [[nodiscard]] static std::shared_ptr loadPublished(const std::shared_ptr& target, std::memory_order order) noexcept { -#if defined(__clang__) +#ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" -#elif defined(__GNUC__) +#elifdef __GNUC__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" -#elif defined(_MSC_VER) +#elifdef _MSC_VER #pragma warning(push) #pragma warning(disable : 4996) #endif return std::atomic_load_explicit(&target, order); -#if defined(__clang__) +#ifdef __clang__ #pragma clang diagnostic pop -#elif defined(__GNUC__) +#elifdef __GNUC__ #pragma GCC diagnostic pop -#elif defined(_MSC_VER) +#elifdef _MSC_VER #pragma warning(pop) #endif } static void storePublished(std::shared_ptr& target, std::shared_ptr value, std::memory_order order) noexcept { -#if defined(__clang__) +#ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" -#elif defined(__GNUC__) +#elifdef __GNUC__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" -#elif defined(_MSC_VER) +#elifdef _MSC_VER #pragma warning(push) #pragma warning(disable : 4996) #endif std::atomic_store_explicit(&target, std::move(value), order); -#if defined(__clang__) +#ifdef __clang__ #pragma clang diagnostic pop -#elif defined(__GNUC__) +#elifdef __GNUC__ #pragma GCC diagnostic pop -#elif defined(_MSC_VER) +#elifdef _MSC_VER #pragma warning(pop) #endif } From 76fc3ba375f29c8efebfa7e0e5a2759fa2b79ef6 Mon Sep 17 00:00:00 2001 From: Ambroise Leclerc Date: Tue, 20 Oct 2026 08:50:40 +0200 Subject: [PATCH 3/4] =?UTF-8?q?Couvrir=20Clang=2020/libstdc++=20et=20stabi?= =?UTF-8?q?liser=20le=20sc=C3=A9nario=20concurrent=20Windows=20[#150]=20[#?= =?UTF-8?q?151]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/clang-build.yml | 70 ++++++++++++++++++- .github/workflows/windows-build.yml | 4 ++ CHANGELOG.md | 4 ++ CMakePresets.json | 21 ++++++ README.md | 1 + .../toolchains/linux-clang20-libstdcxx.cmake | 40 +++++++++++ cmake/toolchains/linux-clang21-libcxx.cmake | 4 +- docs/compatibility-matrix.md | 9 ++- docs/contextual-api-integration.md | 14 +++- examples/ContextualInventory.cpp | 60 +++++++++------- software_development_file/register.json | 4 +- 11 files changed, 194 insertions(+), 37 deletions(-) create mode 100644 cmake/toolchains/linux-clang20-libstdcxx.cmake diff --git a/.github/workflows/clang-build.yml b/.github/workflows/clang-build.yml index 357ffa6..7e52470 100644 --- a/.github/workflows/clang-build.yml +++ b/.github/workflows/clang-build.yml @@ -19,10 +19,74 @@ concurrency: permissions: contents: read -# Three independent jobs instead of one sequential one: the Release build and its tests, the -# clang-tidy analysis (sharded), and the ASan/UBSan Debug configuration. Each sets up the same -# toolchain through .github/actions/setup-clang21, so they cannot drift apart. +# The reference build, analysis and sanitizers share setup-clang21. A separate job exercises +# the admitted Clang 20 floor with GCC 16.1's libstdc++ instead of libc++ (#150/#151). jobs: + clang20-libstdcxx: + name: Linux (Clang 20, libstdc++ GCC 16.1) + runs-on: ubuntu-latest + container: gcc:16.1.0 + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + persist-credentials: false + + - name: Install Clang 20 and host tools + run: | + set -eu + apt-get update + apt-get install -y ca-certificates curl gnupg ninja-build python3 + curl -fsSL https://apt.llvm.org/llvm-snapshot.gpg.key -o /tmp/llvm-snapshot.asc + gpg --show-keys --with-colons --fingerprint /tmp/llvm-snapshot.asc \ + | awk -F: '/^fpr:/ {print $10}' | grep -qx '6084F3CF814B57C1CF12EFD515CF4D18AF4F7421' + install -d -m 0755 /etc/apt/keyrings + gpg --dearmor < /tmp/llvm-snapshot.asc > /etc/apt/keyrings/llvm.gpg + codename="$(. /etc/os-release && echo "$VERSION_CODENAME")" + echo "deb [signed-by=/etc/apt/keyrings/llvm.gpg] https://apt.llvm.org/${codename}/ llvm-toolchain-${codename}-20 main" \ + > /etc/apt/sources.list.d/llvm20.list + apt-get update + apt-get install -y clang-20 clang-tools-20 llvm-20 + git config --global --add safe.directory "$GITHUB_WORKSPACE" + + - name: Setup pinned CMake + uses: jwlawson/actions-setup-cmake@v2 + with: + cmake-version: '4.3.1' + + - name: Verify Clang and selected standard library + run: | + set -eu + test "$(g++ -dumpfullversion)" = '16.1.0' + clang++-20 --version + clang++-20 --version | grep -E 'clang version 20\.' + printf '#include \nstatic_assert(_GLIBCXX_RELEASE == 16);\n' \ + | clang++-20 --gcc-toolchain=/usr/local -stdlib=libstdc++ -std=c++23 -x c++ -fsyntax-only - + cmake --version + ninja --version + + - name: Configure CMake with warnings as errors + run: cmake --preset ninja-clang20-libstdcxx + + - name: Build both libraries, examples and tests + run: cmake --build --preset ninja-clang20-libstdcxx + + - name: Verify governed boundary and negative controls + run: ctest --preset ninja-clang20-libstdcxx -L governed --verbose --no-tests=error --timeout 120 + + - name: Run full suite and independent consumers + run: ctest --preset ninja-clang20-libstdcxx -LE governed --parallel --output-on-failure --no-tests=error --timeout 120 + + - name: Verify no source-tree writes + run: | + status="$(git status --porcelain)" + if [ -n "$status" ]; then + echo "Build wrote into the source tree:" + echo "$status" + exit 1 + fi + clang-build: name: Linux (Clang 21, libc++) runs-on: ubuntu-latest diff --git a/.github/workflows/windows-build.yml b/.github/workflows/windows-build.yml index 21e4aa0..376ac64 100644 --- a/.github/workflows/windows-build.yml +++ b/.github/workflows/windows-build.yml @@ -79,6 +79,10 @@ jobs: run: ctest --preset ninja-msvc -LE governed --parallel --output-on-failure --no-tests=error --timeout 120 shell: cmd + - name: Repeat producer and consumer lifecycle + run: ctest --preset ninja-msvc -R "^examples\.contextualInventory\.lifecycle$" --repeat until-fail:20 --output-on-failure --no-tests=error --timeout 60 + shell: cmd + - name: Verify no source-tree writes shell: pwsh run: | diff --git a/CHANGELOG.md b/CHANGELOG.md index b9fcb64..521c5dd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,10 @@ is a certification, a validation for medical-device use, or a production-readine warnings-as-errors: `SinkRegistry`'s deliberate use of the deprecated `shared_ptr` atomic free functions is now silenced for Clang too, including clang-cl on MSVC's STL. The core was not affected (#150). +- Exercise Clang 20 with GCC 16.1's libstdc++ in CI under warnings-as-errors, including the full + suite and independent package consumers (#151). The inventory lifecycle example now uses + bounded producer/consumer handoffs instead of an aggregate throughput deadline; Windows CI + repeats the scenario twenty times (#150). - Propose the 1.0 compatibility commitments in [ADR-007](docs/adr/ADR-007-compatibility-and-distribution.md): surface tiers recorded in a diff --git a/CMakePresets.json b/CMakePresets.json index 4c143c6..abff66c 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -90,6 +90,25 @@ "MDDLOG_BUILD_TESTS": "ON" } }, + { + "name": "ninja-clang20-libstdcxx", + "displayName": "Ninja with Clang 20 and libstdc++", + "description": "Clang 20 with GCC 16.1 libstdc++, warnings as errors, examples and full tests; clang-build.yml (#151).", + "generator": "Ninja", + "binaryDir": "build-clang20-libstdcxx", + "toolchainFile": "${sourceDir}/cmake/toolchains/linux-clang20-libstdcxx.cmake", + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Linux" + }, + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release", + "WARNINGS_AS_ERRORS": "ON", + "MDDLOG_BUILD_EXAMPLES": "ON", + "MDDLOG_BUILD_TESTS": "ON" + } + }, { "name": "ninja-clang-debug", "displayName": "Ninja with Clang (Debug)", @@ -135,6 +154,7 @@ { "name": "ninja-gcc", "configurePreset": "ninja-gcc" }, { "name": "ninja-gcc-debug", "configurePreset": "ninja-gcc-debug" }, { "name": "ninja-clang", "configurePreset": "ninja-clang" }, + { "name": "ninja-clang20-libstdcxx", "configurePreset": "ninja-clang20-libstdcxx" }, { "name": "ninja-clang-debug", "configurePreset": "ninja-clang-debug" }, { "name": "ninja-macos-clang", "configurePreset": "ninja-macos-clang" }, { "name": "ninja-macos-clang-debug", "configurePreset": "ninja-macos-clang-debug" } @@ -145,6 +165,7 @@ { "name": "ninja-gcc", "configurePreset": "ninja-gcc", "output": { "outputOnFailure": true } }, { "name": "ninja-gcc-debug", "configurePreset": "ninja-gcc-debug", "output": { "outputOnFailure": true } }, { "name": "ninja-clang", "configurePreset": "ninja-clang", "output": { "outputOnFailure": true } }, + { "name": "ninja-clang20-libstdcxx", "configurePreset": "ninja-clang20-libstdcxx", "output": { "outputOnFailure": true } }, { "name": "ninja-clang-debug", "configurePreset": "ninja-clang-debug", "output": { "outputOnFailure": true } }, { "name": "ninja-macos-clang", "configurePreset": "ninja-macos-clang", "output": { "outputOnFailure": true } }, { "name": "ninja-macos-clang-debug", "configurePreset": "ninja-macos-clang-debug", "output": { "outputOnFailure": true } } diff --git a/README.md b/README.md index b0d2451..9878c3f 100644 --- a/README.md +++ b/README.md @@ -232,6 +232,7 @@ Configurations de la CI ([matrice détaillée](docs/compatibility-matrix.md)) : | --- | --- | --- | | Windows x64 | MSVC 19.40+ | 4.1.1 | | Linux x86_64 | GCC 16.1.0 / libstdc++ | 4.1.1 | +| Linux x86_64 | Clang 20 amont / libstdc++ de GCC 16.1.0 | 4.3.1 | | Linux x86_64 | Clang 21 amont / libc++ | 4.3.1 | | macOS arm64 | Clang 21.1.8 amont / libc++ | 4.3.1 | diff --git a/cmake/toolchains/linux-clang20-libstdcxx.cmake b/cmake/toolchains/linux-clang20-libstdcxx.cmake new file mode 100644 index 0000000..a49d879 --- /dev/null +++ b/cmake/toolchains/linux-clang20-libstdcxx.cmake @@ -0,0 +1,40 @@ +# Clang's admitted floor with GCC 16.1's libstdc++, exercised by clang-build.yml (#151). +if(NOT CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux") + message(FATAL_ERROR "The linux-clang20-libstdcxx toolchain is only valid on Linux hosts.") +endif() + +set(_mddlog_llvm_root "$ENV{MDDLOG_LLVM_ROOT}") +if(NOT _mddlog_llvm_root) + set(_mddlog_llvm_root "/usr/lib/llvm-20") +endif() +set(_mddlog_gcc_root "$ENV{MDDLOG_GCC_ROOT}") +if(NOT _mddlog_gcc_root) + # The gcc:16.1.0 container installs its compiler and standard library here. + set(_mddlog_gcc_root "/usr/local") +endif() + +set(CMAKE_CXX_COMPILER "${_mddlog_llvm_root}/bin/clang++" CACHE FILEPATH "" FORCE) +set(CMAKE_AR "${_mddlog_llvm_root}/bin/llvm-ar" CACHE FILEPATH "" FORCE) +set(CMAKE_RANLIB "${_mddlog_llvm_root}/bin/llvm-ranlib" CACHE FILEPATH "" FORCE) +set(CMAKE_CXX_FLAGS_INIT "--gcc-toolchain=${_mddlog_gcc_root} -stdlib=libstdc++") + +# Clang need not discover a separately installed GCC's module manifest on its own. +execute_process( + COMMAND "${_mddlog_gcc_root}/bin/g++" -print-file-name=libstdc++.modules.json + RESULT_VARIABLE _mddlog_manifest_result + OUTPUT_VARIABLE _mddlog_manifest + OUTPUT_STRIP_TRAILING_WHITESPACE +) +if(NOT _mddlog_manifest_result EQUAL 0 OR NOT EXISTS "${_mddlog_manifest}") + message(FATAL_ERROR "No libstdc++.modules.json from GCC under '${_mddlog_gcc_root}'.") +endif() +set(CMAKE_CXX_STDLIB_MODULES_JSON "${_mddlog_manifest}" CACHE FILEPATH "" FORCE) + +foreach(_mddlog_required_tool + "${CMAKE_CXX_COMPILER}" + "${CMAKE_AR}" + "${CMAKE_RANLIB}") + if(NOT EXISTS "${_mddlog_required_tool}") + message(FATAL_ERROR "Required Clang 20 tool is missing: ${_mddlog_required_tool}") + endif() +endforeach() diff --git a/cmake/toolchains/linux-clang21-libcxx.cmake b/cmake/toolchains/linux-clang21-libcxx.cmake index b795348..ecef525 100644 --- a/cmake/toolchains/linux-clang21-libcxx.cmake +++ b/cmake/toolchains/linux-clang21-libcxx.cmake @@ -23,8 +23,8 @@ set(CMAKE_CXX_COMPILER "${_mddlog_llvm_root}/bin/clang++" CACHE FILEPATH "" FORC set(CMAKE_AR "${_mddlog_llvm_root}/bin/llvm-ar" CACHE FILEPATH "" FORCE) set(CMAKE_RANLIB "${_mddlog_llvm_root}/bin/llvm-ranlib" CACHE FILEPATH "" FORCE) -# libc++ rather than libstdc++: the `std` module is shipped by the standard library, and only -# libc++ ships one this project builds against on Linux/Clang. +# Select libc++ for the reference Clang profile; linux-clang20-libstdcxx.cmake exercises +# Clang's admitted floor with GCC's separate standard library and module manifest. set(CMAKE_CXX_FLAGS_INIT "-stdlib=libc++") # Where Debian/Ubuntu put `libc++.modules.json` is not fixed across LLVM packagings, so search the diff --git a/docs/compatibility-matrix.md b/docs/compatibility-matrix.md index 438532f..615f1a8 100644 --- a/docs/compatibility-matrix.md +++ b/docs/compatibility-matrix.md @@ -11,17 +11,24 @@ candidate jusqu’au gel 1.0 après #122. | Plateforme | Compilateur / bibliothèque standard | CMake | Configurations | Consommateurs indépendants | Épinglage | | --- | --- | --- | --- | --- | --- | | Linux x86_64 | GCC 16.1.0 / libstdc++ (conteneur `gcc:16.1.0`) | 4.1.1 | Release (suite complète) ; Debug ASan/UBSan et TSan (`sanitizers.yml`) | installés complet et cœur, sous-répertoire, archive | exact | +| Linux x86_64 | Clang 20 amont / libstdc++ de GCC 16.1.0 (conteneur `gcc:16.1.0`) | 4.3.1 | Release, avertissements en erreurs, exemples et suite complète (`clang-build.yml`) | installés complet et cœur, sous-répertoire, archive | majeure Clang 20 contrôlée, GCC exact | | Linux x86_64 — **profil de référence** | Clang 21 amont / libc++ (apt.llvm.org) | 4.3.1 | Release avec outil CLI ; Debug ASan/UBSan ; fuzzing ; campagnes | installés complet et cœur, sous-répertoire, archive | majeure 21 contrôlée, correctif flottant | | macOS 15 arm64 | Clang 21.1.8 amont / libc++ (Homebrew `llvm@21`) | 4.3.1 | Release ; Debug ASan/UBSan | installés complet et cœur, sous-répertoire, archive | exact (refus sinon) | | Windows x64 | MSVC de l’image `windows-latest` | 4.1.1 | Release | installés complet et cœur, sous-répertoire, archive | version de l’image flottante | -Les deux combinaisons à correctif ou image flottants sont **exercées**, non figées. Avant le +Les combinaisons à correctif ou image flottants sont **exercées**, non figées. Avant le gel 1.0, il faut soit épingler leur version exacte, soit conserver par exécution le `mddlog-build-info.json` du paquet testé, que `InstallTreeConsumer` imprime. Le signalement TSan de Clang/libc++ ([#147](https://github.com/ambroise-leclerc/mddlog/issues/147), [source conservée](validation/audit-robustness/tsan-libcxx/README.md)) reste ouvert : ce tuple n’est pas qualifié pour TSan ; le TSan qualifié est GCC/libstdc++. +Le preset `ninja-clang20-libstdcxx` couvre le plancher Clang et la bibliothèque standard +qui déclenchait #150. Il sélectionne explicitement GCC 16.1 et son manifeste de modules ; +`MDDLOG_LLVM_ROOT` et `MDDLOG_GCC_ROOT` permettent d'indiquer d'autres racines d'installation. +Le job vérifie aussi `_GLIBCXX_RELEASE == 16` avant de construire. L'ajout du job ne constitue +une preuve pour une révision qu'après sa configuration, sa construction et ses tests réussis. + Le profil de référence est celui qui active tout : adaptateurs fichiers Linux, outil `mddlog-audit`, sanitizers, fuzzing et campagnes de robustesse. Il ne remplace pas le profil de déploiement réel, qui relève de #122. diff --git a/docs/contextual-api-integration.md b/docs/contextual-api-integration.md index 59a5b33..a441ffa 100644 --- a/docs/contextual-api-integration.md +++ b/docs/contextual-api-integration.md @@ -99,9 +99,10 @@ Le scénario `lifecycle` crée deux composants, chacun avec un anneau diagnostic audit de capacité 4, et des identités de flux distinctes. Des chaînes temporaires alimentent les factories avant le démarrage ; les liaisons et composants sont copiés dans les producteurs. Deux `jthread` produisent pendant qu'un seul consommateur d'application draine les quatre -anneaux. Un acquittement atomique par producteur organise la prochaine commande à l'extérieur -du métier ; la méthode d'émission n'attend pas. Le délai de garde arrête l'essai en cas de défaut. -Les tokens d'arrêt permettent aussi aux producteurs de sortir pendant une exception de l'hôte. +anneaux. Deux sémaphores par producteur signalent la publication complète et son acquittement +à l'extérieur du métier ; la méthode d'émission n'attend pas. Chaque attente a un délai de garde +de dix secondes. Les tokens d'arrêt empêchent une nouvelle commande pendant une exception +de l'hôte ; une attente déjà engagée reste bornée par son délai. 200 mutations par producteur donnent 400 diagnostics et 800 événements d'audit. Les multiples réutilisations des slots vérifient les copies avant acquittement, les séquences propres à @@ -111,6 +112,13 @@ liaisons et destinations sortent de portée dans cet ordre. Les copies conservé consommateur sont examinées après réutilisation des slots ; aucune vue acquittée n'est utilisée. Ce scénario exerce une ordonnance particulière et ne remplace pas #120. +Suivi de PR #153 (#150/#151) : le job Windows avait échoué après 10,60 s dans `lifecycle`. +La boucle d'attente active avec `yield()` et le délai global de dix secondes sont remplacés +par les rendez-vous ci-dessus : le test ne fixe plus un débit minimal de 200 échanges sous +l'ordonnanceur de l'hôte. Les contrôles de comptage, de phases, de liens et de copies restent +actifs. Le job Windows répète désormais ce scénario vingt fois. Cette correction ultérieure +ne modifie ni les révisions ni les résultats des campagnes historiques ci-dessous. + ## Critères C de #113 | Critère | Preuve et limite | diff --git a/examples/ContextualInventory.cpp b/examples/ContextualInventory.cpp index bd8290d..83026c4 100644 --- a/examples/ContextualInventory.cpp +++ b/examples/ContextualInventory.cpp @@ -118,66 +118,74 @@ void refusals() { void concurrentLifecycle() { constexpr int iterations = 200; // One diagnostic and one audit ring per producer, owned before any copied binding or thread. - RingLog<4> firstLog; - RingLog<4> secondLog; - AuditRing<4> firstAudit("inventory-first:boot-4"); - AuditRing<4> secondAudit("inventory-second:boot-4"); - InventoryWorker first(GovernedBinding(firstLog, diagnosticContext(std::string("first"))), + RingLog<4> firstLog; + RingLog<4> secondLog; + AuditRing<4> firstAudit("inventory-first:boot-4"); + AuditRing<4> secondAudit("inventory-second:boot-4"); + InventoryWorker first(GovernedBinding(firstLog, diagnosticContext(std::string("first"))), AuditBinding(firstAudit, description(), auditContext(std::string("first")))); - InventoryWorker second(GovernedBinding(secondLog, diagnosticContext(std::string("second"))), + InventoryWorker second(GovernedBinding(secondLog, diagnosticContext(std::string("second"))), AuditBinding(secondAudit, description(), auditContext(std::string("second")))); - std::atomic firstAcknowledged{0}; - std::atomic secondAcknowledged{0}; + struct Handoff { + std::binary_semaphore ready{0}; + std::binary_semaphore acknowledged{0}; + }; + Handoff firstHandoff; + Handoff secondHandoff; std::atomic stop{false}; std::atomic healthy{true}; - auto produce = [&](auto worker, std::atomic& acknowledged, const std::stop_token& cancellation) { + auto produce = [&](auto worker, Handoff& handoff, const std::stop_token& cancellation) { for (int i = 0; i < iterations && !stop.load() && !cancellation.stop_requested(); ++i) { const auto result = worker.apply(1, RawTime::unavailable(), RawTime::unavailable()); if (!result.changed || !result.outcome || !result.outcome->wasAdmitted() || !result.diagnostic || result.diagnostic->admission() != Admission::Written) healthy.store(false); // Application scheduling waits for the consumer; the logging API itself never waits. - while (acknowledged.load() <= i && !stop.load() && !cancellation.stop_requested()) - std::this_thread::yield(); + handoff.ready.release(); + if (!handoff.acknowledged.try_acquire_for(std::chrono::seconds(10))) { + stop.store(true); + break; + } } if (!stop.load() && worker.quantity() != iterations) healthy.store(false); }; std::jthread firstProducer([&](const std::stop_token& cancellation) { - produce(first, firstAcknowledged, cancellation); + produce(first, firstHandoff, cancellation); }); std::jthread secondProducer([&](const std::stop_token& cancellation) { - produce(second, secondAcknowledged, cancellation); + produce(second, secondHandoff, cancellation); }); std::vector diagnostics; std::vector events; - auto consume = [&](auto& log, auto& audit, std::atomic& acknowledged) { + auto consume = [&](auto& log, auto& audit, Handoff& handoff) { + if (!handoff.ready.try_acquire_for(std::chrono::seconds(10))) { + stop.store(true); + return; + } const auto diagnosticView = log.drain(); const auto auditView = audit.drain(); if (diagnosticView.size() != 1 || auditView.size() != 2) - return; + healthy.store(false); for (const auto span : {diagnosticView.first(), diagnosticView.second()}) diagnostics.insert(diagnostics.end(), span.begin(), span.end()); for (const auto span : {auditView.first(), auditView.second()}) events.insert(events.end(), span.begin(), span.end()); if (!log.acknowledge(diagnosticView, diagnosticView.size()) || !audit.acknowledge(auditView, auditView.size())) healthy.store(false); - acknowledged.fetch_add(1); + handoff.acknowledged.release(); }; - const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(10); - while (firstAcknowledged.load() < iterations || secondAcknowledged.load() < iterations) { - consume(firstLog, firstAudit, firstAcknowledged); - consume(secondLog, secondAudit, secondAcknowledged); - if (std::chrono::steady_clock::now() >= deadline) { - stop.store(true); - break; - } - std::this_thread::yield(); + // Bound each stalled handoff, not the total throughput of both scheduled producer threads. + for (int i = 0; i < iterations && !stop.load(); ++i) { + consume(firstLog, firstAudit, firstHandoff); + if (!stop.load()) + consume(secondLog, secondAudit, secondHandoff); } // Quiesce producers, finish consumers, discard bindings, then destroy rings. Copies survive ack. firstProducer.join(); secondProducer.join(); - require(!stop.load() && healthy.load() && diagnostics.size() == 400 && events.size() == 800, "concurrent admission, wrap-around and shutdown"); + require(!stop.load(), "producer/consumer handoff timed out"); + require(healthy.load() && diagnostics.size() == 400 && events.size() == 800, "concurrent admission, wrap-around and shutdown"); std::uint64_t firstSequence = 0; std::uint64_t secondSequence = 0; for (const auto& value : events) { diff --git a/software_development_file/register.json b/software_development_file/register.json index ee00e05..0e7c71c 100644 --- a/software_development_file/register.json +++ b/software_development_file/register.json @@ -2132,7 +2132,7 @@ "provenance": "CMakeLists.txt", "license": "GCC GPL avec exceptions runtime ; LLVM Apache-2.0 avec exceptions ; MSVC propriétaire. À relever pour chaque installation.", "classification": "Outils de construction/vérification, non SOUP déployé sauf runtimes DEP-001.", - "knownIssues": "GCC 16.2 exclu : corruption BMI ; AppleClang exclu ; import std expérimental.", + "knownIssues": "GCC 16.2 exclu : corruption BMI ; AppleClang exclu ; import std expérimental. PR #153 ajoute le job Clang 20/libstdc++ de GCC 16.1.0 (#151), suite complète et consommateurs ; preuve par révision après CI réussie (docs/compatibility-matrix.md).", "monitoring": "Plan : mainteneur examine avant mise à jour et release les défauts modules GCC dans https://gcc.gnu.org/bugzilla/ (accès automatisé bloqué le 2026-10-04), les anomalies LLVM dans https://github.com/llvm/llvm-project/issues et avis dans https://github.com/llvm/llvm-project/security/advisories, ainsi que https://msrc.microsoft.com/update-guide pour MSVC. Filtrer compilateur/analyseur exact et tester BMIs, consommateurs et frontière ; exclusions restent dans CMake.", "gaps": [ "GAP-005" @@ -2146,7 +2146,7 @@ "provenance": "CMakePresets.json", "license": "CMake BSD-3-Clause ; Ninja Apache-2.0 ; distribution exacte à confirmer GAP-005.", "classification": "Outils de génération/construction, non déployés.", - "knownIssues": "Gate expérimental import std dépendant de version CMake. Avec Clang, CMake passe un -c redondant à la précompilation des BMIs (avertissement Clang) ; le module std est créé à l’activation de CXX, d’où CMAKE_CXX_EXTENSIONS OFF avant project() côté consommateur (docs/consumer-requirements.md).", + "knownIssues": "Clang 20/libstdc++ utilise CMake 4.3.1 et le manifeste du GCC sélectionné (preset ninja-clang20-libstdcxx, #151). Gate expérimental import std dépendant de version CMake. Avec Clang, CMake passe un -c redondant à la précompilation des BMIs (avertissement Clang) ; le module std est créé à l’activation de CXX, d’où CMAKE_CXX_EXTENSIONS OFF avant project() côté consommateur (docs/consumer-requirements.md).", "monitoring": "Plan : mainteneur examine avant mise à jour et release https://cmake.org/cmake/help/latest/release/index.html pour les changements import std/FILE_SET et https://github.com/ninja-build/ninja/releases pour le scanning/modules ; vérifier le gate CMake, la génération et les consommateurs sur le tuple retenu.", "gaps": [ "GAP-005" From 85e8aa0960a1a36b241382e8b08b3b4e4c5fc543 Mon Sep 17 00:00:00 2001 From: Ambroise Leclerc Date: Tue, 20 Oct 2026 09:23:10 +0200 Subject: [PATCH 4/4] =?UTF-8?q?Actualiser=20le=20texte=20de=20la=20matrice?= =?UTF-8?q?=20=C3=A9tendue=20[#151]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/compatibility-matrix.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/compatibility-matrix.md b/docs/compatibility-matrix.md index 615f1a8..11592ae 100644 --- a/docs/compatibility-matrix.md +++ b/docs/compatibility-matrix.md @@ -35,7 +35,7 @@ de déploiement réel, qui relève de #122. ## Debug, Release et options du consommateur -- La suite complète tourne en Release sur les quatre combinaisons ; Debug est exercé avec les +- La suite complète tourne en Release sur les combinaisons du tableau ; Debug est exercé avec les sanitizers sur Linux et macOS. Windows Debug n’est pas exercé en CI : preset disponible, non qualifié. - Une bibliothèque installée en Release sert un consommateur Debug avec GCC et Clang (`InstallTreeConsumer`, `InstallTreeCoreConsumer`) ; avec MSVC, même configuration.