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.