Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 32 additions & 6 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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' }}
Expand All @@ -87,15 +113,15 @@ 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
# run and a section in its summary. Otherwise a test that fails on every run looks green for ever.
- 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 ""
Expand Down
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions InterprocessMemory.Tests/CrossProcessTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
7 changes: 5 additions & 2 deletions InterprocessMemory.Tests/InterprocessMemory.Tests.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<TargetFrameworks>net8.0;net10.0</TargetFrameworks>
<LangVersion>latest</LangVersion>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
Expand Down Expand Up @@ -32,11 +32,14 @@
ReferenceOutputAssembly="false" />
</ItemGroup>

<!-- The worker is a net8.0 program that loads the net8.0 build of the library. It gets a folder of its own
("worker"): next to the tests of the net10.0 build it would find the net10.0 build of the library, which
a net8.0 runtime cannot load. -->
<Target Name="CopyTestWorker" AfterTargets="Build">
<ItemGroup>
<TestWorkerFiles Include="$(MSBuildThisFileDirectory)..\InterprocessMemory.TestWorker\bin\$(Configuration)\net8.0\*.*" />
</ItemGroup>
<Copy SourceFiles="@(TestWorkerFiles)" DestinationFolder="$(OutputPath)" SkipUnchangedFiles="true" />
<Copy SourceFiles="@(TestWorkerFiles)" DestinationFolder="$(OutputPath)worker\" SkipUnchangedFiles="true" />
</Target>

</Project>
2 changes: 1 addition & 1 deletion InterprocessMemory/InterprocessMemory.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<OutputPath>..\bin\$(Configuration)\</OutputPath>
<AssemblyName>InterprocessMemory</AssemblyName>
<RootNamespace>InterprocessMemory</RootNamespace>
<TargetFramework>net8.0</TargetFramework>
<TargetFrameworks>net8.0;net10.0</TargetFrameworks>
<DocumentationFile>..\bin\$(Configuration)\$(TargetFramework)\InterprocessMemory.xml</DocumentationFile>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
Expand Down
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<T>` and `unmanaged` typed-queue hot paths

Expand Down Expand Up @@ -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.

Expand Down
Loading