diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 16dc1e5..711a5bf 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,19 +69,41 @@ 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 + + # 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 -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) + 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. # # 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 -user "$(id -u)" -delete + - name: Test (timing sensitive, informational) id: timing if: ${{ !cancelled() && steps.build.conclusion == 'success' }} @@ -87,7 +113,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 +121,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 e615165..0723faa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,9 @@ including one that runs a later version. Update all processes that use a region ### 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. - Region names may start with `Global\` or `Local\`, the Windows kernel namespaces (all sessions, or the session of the process). A service and a desktop process can share a region through `Global\name`. What follows the prefix must be flat. On Linux the prefix is dropped (there are no sessions), so `Global\x`, `Local\x` and `x` are one 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 c4d8794..f1dbc35 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 @@ -32,11 +32,14 @@ ReferenceOutputAssembly="false" /> + - + 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 1c78b51..04f2d92 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 @@ -376,6 +376,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.