From 3db18787bd2ba9d9aca91d8e6374cc2edfbb5242 Mon Sep 17 00:00:00 2001 From: Phill Date: Tue, 3 Jun 2025 11:17:41 -0400 Subject: [PATCH 1/5] working initial design --- core/SystemGroup.cs | 152 ++++++++++++++++++++++++++++++++++++ tests/MultithreadedTests.cs | 36 +++++++++ 2 files changed, 188 insertions(+) create mode 100644 core/SystemGroup.cs create mode 100644 tests/MultithreadedTests.cs diff --git a/core/SystemGroup.cs b/core/SystemGroup.cs new file mode 100644 index 0000000..0066410 --- /dev/null +++ b/core/SystemGroup.cs @@ -0,0 +1,152 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Threading; + +namespace Simulation +{ + /// + /// A group of systems that all run on a separate foreground thread. + /// + public sealed class SystemGroup : IDisposable, IListener where T : unmanaged + { + private readonly CancellationTokenSource cts; + private readonly Thread thread; + private IListener[] systems; + private bool disposed; + private volatile int messageVersion; + private volatile bool finishedSignal; + private T message; + + int IListener.Count => 1; + + /// + /// Creates a new system group. + /// + public SystemGroup(ReadOnlySpan threadName) + { + systems = []; + cts = new(); + thread = new Thread(Run); + thread.Name = threadName.ToString(); + thread.IsBackground = false; + thread.Start(); + } + + void IListener.CollectMessageHandlers(Span messageHandlers) + { + messageHandlers[0] = MessageHandler.Get, T>(this); + } + + private void Run() + { + SpinWait waiter = new(); + int lastMessageVersion = 0; + while (true) + { + while (lastMessageVersion == messageVersion && !cts.IsCancellationRequested) + { + waiter.SpinOnce(); + } + + if (cts.IsCancellationRequested) + { + break; + } + + Thread.MemoryBarrier(); + T currentMessage = message; + lastMessageVersion = messageVersion; + + foreach (IListener system in systems) + { + system.Receive(ref currentMessage); + } + + waiter.Reset(); + finishedSignal = true; + } + } + + /// + /// Disposes the system group and finishes work on its thread. + /// + public void Dispose() + { + ThrowIfDisposed(); + + disposed = true; + cts.Cancel(); + if (thread.IsAlive) + { + thread.Join(); + } + + cts.Dispose(); + } + + /// + /// Adds the given to the group. + /// + public void Add(S system) where S : IListener + { + ThrowIfDisposed(); + + Array.Resize(ref systems, systems.Length + 1); + systems[^1] = system; + } + + /// + /// Removes the system of type from the group. + /// + public void Remove() where S : IListener + { + ThrowIfDisposed(); + + List> systemList = new(systems); + for (int i = 0; i < systemList.Count; i++) + { + if (systemList[i] is S system) + { + systemList.RemoveAt(i); + if (system is IDisposable disposable) + { + disposable.Dispose(); + } + + break; + } + } + + systems = systemList.ToArray(); + } + + /// + /// Signals the thread to process the given . + /// + public void Receive(ref T message) + { + ThrowIfDisposed(); + + finishedSignal = false; + this.message = message; + Thread.MemoryBarrier(); + Interlocked.Increment(ref messageVersion); + + SpinWait waiter = new(); + while (!finishedSignal) + { + waiter.SpinOnce(); + } + } + + [Conditional("DEBUG")] + private void ThrowIfDisposed() + { + if (disposed) + { + throw new ObjectDisposedException(nameof(SystemGroup), "This system group has already been disposed"); + } + } + } +} \ No newline at end of file diff --git a/tests/MultithreadedTests.cs b/tests/MultithreadedTests.cs new file mode 100644 index 0000000..4d9252e --- /dev/null +++ b/tests/MultithreadedTests.cs @@ -0,0 +1,36 @@ +namespace Simulation.Tests +{ + public class MultithreadedTests : SimulationTests + { + [Test] + public void SystemGroupCreation() + { + SystemGroup group = new("update group"); + TimeSystem a = new(); + group.Add(a); + Simulator.Add(group); + Simulator.Broadcast(new UpdateMessage(0.1)); + Assert.That(a.time, Is.EqualTo(0.1)); + Simulator.Remove(group); + Simulator.Broadcast(new UpdateMessage(0.1)); + Assert.That(a.time, Is.EqualTo(0.1)); + group.Dispose(); + } + + [Test] + public void MultipleSystems() + { + SystemGroup group = new("update group"); + TimeSystem a = new(); + TimeSystem b = new(); + group.Add(a); + group.Add(b); + Simulator.Add(group); + Simulator.Broadcast(new UpdateMessage(0.1)); + Assert.That(a.time, Is.EqualTo(0.1)); + Assert.That(b.time, Is.EqualTo(0.1)); + Simulator.Remove(group); + group.Dispose(); + } + } +} \ No newline at end of file From 17913cbc2dc2e59d01f7ca11680600c86665a58f Mon Sep 17 00:00:00 2001 From: Phill Date: Sat, 7 Jun 2025 12:22:02 -0400 Subject: [PATCH 2/5] include both debug and release code in nuget package --- .github/workflows/publish.yml | 42 +++++++++++------- .github/workflows/test.yml | 24 +++++------ core/Simulation.Core.csproj | 69 +++++++++++++++++------------- core/build/Simulation.Core.targets | 18 ++++++++ 4 files changed, 96 insertions(+), 57 deletions(-) create mode 100644 core/build/Simulation.Core.targets diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index d07e46a..df679e7 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -15,33 +15,33 @@ jobs: with: path: ${{ github.event.repository.name }} - - name: Checkout `worlds` + - name: Checkout `types` uses: actions/checkout@v4.1.2 with: - repository: simulation-tree/worlds + repository: simulation-tree/types token: ${{ secrets.PAT }} - path: worlds + path: types - - name: Checkout `types` + - name: Checkout `collections` uses: actions/checkout@v4.1.2 with: - repository: simulation-tree/types + repository: simulation-tree/collections token: ${{ secrets.PAT }} - path: types + path: collections - - name: Checkout `unmanaged` + - name: Checkout `worlds` uses: actions/checkout@v4.1.2 with: - repository: simulation-tree/unmanaged + repository: simulation-tree/worlds token: ${{ secrets.PAT }} - path: unmanaged + path: worlds - - name: Checkout `collections` + - name: Checkout `unmanaged` uses: actions/checkout@v4.1.2 with: - repository: simulation-tree/collections + repository: simulation-tree/unmanaged token: ${{ secrets.PAT }} - path: collections + path: unmanaged - name: Setup uses: actions/setup-dotnet@v4 @@ -51,15 +51,27 @@ jobs: - name: Set VERSION variable from tag run: echo "VERSION=${GITHUB_REF/refs\/tags\/v/}" >> $GITHUB_ENV + - name: Build `Simulation.Core` + run: dotnet build "${{ github.event.repository.name }}/core" -c Debug /p:Version=${VERSION} + - name: Build `Simulation.Core` run: dotnet build "${{ github.event.repository.name }}/core" -c Release /p:Version=${VERSION} + - name: Build `Simulation.Generator` + run: dotnet build "${{ github.event.repository.name }}/generator" -c Debug /p:Version=${VERSION} + - name: Build `Simulation.Generator` run: dotnet build "${{ github.event.repository.name }}/generator" -c Release /p:Version=${VERSION} + - name: Build `Simulation` + run: dotnet build "${{ github.event.repository.name }}/source" -c Debug /p:Version=${VERSION} + - name: Build `Simulation` run: dotnet build "${{ github.event.repository.name }}/source" -c Release /p:Version=${VERSION} + - name: Build `Simulation.Tests` + run: dotnet build "${{ github.event.repository.name }}/tests" -c Debug /p:Version=${VERSION} + - name: Build `Simulation.Tests` run: dotnet build "${{ github.event.repository.name }}/tests" -c Release /p:Version=${VERSION} @@ -67,13 +79,13 @@ jobs: run: dotnet test "${{ github.event.repository.name }}/tests" -c Release --logger "trx" - name: Pack `Simulation.Core` - run: dotnet pack "${{ github.event.repository.name }}/core" -c Release /p:Version=${VERSION} --no-build --output . + run: dotnet pack "${{ github.event.repository.name }}/core" /p:Version=${VERSION} --no-build --output . - name: Pack `Simulation.Generator` - run: dotnet pack "${{ github.event.repository.name }}/generator" -c Release /p:Version=${VERSION} --no-build --output . + run: dotnet pack "${{ github.event.repository.name }}/generator" /p:Version=${VERSION} --no-build --output . - name: Pack `Simulation` - run: dotnet pack "${{ github.event.repository.name }}/source" -c Release /p:Version=${VERSION} --no-build --output . + run: dotnet pack "${{ github.event.repository.name }}/source" /p:Version=${VERSION} --no-build --output . - name: Add NuGet Source run: dotnet nuget add source https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json --name github --username ${{ github.repository_owner }} --password ${{ github.token }} --store-password-in-clear-text diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index be0882a..9a53745 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -28,33 +28,33 @@ jobs: with: path: ${{ github.event.repository.name }} - - name: Checkout `worlds` + - name: Checkout `types` uses: actions/checkout@v4.1.2 with: - repository: simulation-tree/worlds + repository: simulation-tree/types token: ${{ secrets.PAT }} - path: worlds + path: types - - name: Checkout `types` + - name: Checkout `collections` uses: actions/checkout@v4.1.2 with: - repository: simulation-tree/types + repository: simulation-tree/collections token: ${{ secrets.PAT }} - path: types + path: collections - - name: Checkout `unmanaged` + - name: Checkout `worlds` uses: actions/checkout@v4.1.2 with: - repository: simulation-tree/unmanaged + repository: simulation-tree/worlds token: ${{ secrets.PAT }} - path: unmanaged + path: worlds - - name: Checkout `collections` + - name: Checkout `unmanaged` uses: actions/checkout@v4.1.2 with: - repository: simulation-tree/collections + repository: simulation-tree/unmanaged token: ${{ secrets.PAT }} - path: collections + path: unmanaged - name: Setup uses: actions/setup-dotnet@v4 diff --git a/core/Simulation.Core.csproj b/core/Simulation.Core.csproj index ce2db7f..8ec8e21 100644 --- a/core/Simulation.Core.csproj +++ b/core/Simulation.Core.csproj @@ -1,35 +1,44 @@ - + - - net9.0 - disable - enable - True - True - True - True - popcron - simulation-tree - Logic upon data - https://github.com/simulation-tree/simulation - README.md - ecs - Simulation Core - Simulation - - True - + + net9.0 + disable + enable + True + True + True + True + popcron + simulation-tree + Logic upon data + https://github.com/simulation-tree/simulation + README.md + ecs + Simulation Core + Simulation + True + false + true + - - - True - \ - - + + + True + \ + + - - - - + + + + + + + + + + + + \ No newline at end of file diff --git a/core/build/Simulation.Core.targets b/core/build/Simulation.Core.targets new file mode 100644 index 0000000..5b7e3bb --- /dev/null +++ b/core/build/Simulation.Core.targets @@ -0,0 +1,18 @@ + + + + + + $(MSBuildThisFileDirectory)..\tools\debug\Simulation.Core.dll + True + + + + + + $(MSBuildThisFileDirectory)..\tools\release\Simulation.Core.dll + True + + + + \ No newline at end of file From 7cbc00d4a9277cd51e3ac8bdc1d993e2073f1735 Mon Sep 17 00:00:00 2001 From: Phill Date: Sat, 7 Jun 2025 13:01:50 -0400 Subject: [PATCH 3/5] copy xml files to package if they exist --- core/Simulation.Core.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/Simulation.Core.csproj b/core/Simulation.Core.csproj index 8ec8e21..58060e3 100644 --- a/core/Simulation.Core.csproj +++ b/core/Simulation.Core.csproj @@ -35,9 +35,9 @@ - + - + From bcf0c055dbd405a538c22f1f22d374cf309e9b6e Mon Sep 17 00:00:00 2001 From: Phill Date: Sun, 8 Jun 2025 10:01:38 -0400 Subject: [PATCH 4/5] adjust package assembly paths --- core/Simulation.Core.csproj | 9 +++------ core/build/Simulation.Core.targets | 18 ------------------ 2 files changed, 3 insertions(+), 24 deletions(-) delete mode 100644 core/build/Simulation.Core.targets diff --git a/core/Simulation.Core.csproj b/core/Simulation.Core.csproj index 58060e3..b375133 100644 --- a/core/Simulation.Core.csproj +++ b/core/Simulation.Core.csproj @@ -18,7 +18,7 @@ Simulation True false - true + bin/$(TargetFramework)/$(Configuration) @@ -34,11 +34,8 @@ - - - - - + + \ No newline at end of file diff --git a/core/build/Simulation.Core.targets b/core/build/Simulation.Core.targets deleted file mode 100644 index 5b7e3bb..0000000 --- a/core/build/Simulation.Core.targets +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - $(MSBuildThisFileDirectory)..\tools\debug\Simulation.Core.dll - True - - - - - - $(MSBuildThisFileDirectory)..\tools\release\Simulation.Core.dll - True - - - - \ No newline at end of file From 6712c3e961de3452273fe75a357526b5da8769ad Mon Sep 17 00:00:00 2001 From: Phill Date: Sun, 8 Jun 2025 10:03:37 -0400 Subject: [PATCH 5/5] add targets --- core/buildTransitive/Simulation.Core.targets | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 core/buildTransitive/Simulation.Core.targets diff --git a/core/buildTransitive/Simulation.Core.targets b/core/buildTransitive/Simulation.Core.targets new file mode 100644 index 0000000..64a0488 --- /dev/null +++ b/core/buildTransitive/Simulation.Core.targets @@ -0,0 +1,10 @@ + + + + + + $(MSBuildThisFileDirectory)..\lib\net9.0\$(Configuration)\Simulation.Core.dll + + + + \ No newline at end of file