From fead877f182112d1754fe737e04d0982fd3977e1 Mon Sep 17 00:00:00 2001 From: zacala Date: Thu, 22 Oct 2026 15:30:50 +0000 Subject: [PATCH 1/3] Target net10.0 as well as net8.0 .NET 8 support ends on 2026-11-10. The library and its tests are multi-targeted (net8.0;net10.0), and CI installs both SDKs, runs the blocking tests once per framework (a failure of the first does not hide the second) and builds the package, which then contains both builds. The test worker stays on net8.0, so a net10.0 test run also has a net10.0 and a net8.0 process share a region. Benchmarks stay on net8.0. --- .github/workflows/ci.yml | 27 ++++++++++++++----- CHANGELOG.md | 8 ++++++ .../InterprocessMemory.Tests.csproj | 2 +- InterprocessMemory/InterprocessMemory.csproj | 2 +- README.md | 6 ++++- 5 files changed, 36 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 16dc1e5..87fd049 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -41,10 +41,14 @@ jobs: - name: Check out uses: actions/checkout@v7 + # The library and its tests target net8.0 and net10.0. Both runtimes are needed: the tests of each + # framework start the net8.0 test worker as a child process. - name: Set up .NET uses: actions/setup-dotnet@v6 with: - dotnet-version: 8.0.x + dotnet-version: | + 8.0.x + 10.0.x - name: Cache NuGet packages uses: actions/cache@v6 @@ -65,13 +69,24 @@ jobs: # The blame collector (enabled by --blame-hang-timeout) names the running test when the test host # dies, which is what an AccessViolationException from a memory-mapped region looks like; the hang # timeout stops a stuck test from holding the runner until the job timeout. - - name: Test + - name: Test (net8.0) run: > dotnet test InterprocessMemory.Tests/InterprocessMemory.Tests.csproj - --configuration Release --no-build + --configuration Release --no-build --framework net8.0 + --filter "Category!=TimingSensitive&Category!=LongRunning" + --blame-hang-timeout 10m --blame-hang-dump-type none + --logger "trx;LogFileName=test-results-net8.0.trx" + --results-directory TestResults + + # A failure of the net8.0 step does not hide the net10.0 result: both are worth reading. + - name: Test (net10.0) + if: ${{ !cancelled() && steps.build.conclusion == 'success' }} + run: > + dotnet test InterprocessMemory.Tests/InterprocessMemory.Tests.csproj + --configuration Release --no-build --framework net10.0 --filter "Category!=TimingSensitive&Category!=LongRunning" --blame-hang-timeout 10m --blame-hang-dump-type none - --logger "trx;LogFileName=test-results.trx" + --logger "trx;LogFileName=test-results-net10.0.trx" --results-directory TestResults # Tests marked LongRunning (minutes each) are explicit-only and are left to manual runs. @@ -87,7 +102,7 @@ jobs: --configuration Release --no-build --filter "Category=TimingSensitive" --blame-hang-timeout 5m --blame-hang-dump-type none - --logger "trx;LogFileName=timing-sensitive-results.trx" + --logger "trx;LogFilePrefix=timing-sensitive-results" --results-directory TestResults # continue-on-error shows a failed step as a success, so say it out loud: a warning annotation on the @@ -95,7 +110,7 @@ jobs: - name: Report timing sensitive failures if: ${{ !cancelled() && steps.timing.outcome == 'failure' }} run: | - echo "::warning title=Timing sensitive tests failed on ${{ matrix.os }}::This step does not fail the job. See the step log and timing-sensitive-results.trx in the test-results artifact." + echo "::warning title=Timing sensitive tests failed on ${{ matrix.os }}::This step does not fail the job. See the step log and the timing-sensitive-results*.trx files in the test-results artifact." { echo "### Timing sensitive tests failed on ${{ matrix.os }}" echo "" diff --git a/CHANGELOG.md b/CHANGELOG.md index 89de0fc..eab85c0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,14 @@ Migrating from 3.0.0 or 2.x: see [MIGRATION.md](MIGRATION.md). `Process.StartTime`, which differs between observers, so it takes the write lock away from every live owner, including one that runs a later version. Update all processes that use a region together. +## 4.1.0 (not released yet) + +### Added + +- The package contains a `net10.0` build next to the `net8.0` one. The tests run on both frameworks in CI. The + cross-process tests keep starting a `net8.0` worker, so a `net10.0` test run also exercises a `net10.0` and a + `net8.0` process sharing one region. + ## 4.0.0 (not released yet) Changes since the 3.0.0 release. The memory format is unchanged (format version 3). diff --git a/InterprocessMemory.Tests/InterprocessMemory.Tests.csproj b/InterprocessMemory.Tests/InterprocessMemory.Tests.csproj index c4d8794..bdfba90 100644 --- a/InterprocessMemory.Tests/InterprocessMemory.Tests.csproj +++ b/InterprocessMemory.Tests/InterprocessMemory.Tests.csproj @@ -1,7 +1,7 @@ - net8.0 + net8.0;net10.0 latest enable enable diff --git a/InterprocessMemory/InterprocessMemory.csproj b/InterprocessMemory/InterprocessMemory.csproj index c467de6..e8b655d 100644 --- a/InterprocessMemory/InterprocessMemory.csproj +++ b/InterprocessMemory/InterprocessMemory.csproj @@ -4,7 +4,7 @@ ..\bin\$(Configuration)\ InterprocessMemory InterprocessMemory - net8.0 + net8.0;net10.0 ..\bin\$(Configuration)\$(TargetFramework)\InterprocessMemory.xml latest enable diff --git a/README.md b/README.md index 3b46845..293b8ca 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ arrays, and structured records through named shared memory. - Windows: named `MemoryMappedFile` - Linux: named files in `/dev/shm` -- .NET 8 or later +- .NET 8 or later (the package contains builds for `net8.0` and `net10.0`) - Process-wide reader/writer synchronization with orphan-lock recovery - Allocation-free `Span` and `unmanaged` typed-queue hot paths @@ -371,6 +371,10 @@ dotnet build InterprocessMemory.sln --configuration Release dotnet test InterprocessMemory.sln --configuration Release ``` +The library and its tests target `net8.0` and `net10.0`, so building needs a .NET 10 SDK and running every +test needs both runtimes (the cross-process tests start a `net8.0` worker). Add `--framework net8.0` or +`--framework net10.0` to `dotnet test` to run one of them. + The test suite includes real child-process transfer, multi-process typed MPMC delivery, cross-process lock exclusion, and orphan-lock recovery. From a25885df64c7c558cfa932c7523be5a56c417024 Mon Sep 17 00:00:00 2001 From: zacala Date: Thu, 22 Oct 2026 15:40:55 +0000 Subject: [PATCH 2/3] Run the net8.0 worker from a folder of its own and clear regions between runs The net8.0 test worker was copied next to the tests of every framework. Next to the net10.0 tests it found the net10.0 build of the library, which a net8.0 runtime cannot load (System.Runtime 10.0.0.0), so every cross-process test of the net10.0 run failed. It now lives in a "worker" folder of the test output with the net8.0 build of the library. On Linux a region outlives its creator and a number of tests use fixed region names, so the net10.0 run found what the net8.0 run had left in /dev/shm and several tests saw old data. CI removes the files between the two runs. --- .github/workflows/ci.yml | 11 +++++++++++ InterprocessMemory.Tests/CrossProcessTests.cs | 3 +++ .../InterprocessMemory.Tests.csproj | 5 ++++- 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 87fd049..59b3876 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -78,6 +78,13 @@ jobs: --logger "trx;LogFileName=test-results-net8.0.trx" --results-directory TestResults + # On Linux a region outlives the process that created it (a file in /dev/shm), and a number of tests use + # fixed region names. Both frameworks run in this job one after the other, so the second would find what + # the first left behind. + - name: Remove the regions left by the net8.0 tests + if: ${{ !cancelled() && runner.os == 'Linux' }} + run: find /dev/shm -maxdepth 1 -type f -delete + # A failure of the net8.0 step does not hide the net10.0 result: both are worth reading. - name: Test (net10.0) if: ${{ !cancelled() && steps.build.conclusion == 'success' }} @@ -93,6 +100,10 @@ jobs: # # Tests whose outcome depends on the core count and the scheduler. They still run and show up # in the log and the artifact, but a failure here does not fail the job. + - name: Remove the regions left by the net10.0 tests + if: ${{ !cancelled() && runner.os == 'Linux' }} + run: find /dev/shm -maxdepth 1 -type f -delete + - name: Test (timing sensitive, informational) id: timing if: ${{ !cancelled() && steps.build.conclusion == 'success' }} diff --git a/InterprocessMemory.Tests/CrossProcessTests.cs b/InterprocessMemory.Tests/CrossProcessTests.cs index 05b44af..b3548e1 100644 --- a/InterprocessMemory.Tests/CrossProcessTests.cs +++ b/InterprocessMemory.Tests/CrossProcessTests.cs @@ -22,8 +22,11 @@ namespace InterprocessMemory.Tests; [Category("CrossProcess")] public class CrossProcessTests { + // The worker is a net8.0 program with the net8.0 build of the library next to it, in its own folder (see the + // CopyTestWorker target of the test project); the tests of the net10.0 build start the same worker. private static readonly string HelperDll = Path.Combine( AppContext.BaseDirectory, + "worker", "InterprocessMemory.TestWorker.dll"); [OneTimeSetUp] diff --git a/InterprocessMemory.Tests/InterprocessMemory.Tests.csproj b/InterprocessMemory.Tests/InterprocessMemory.Tests.csproj index bdfba90..f1dbc35 100644 --- a/InterprocessMemory.Tests/InterprocessMemory.Tests.csproj +++ b/InterprocessMemory.Tests/InterprocessMemory.Tests.csproj @@ -32,11 +32,14 @@ ReferenceOutputAssembly="false" /> + - + From c4989f33265abe1c0be9b0a46a1ffd311bccc0ff Mon Sep 17 00:00:00 2001 From: zacala Date: Thu, 22 Oct 2026 15:45:22 +0000 Subject: [PATCH 3/3] Only remove the runner's own files from /dev/shm between test runs The runner image keeps a root-owned file there (libpod_lock) that find cannot delete, so the cleanup step exited with 1 and failed the job although both test runs had passed. The tests create their regions as the runner user, so only that user's files are removed. --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 59b3876..711a5bf 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -83,7 +83,7 @@ jobs: # the first left behind. - name: Remove the regions left by the net8.0 tests if: ${{ !cancelled() && runner.os == 'Linux' }} - run: find /dev/shm -maxdepth 1 -type f -delete + run: find /dev/shm -maxdepth 1 -type f -user "$(id -u)" -delete # A failure of the net8.0 step does not hide the net10.0 result: both are worth reading. - name: Test (net10.0) @@ -102,7 +102,7 @@ jobs: # in the log and the artifact, but a failure here does not fail the job. - name: Remove the regions left by the net10.0 tests if: ${{ !cancelled() && runner.os == 'Linux' }} - run: find /dev/shm -maxdepth 1 -type f -delete + run: find /dev/shm -maxdepth 1 -type f -user "$(id -u)" -delete - name: Test (timing sensitive, informational) id: timing