From 369e463bc2db6bc9f0a14af4a8a78764b640b74e Mon Sep 17 00:00:00 2001 From: Aaron Stannard Date: Tue, 24 Nov 2020 17:27:10 -0600 Subject: [PATCH 1/6] made WaitForThreadsToExit return the underlying Tasks --- .../Helios.Concurrency.DedicatedThreadPool.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/core/Helios.DedicatedThreadPool/Helios.Concurrency.DedicatedThreadPool.cs b/src/core/Helios.DedicatedThreadPool/Helios.Concurrency.DedicatedThreadPool.cs index b45310c..977792f 100644 --- a/src/core/Helios.DedicatedThreadPool/Helios.Concurrency.DedicatedThreadPool.cs +++ b/src/core/Helios.DedicatedThreadPool/Helios.Concurrency.DedicatedThreadPool.cs @@ -304,18 +304,18 @@ public void Dispose() /// /// TBD /// - public void WaitForThreadsExit() + public async Task WaitForThreadsExit() { - WaitForThreadsExit(Timeout.InfiniteTimeSpan); + await WaitForThreadsExit(Timeout.InfiniteTimeSpan); } /// /// TBD /// /// TBD - public void WaitForThreadsExit(TimeSpan timeout) + public async Task WaitForThreadsExit(TimeSpan timeout) { - Task.WaitAll(_workers.Select(worker => worker.ThreadExit).ToArray(), timeout); + await Task.WhenAll(_workers.Select(worker => worker.ThreadExit)); } #region Pool worker implementation From 02cf6e3232752e01be4cba3cd26877f0f00195b7 Mon Sep 17 00:00:00 2001 From: Aaron Stannard Date: Tue, 24 Nov 2020 17:27:28 -0600 Subject: [PATCH 2/6] adding idle CPU measurement benchmark using Docker --- src/Helios.DedicatedThreadPool.sln | 20 ++++++++++++----- .../Helios.DedicatedThreadPool/_Visibility.cs | 3 +-- ...DedicatedThreadPool.IdleCpu.Program.csproj | 12 ++++++++++ .../Program.cs | 22 +++++++++++++++++++ 4 files changed, 50 insertions(+), 7 deletions(-) create mode 100644 src/tests/Helios.DedicatedThreadPool.IdleCpu.Program/Helios.DedicatedThreadPool.IdleCpu.Program.csproj create mode 100644 src/tests/Helios.DedicatedThreadPool.IdleCpu.Program/Program.cs diff --git a/src/Helios.DedicatedThreadPool.sln b/src/Helios.DedicatedThreadPool.sln index b918e85..d5b495c 100644 --- a/src/Helios.DedicatedThreadPool.sln +++ b/src/Helios.DedicatedThreadPool.sln @@ -1,11 +1,11 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 14 -VisualStudioVersion = 14.0.24720.0 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30717.126 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Helios.DedicatedThreadPool", "core\Helios.DedicatedThreadPool\Helios.DedicatedThreadPool.csproj", "{6C8F359E-E479-44D5-8FA0-02BFD38C3860}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Helios.DedicatedThreadPool", "core\Helios.DedicatedThreadPool\Helios.DedicatedThreadPool.csproj", "{6C8F359E-E479-44D5-8FA0-02BFD38C3860}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Helios.DedicatedThreadPool.Tests", "tests\Helios.DedicatedThreadPool.Tests\Helios.DedicatedThreadPool.Tests.csproj", "{BD7B3C66-14D8-4F01-AECE-F7F3F440A899}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Helios.DedicatedThreadPool.Tests", "tests\Helios.DedicatedThreadPool.Tests\Helios.DedicatedThreadPool.Tests.csproj", "{BD7B3C66-14D8-4F01-AECE-F7F3F440A899}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{741F51D5-FB48-4AE3-837E-3C32FAC6584E}" EndProject @@ -23,7 +23,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{39EF5B .nuget\NuGet.targets = .nuget\NuGet.targets EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Helios.DedicatedThreadPool.Tests.Performance", "tests\Helios.DedicatedThreadPool.Tests.Performance\Helios.DedicatedThreadPool.Tests.Performance.csproj", "{428DF580-1734-4AB6-B80E-F5ABABCE61D6}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Helios.DedicatedThreadPool.Tests.Performance", "tests\Helios.DedicatedThreadPool.Tests.Performance\Helios.DedicatedThreadPool.Tests.Performance.csproj", "{428DF580-1734-4AB6-B80E-F5ABABCE61D6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Helios.DedicatedThreadPool.IdleCpu.Program", "tests\Helios.DedicatedThreadPool.IdleCpu.Program\Helios.DedicatedThreadPool.IdleCpu.Program.csproj", "{876973A9-060F-402E-9434-733F8320AA6B}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -43,6 +45,10 @@ Global {428DF580-1734-4AB6-B80E-F5ABABCE61D6}.Debug|Any CPU.Build.0 = Debug|Any CPU {428DF580-1734-4AB6-B80E-F5ABABCE61D6}.Release|Any CPU.ActiveCfg = Release|Any CPU {428DF580-1734-4AB6-B80E-F5ABABCE61D6}.Release|Any CPU.Build.0 = Release|Any CPU + {876973A9-060F-402E-9434-733F8320AA6B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {876973A9-060F-402E-9434-733F8320AA6B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {876973A9-060F-402E-9434-733F8320AA6B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {876973A9-060F-402E-9434-733F8320AA6B}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -51,5 +57,9 @@ Global {6C8F359E-E479-44D5-8FA0-02BFD38C3860} = {E86B275F-E912-47C5-8F5E-BA62D5959EBD} {BD7B3C66-14D8-4F01-AECE-F7F3F440A899} = {741F51D5-FB48-4AE3-837E-3C32FAC6584E} {428DF580-1734-4AB6-B80E-F5ABABCE61D6} = {741F51D5-FB48-4AE3-837E-3C32FAC6584E} + {876973A9-060F-402E-9434-733F8320AA6B} = {741F51D5-FB48-4AE3-837E-3C32FAC6584E} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {DD52A4C9-1308-4946-82E0-9AD4884CAA85} EndGlobalSection EndGlobal diff --git a/src/core/Helios.DedicatedThreadPool/_Visibility.cs b/src/core/Helios.DedicatedThreadPool/_Visibility.cs index 916444f..61935f9 100644 --- a/src/core/Helios.DedicatedThreadPool/_Visibility.cs +++ b/src/core/Helios.DedicatedThreadPool/_Visibility.cs @@ -2,5 +2,4 @@ [assembly: InternalsVisibleTo("Helios.DedicatedThreadPool.Tests")] [assembly: InternalsVisibleTo("Helios.DedicatedThreadPool.Tests.Performance")] -[assembly: InternalsVisibleTo("Helios.DedicatedThreadPool.VsThreadpoolBenchmark")] -[assembly: InternalsVisibleTo("Helios.DedicatedThreadPool.VsDedicatedThreadFiber")] +[assembly: InternalsVisibleTo("Helios.DedicatedThreadPool.IdleCpu.Program")] diff --git a/src/tests/Helios.DedicatedThreadPool.IdleCpu.Program/Helios.DedicatedThreadPool.IdleCpu.Program.csproj b/src/tests/Helios.DedicatedThreadPool.IdleCpu.Program/Helios.DedicatedThreadPool.IdleCpu.Program.csproj new file mode 100644 index 0000000..95fb7bb --- /dev/null +++ b/src/tests/Helios.DedicatedThreadPool.IdleCpu.Program/Helios.DedicatedThreadPool.IdleCpu.Program.csproj @@ -0,0 +1,12 @@ + + + + Exe + netcoreapp3.1 + false + + + + + + diff --git a/src/tests/Helios.DedicatedThreadPool.IdleCpu.Program/Program.cs b/src/tests/Helios.DedicatedThreadPool.IdleCpu.Program/Program.cs new file mode 100644 index 0000000..ef75799 --- /dev/null +++ b/src/tests/Helios.DedicatedThreadPool.IdleCpu.Program/Program.cs @@ -0,0 +1,22 @@ +using System; +using System.Threading.Tasks; +using Helios.Concurrency; + +namespace Helios.DedicatedThreadPool.IdleCpu.Program +{ + class Program + { + static async Task Main(string[] args) + { + var maxThreads = Math.Max(4, Environment.ProcessorCount); + + Console.WriteLine("Starting Idle Cpu Test with Following Configuration"); + Console.WriteLine("MaxThreads: {0}", maxThreads); + + var settings = new DedicatedThreadPoolSettings(maxThreads); + var threadPool = new Concurrency.DedicatedThreadPool(settings); + + await threadPool.WaitForThreadsExit(); + } + } +} From 2b4435643e2fc73505a6501b95a242800674adad Mon Sep 17 00:00:00 2001 From: Aaron Stannard Date: Tue, 24 Nov 2020 18:22:18 -0600 Subject: [PATCH 3/6] fixed background thread delay --- .../Helios.DedicatedThreadPool.IdleCpu.Program/Program.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/tests/Helios.DedicatedThreadPool.IdleCpu.Program/Program.cs b/src/tests/Helios.DedicatedThreadPool.IdleCpu.Program/Program.cs index ef75799..576d865 100644 --- a/src/tests/Helios.DedicatedThreadPool.IdleCpu.Program/Program.cs +++ b/src/tests/Helios.DedicatedThreadPool.IdleCpu.Program/Program.cs @@ -16,7 +16,8 @@ static async Task Main(string[] args) var settings = new DedicatedThreadPoolSettings(maxThreads); var threadPool = new Concurrency.DedicatedThreadPool(settings); - await threadPool.WaitForThreadsExit(); + // force background Helios threads to run + await Task.Delay(TimeSpan.FromMinutes(2)); } } } From ebebfaa144b5a21f15facd12cb0f2ef7cdebddf5 Mon Sep 17 00:00:00 2001 From: Aaron Stannard Date: Wed, 25 Nov 2020 17:06:35 -0600 Subject: [PATCH 4/6] working on idle cpu tests --- src/.dockerignore | 25 ++++++++++++++ src/Helios.DedicatedThreadPool.sln | 7 ++++ .../Dockerfile | 21 ++++++++++++ ...DedicatedThreadPool.IdleCpu.Program.csproj | 16 +++++++++ ...DedicatedThreadPool.IdleCpu.Program.csproj | 5 +++ .../Properties/launchSettings.json | 10 ++++++ ...s.DedicatedThreadPool.IdleCpu.Tests.csproj | 23 +++++++++++++ .../UnitTest1.cs | 16 +++++++++ .../DedicatedThreadPoolTaskSchedulerTests.cs | 3 +- .../DedicatedThreadPoolTests.cs | 15 ++++----- .../Helios.DedicatedThreadPool.Tests.csproj | 33 ++++++++++--------- 11 files changed, 149 insertions(+), 25 deletions(-) create mode 100644 src/.dockerignore create mode 100644 src/tests/Helios.DedicatedThreadPool.IdleCpu.Program/Dockerfile create mode 100644 src/tests/Helios.DedicatedThreadPool.IdleCpu.Program/Helios - Backup.DedicatedThreadPool.IdleCpu.Program.csproj create mode 100644 src/tests/Helios.DedicatedThreadPool.IdleCpu.Program/Properties/launchSettings.json create mode 100644 src/tests/Helios.DedicatedThreadPool.IdleCpu.Tests/Helios.DedicatedThreadPool.IdleCpu.Tests.csproj create mode 100644 src/tests/Helios.DedicatedThreadPool.IdleCpu.Tests/UnitTest1.cs diff --git a/src/.dockerignore b/src/.dockerignore new file mode 100644 index 0000000..3729ff0 --- /dev/null +++ b/src/.dockerignore @@ -0,0 +1,25 @@ +**/.classpath +**/.dockerignore +**/.env +**/.git +**/.gitignore +**/.project +**/.settings +**/.toolstarget +**/.vs +**/.vscode +**/*.*proj.user +**/*.dbmdl +**/*.jfm +**/azds.yaml +**/bin +**/charts +**/docker-compose* +**/Dockerfile* +**/node_modules +**/npm-debug.log +**/obj +**/secrets.dev.yaml +**/values.dev.yaml +LICENSE +README.md \ No newline at end of file diff --git a/src/Helios.DedicatedThreadPool.sln b/src/Helios.DedicatedThreadPool.sln index d5b495c..4ac5f45 100644 --- a/src/Helios.DedicatedThreadPool.sln +++ b/src/Helios.DedicatedThreadPool.sln @@ -27,6 +27,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Helios.DedicatedThreadPool. EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Helios.DedicatedThreadPool.IdleCpu.Program", "tests\Helios.DedicatedThreadPool.IdleCpu.Program\Helios.DedicatedThreadPool.IdleCpu.Program.csproj", "{876973A9-060F-402E-9434-733F8320AA6B}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Helios.DedicatedThreadPool.IdleCpu.Tests", "tests\Helios.DedicatedThreadPool.IdleCpu.Tests\Helios.DedicatedThreadPool.IdleCpu.Tests.csproj", "{F0A0B730-337D-4FE2-AA41-54535CF70726}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -49,6 +51,10 @@ Global {876973A9-060F-402E-9434-733F8320AA6B}.Debug|Any CPU.Build.0 = Debug|Any CPU {876973A9-060F-402E-9434-733F8320AA6B}.Release|Any CPU.ActiveCfg = Release|Any CPU {876973A9-060F-402E-9434-733F8320AA6B}.Release|Any CPU.Build.0 = Release|Any CPU + {F0A0B730-337D-4FE2-AA41-54535CF70726}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0A0B730-337D-4FE2-AA41-54535CF70726}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0A0B730-337D-4FE2-AA41-54535CF70726}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0A0B730-337D-4FE2-AA41-54535CF70726}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -58,6 +64,7 @@ Global {BD7B3C66-14D8-4F01-AECE-F7F3F440A899} = {741F51D5-FB48-4AE3-837E-3C32FAC6584E} {428DF580-1734-4AB6-B80E-F5ABABCE61D6} = {741F51D5-FB48-4AE3-837E-3C32FAC6584E} {876973A9-060F-402E-9434-733F8320AA6B} = {741F51D5-FB48-4AE3-837E-3C32FAC6584E} + {F0A0B730-337D-4FE2-AA41-54535CF70726} = {741F51D5-FB48-4AE3-837E-3C32FAC6584E} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {DD52A4C9-1308-4946-82E0-9AD4884CAA85} diff --git a/src/tests/Helios.DedicatedThreadPool.IdleCpu.Program/Dockerfile b/src/tests/Helios.DedicatedThreadPool.IdleCpu.Program/Dockerfile new file mode 100644 index 0000000..d39dd1f --- /dev/null +++ b/src/tests/Helios.DedicatedThreadPool.IdleCpu.Program/Dockerfile @@ -0,0 +1,21 @@ +#See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging. + +FROM mcr.microsoft.com/dotnet/core/runtime:3.1-buster-slim AS base +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/core/sdk:3.1-buster AS build +WORKDIR /src +COPY ["tests/Helios.DedicatedThreadPool.IdleCpu.Program/Helios.DedicatedThreadPool.IdleCpu.Program.csproj", "tests/Helios.DedicatedThreadPool.IdleCpu.Program/"] +COPY ["core/Helios.DedicatedThreadPool/Helios.DedicatedThreadPool.csproj", "core/Helios.DedicatedThreadPool/"] +RUN dotnet restore "tests/Helios.DedicatedThreadPool.IdleCpu.Program/Helios.DedicatedThreadPool.IdleCpu.Program.csproj" +COPY . . +WORKDIR "/src/tests/Helios.DedicatedThreadPool.IdleCpu.Program" +RUN dotnet build "Helios.DedicatedThreadPool.IdleCpu.Program.csproj" -c Release -o /app/build + +FROM build AS publish +RUN dotnet publish "Helios.DedicatedThreadPool.IdleCpu.Program.csproj" -c Release -o /app/publish + +FROM base AS final +WORKDIR /app +COPY --from=publish /app/publish . +ENTRYPOINT ["dotnet", "Helios.DedicatedThreadPool.IdleCpu.Program.dll"] \ No newline at end of file diff --git a/src/tests/Helios.DedicatedThreadPool.IdleCpu.Program/Helios - Backup.DedicatedThreadPool.IdleCpu.Program.csproj b/src/tests/Helios.DedicatedThreadPool.IdleCpu.Program/Helios - Backup.DedicatedThreadPool.IdleCpu.Program.csproj new file mode 100644 index 0000000..22030b0 --- /dev/null +++ b/src/tests/Helios.DedicatedThreadPool.IdleCpu.Program/Helios - Backup.DedicatedThreadPool.IdleCpu.Program.csproj @@ -0,0 +1,16 @@ + + + + Exe + netcoreapp3.1 + false + Linux + + + + + + + + + diff --git a/src/tests/Helios.DedicatedThreadPool.IdleCpu.Program/Helios.DedicatedThreadPool.IdleCpu.Program.csproj b/src/tests/Helios.DedicatedThreadPool.IdleCpu.Program/Helios.DedicatedThreadPool.IdleCpu.Program.csproj index 95fb7bb..c6939e0 100644 --- a/src/tests/Helios.DedicatedThreadPool.IdleCpu.Program/Helios.DedicatedThreadPool.IdleCpu.Program.csproj +++ b/src/tests/Helios.DedicatedThreadPool.IdleCpu.Program/Helios.DedicatedThreadPool.IdleCpu.Program.csproj @@ -4,7 +4,12 @@ Exe netcoreapp3.1 false + Linux + ..\.. + + + diff --git a/src/tests/Helios.DedicatedThreadPool.IdleCpu.Program/Properties/launchSettings.json b/src/tests/Helios.DedicatedThreadPool.IdleCpu.Program/Properties/launchSettings.json new file mode 100644 index 0000000..00fd40b --- /dev/null +++ b/src/tests/Helios.DedicatedThreadPool.IdleCpu.Program/Properties/launchSettings.json @@ -0,0 +1,10 @@ +{ + "profiles": { + "Helios.DedicatedThreadPool.IdleCpu.Program": { + "commandName": "Project" + }, + "Docker": { + "commandName": "Docker" + } + } +} \ No newline at end of file diff --git a/src/tests/Helios.DedicatedThreadPool.IdleCpu.Tests/Helios.DedicatedThreadPool.IdleCpu.Tests.csproj b/src/tests/Helios.DedicatedThreadPool.IdleCpu.Tests/Helios.DedicatedThreadPool.IdleCpu.Tests.csproj new file mode 100644 index 0000000..4ad54c8 --- /dev/null +++ b/src/tests/Helios.DedicatedThreadPool.IdleCpu.Tests/Helios.DedicatedThreadPool.IdleCpu.Tests.csproj @@ -0,0 +1,23 @@ + + + + netcoreapp3.1 + + false + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + diff --git a/src/tests/Helios.DedicatedThreadPool.IdleCpu.Tests/UnitTest1.cs b/src/tests/Helios.DedicatedThreadPool.IdleCpu.Tests/UnitTest1.cs new file mode 100644 index 0000000..8ec6423 --- /dev/null +++ b/src/tests/Helios.DedicatedThreadPool.IdleCpu.Tests/UnitTest1.cs @@ -0,0 +1,16 @@ +using System; +using Xunit; + +namespace Helios.DedicatedThreadPool.IdleCpu.Tests.Performance +{ + public class UnitTest1 + { + [Fact] + public void Test1() + { + + } + } + + +} diff --git a/src/tests/Helios.DedicatedThreadPool.Tests/DedicatedThreadPoolTaskSchedulerTests.cs b/src/tests/Helios.DedicatedThreadPool.Tests/DedicatedThreadPoolTaskSchedulerTests.cs index 0bd8937..3d1315b 100644 --- a/src/tests/Helios.DedicatedThreadPool.Tests/DedicatedThreadPoolTaskSchedulerTests.cs +++ b/src/tests/Helios.DedicatedThreadPool.Tests/DedicatedThreadPoolTaskSchedulerTests.cs @@ -3,11 +3,10 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using NUnit.Framework; +using Xunit; namespace Helios.Concurrency.Tests { - [TestFixture] public class DedicatedThreadPoolTaskSchedulerTests { protected TaskScheduler Scheduler; diff --git a/src/tests/Helios.DedicatedThreadPool.Tests/DedicatedThreadPoolTests.cs b/src/tests/Helios.DedicatedThreadPool.Tests/DedicatedThreadPoolTests.cs index 4b28721..794a9da 100644 --- a/src/tests/Helios.DedicatedThreadPool.Tests/DedicatedThreadPoolTests.cs +++ b/src/tests/Helios.DedicatedThreadPool.Tests/DedicatedThreadPoolTests.cs @@ -3,14 +3,13 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using NUnit.Framework; +using Xunit; namespace Helios.Concurrency.Tests { - [TestFixture] public class DedicatedThreadPoolTests { - [Test(Description = "Simple test to ensure that the entire thread pool doesn't just crater")] + [Fact()] public void Should_process_multithreaded_workload() { var atomicCounter = new AtomicCounter(0); @@ -22,10 +21,10 @@ public void Should_process_multithreaded_workload() } SpinWait.SpinUntil(() => atomicCounter.Current == 1000, TimeSpan.FromSeconds(1)); } - Assert.Pass(string.Format("Passed! Final counter value: {0} / Expected {1}", atomicCounter.Current, 1000)); + Assert.True(true, $"Passed! Final counter value: {atomicCounter.Current} / Expected {1000}"); } - [Test(Description = "Ensure that the number of threads running in the pool concurrently equal is AtMost equal to the DedicatedThreadPoolSettings.NumThreads property")] + [Fact(DisplayName = "Ensure that the number of threads running in the pool concurrently equal is AtMost equal to the DedicatedThreadPoolSettings.NumThreads property")] public void Should_process_workload_across_AtMost_DedicatedThreadPoolSettings_NumThreads() { var numThreads = Environment.ProcessorCount; @@ -49,7 +48,7 @@ public void Should_process_workload_across_AtMost_DedicatedThreadPoolSettings_Nu Assert.True(threadIds.Distinct().Count() <= numThreads); } - [Test(Description = "Have a user-defined method that throws an exception? The world should not end.")] + [Fact(DisplayName = "Have a user-defined method that throws an exception? The world should not end.")] public void World_should_not_end_if_exception_thrown_in_user_callback() { var numThreads = 3; @@ -73,7 +72,7 @@ public void World_should_not_end_if_exception_thrown_in_user_callback() } //sanity check - Assert.AreEqual(numThreads, threadIds.Distinct().Count()); + Assert.Equal(numThreads, threadIds.Distinct().Count()); //run the job again. Should get the same thread IDs as before for (var i = 0; i < numThreads*10; i++) @@ -84,7 +83,7 @@ public void World_should_not_end_if_exception_thrown_in_user_callback() } // half of thread IDs should belong to failed threads, other half to successful ones - Assert.AreEqual(numThreads, threadIds.Distinct().Count()); + Assert.Equal(numThreads, threadIds.Distinct().Count()); } } } diff --git a/src/tests/Helios.DedicatedThreadPool.Tests/Helios.DedicatedThreadPool.Tests.csproj b/src/tests/Helios.DedicatedThreadPool.Tests/Helios.DedicatedThreadPool.Tests.csproj index a7f0cb8..0e00c98 100644 --- a/src/tests/Helios.DedicatedThreadPool.Tests/Helios.DedicatedThreadPool.Tests.csproj +++ b/src/tests/Helios.DedicatedThreadPool.Tests/Helios.DedicatedThreadPool.Tests.csproj @@ -1,17 +1,20 @@  - - - netcoreapp3.1 - false - - - - - - - - - - - + + + netcoreapp3.1 + false + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + From 627ab5ed32d70d6f1ef97c9d75fa35459f53f34b Mon Sep 17 00:00:00 2001 From: Aaron Stannard Date: Fri, 4 Dec 2020 14:28:53 -0600 Subject: [PATCH 5/6] migrated test suite to XUnit --- .../DedicatedThreadPoolTaskSchedulerTests.cs | 25 +++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/src/tests/Helios.DedicatedThreadPool.Tests/DedicatedThreadPoolTaskSchedulerTests.cs b/src/tests/Helios.DedicatedThreadPool.Tests/DedicatedThreadPoolTaskSchedulerTests.cs index 3d1315b..5a2b5ac 100644 --- a/src/tests/Helios.DedicatedThreadPool.Tests/DedicatedThreadPoolTaskSchedulerTests.cs +++ b/src/tests/Helios.DedicatedThreadPool.Tests/DedicatedThreadPoolTaskSchedulerTests.cs @@ -7,28 +7,21 @@ namespace Helios.Concurrency.Tests { - public class DedicatedThreadPoolTaskSchedulerTests + public class DedicatedThreadPoolTaskSchedulerTests : IDisposable { protected TaskScheduler Scheduler; protected TaskFactory Factory; private DedicatedThreadPool Pool; - [SetUp] - public void SetUp() + public DedicatedThreadPoolTaskSchedulerTests() { Pool = new DedicatedThreadPool(new DedicatedThreadPoolSettings(Environment.ProcessorCount)); Scheduler = new DedicatedThreadPoolTaskScheduler(Pool); Factory = new TaskFactory(Scheduler); } - [TearDown] - public void TearDown() - { - Pool.Dispose(); - } - - [Test(Description = "Shouldn't immediately try to schedule all threads for task execution")] - [Ignore("Totally unpredictable on low powered machines")] + // "Shouldn't immediately try to schedule all threads for task execution" + [Fact(Skip = "Totally unpredictable on low powered machines")] public void Should_only_use_one_thread_for_single_task_request() { var allThreadIds = new ConcurrentBag(); @@ -50,10 +43,11 @@ public void Should_only_use_one_thread_for_single_task_request() task.Wait(); - Assert.AreEqual(Pool.Settings.NumThreads, allThreadIds.Count); + Assert.Equal(Pool.Settings.NumThreads, allThreadIds.Count); } - [Test(Description = "Should be able to utilize the entire DedicatedThreadPool for queuing tasks")] + // "Should be able to utilize the entire DedicatedThreadPool for queuing tasks" + [Fact] public void Should_use_all_threads_for_many_tasks() { var threadIds = new ConcurrentBag(); @@ -73,5 +67,10 @@ public void Should_use_all_threads_for_many_tasks() Assert.True(threadIds.Distinct().Count() <= Pool.Settings.NumThreads); } + + public void Dispose() + { + Pool?.Dispose(); + } } } From ca7667191420704d2e3a4b4cc39ffbc6e369b957 Mon Sep 17 00:00:00 2001 From: Aaron Stannard Date: Fri, 4 Dec 2020 16:13:43 -0600 Subject: [PATCH 6/6] completed idle CPU program --- build.fsx | 114 ++++++++++++++++-- .../Dockerfile | 15 +-- ...DedicatedThreadPool.IdleCpu.Program.csproj | 16 --- .../Program.cs | 58 ++++++++- 4 files changed, 161 insertions(+), 42 deletions(-) delete mode 100644 src/tests/Helios.DedicatedThreadPool.IdleCpu.Program/Helios - Backup.DedicatedThreadPool.IdleCpu.Program.csproj diff --git a/build.fsx b/build.fsx index 4a081c5..54806d5 100644 --- a/build.fsx +++ b/build.fsx @@ -10,13 +10,13 @@ open Fake.DotNetCli open Fake.DocFxHelper // Information about the project for Nuget and Assembly info files -let product = "Akka.Streams.Kafka" +let product = "Helios.DedicatedThreadPool" let configuration = "Release" // Metadata used when signing packages and DLLs -let signingName = "Akka.Streams.Kafka" -let signingDescription = "Apache Kafka adapter for Akka.NET Streams" -let signingUrl = "https://github.com/akkadotnet/Akka.Streams.Kafka" +let signingName = "Helios.DedicatedThreadPool" +let signingDescription = "Stand-alone .NET Threadpool" +let signingUrl = "https://github.com/helios-io/DedicatedThreadPool" // Read release notes and version let codeDirectory = __SOURCE_DIRECTORY__ @@ "src" @@ -24,22 +24,26 @@ let solutionFile = FindFirstMatchingFile "*.sln" codeDirectory // dynamically l let buildNumber = environVarOrDefault "BUILD_NUMBER" "0" let hasTeamCity = (not (buildNumber = "0")) // check if we have the TeamCity environment variable for build # set let preReleaseVersionSuffix = "beta" + (if (not (buildNumber = "0")) then (buildNumber) else DateTime.UtcNow.Ticks.ToString()) +let versionSuffix = + match (getBuildParam "nugetprerelease") with + | "dev" -> preReleaseVersionSuffix + | "" -> "" + | str -> str let releaseNotes = File.ReadLines (__SOURCE_DIRECTORY__ @@ "RELEASE_NOTES.md") |> ReleaseNotesHelper.parseReleaseNotes +let dockerTagVersion = + match versionSuffix with + | "" -> releaseNotes.AssemblyVersion + | str -> releaseNotes.AssemblyVersion + "-" + versionSuffix + let versionFromReleaseNotes = match releaseNotes.SemVer.PreRelease with | Some r -> r.Origin | None -> "" -let versionSuffix = - match (getBuildParam "nugetprerelease") with - | "dev" -> preReleaseVersionSuffix - | "" -> versionFromReleaseNotes - | str -> str - // Directories let toolsDir = __SOURCE_DIRECTORY__ @@ "tools" let output = __SOURCE_DIRECTORY__ @@ "bin" @@ -252,6 +256,92 @@ Target "PublishNuget" (fun _ -> projects |> Seq.iter (runSingleProject) ) +//-------------------------------------------------------------------------------- +// Docker images +//-------------------------------------------------------------------------------- +let GetDockerProjects = + let dockerFiles = !! "src/**/Dockerfile" // folders with Dockerfiles in it + + let projects = dockerFiles + |> Seq.map (fun dFile -> Path.GetDirectoryName(dFile)) + |> Seq.map (fun folder -> !! (folder + "/*.csproj")) + |> Seq.concat + + projects + +Target "PublishCode" (fun _ -> + ActivateFinalTarget "KillCreatedProcesses" + let projects = GetDockerProjects + + let runSingleProject project = + DotNetCli.Publish + (fun p -> + { p with + Project = project + Configuration = configuration + }) + + projects|> Seq.iter (runSingleProject) +) + +let mapDockerImageName (projectName:string) = + match projectName with + | str -> Some(str.ToLowerInvariant()) + +Target "BuildDockerImages" (fun _ -> + let projects = GetDockerProjects + + let remoteRegistryUrl = getBuildParamOrDefault "remoteRegistry" "" + + let composedGetFileNameWithoutExtension (p:string) = + System.IO.Path.GetFileNameWithoutExtension p + + let buildDockerImage imageName projectPath = + + let args = + if(hasBuildParam "remoteRegistry") then + StringBuilder() + |> append "build" + |> append "-t" + |> append (imageName + ":" + dockerTagVersion) + |> append "-t" + |> append (imageName + ":latest") + |> append "-t" + |> append (remoteRegistryUrl + "/" + imageName + ":" + dockerTagVersion) + |> append "-t" + |> append (remoteRegistryUrl + "/" + imageName + ":latest") + |> append "." + |> toText + else + StringBuilder() + |> append "build" + |> append "-t" + |> append (imageName + ":" + dockerTagVersion) + |> append "-t" + |> append (imageName + ":latest") + |> append "." + |> toText + + let composedGetDirName (p:string) = + System.IO.Path.GetDirectoryName p + + + ExecProcess(fun info -> + info.FileName <- "docker" + info.WorkingDirectory <- composedGetDirName projectPath + info.Arguments <- args) (System.TimeSpan.FromMinutes 5.0) (* Reasonably long-running task. *) + + let runSingleProject project = + let projectName = composedGetFileNameWithoutExtension project + let imageName = mapDockerImageName projectName + let result = match imageName with + | None -> 0 + | Some(name) -> buildDockerImage name project + if result <> 0 then failwithf "docker build failed. %s" project + + projects |> Seq.iter (runSingleProject) +) + //-------------------------------------------------------------------------------- // Documentation //-------------------------------------------------------------------------------- @@ -308,6 +398,7 @@ Target "Help" <| fun _ -> Target "BuildRelease" DoNothing Target "All" DoNothing +Target "Docker" DoNothing Target "Nuget" DoNothing // build dependencies @@ -323,6 +414,9 @@ Target "Nuget" DoNothing // docs "Clean" ==> "RestorePackages" ==> "BuildRelease" ==> "Docfx" +// Docker +"BuildRelease" ==> "PublishCode" ==> "BuildDockerImages" ==> "Docker" + // all "BuildRelease" ==> "All" "RunTests" ==> "All" diff --git a/src/tests/Helios.DedicatedThreadPool.IdleCpu.Program/Dockerfile b/src/tests/Helios.DedicatedThreadPool.IdleCpu.Program/Dockerfile index d39dd1f..9ad29f9 100644 --- a/src/tests/Helios.DedicatedThreadPool.IdleCpu.Program/Dockerfile +++ b/src/tests/Helios.DedicatedThreadPool.IdleCpu.Program/Dockerfile @@ -3,19 +3,6 @@ FROM mcr.microsoft.com/dotnet/core/runtime:3.1-buster-slim AS base WORKDIR /app -FROM mcr.microsoft.com/dotnet/core/sdk:3.1-buster AS build -WORKDIR /src -COPY ["tests/Helios.DedicatedThreadPool.IdleCpu.Program/Helios.DedicatedThreadPool.IdleCpu.Program.csproj", "tests/Helios.DedicatedThreadPool.IdleCpu.Program/"] -COPY ["core/Helios.DedicatedThreadPool/Helios.DedicatedThreadPool.csproj", "core/Helios.DedicatedThreadPool/"] -RUN dotnet restore "tests/Helios.DedicatedThreadPool.IdleCpu.Program/Helios.DedicatedThreadPool.IdleCpu.Program.csproj" -COPY . . -WORKDIR "/src/tests/Helios.DedicatedThreadPool.IdleCpu.Program" -RUN dotnet build "Helios.DedicatedThreadPool.IdleCpu.Program.csproj" -c Release -o /app/build +COPY ./bin/Release/netcoreapp3.1/publish /app -FROM build AS publish -RUN dotnet publish "Helios.DedicatedThreadPool.IdleCpu.Program.csproj" -c Release -o /app/publish - -FROM base AS final -WORKDIR /app -COPY --from=publish /app/publish . ENTRYPOINT ["dotnet", "Helios.DedicatedThreadPool.IdleCpu.Program.dll"] \ No newline at end of file diff --git a/src/tests/Helios.DedicatedThreadPool.IdleCpu.Program/Helios - Backup.DedicatedThreadPool.IdleCpu.Program.csproj b/src/tests/Helios.DedicatedThreadPool.IdleCpu.Program/Helios - Backup.DedicatedThreadPool.IdleCpu.Program.csproj deleted file mode 100644 index 22030b0..0000000 --- a/src/tests/Helios.DedicatedThreadPool.IdleCpu.Program/Helios - Backup.DedicatedThreadPool.IdleCpu.Program.csproj +++ /dev/null @@ -1,16 +0,0 @@ - - - - Exe - netcoreapp3.1 - false - Linux - - - - - - - - - diff --git a/src/tests/Helios.DedicatedThreadPool.IdleCpu.Program/Program.cs b/src/tests/Helios.DedicatedThreadPool.IdleCpu.Program/Program.cs index 576d865..57cb8f8 100644 --- a/src/tests/Helios.DedicatedThreadPool.IdleCpu.Program/Program.cs +++ b/src/tests/Helios.DedicatedThreadPool.IdleCpu.Program/Program.cs @@ -1,23 +1,77 @@ using System; +using System.Collections.Concurrent; +using System.Linq; +using System.Threading; using System.Threading.Tasks; using Helios.Concurrency; namespace Helios.DedicatedThreadPool.IdleCpu.Program { + /// + /// Create random numbers with Thread-specific seeds. + /// + /// Borrowed form Jon Skeet's brilliant C# in Depth: http://csharpindepth.com/Articles/Chapter12/Random.aspx + /// + public static class ThreadLocalRandom + { + private static int _seed = Environment.TickCount; + + private static ThreadLocal _rng = new ThreadLocal(() => new Random(Interlocked.Increment(ref _seed))); + + /// + /// The current random number seed available to this thread + /// + public static Random Current + { + get + { + return _rng.Value; + } + } + } + class Program { static async Task Main(string[] args) { - var maxThreads = Math.Max(4, Environment.ProcessorCount); + // set a huge number of threads per core to exaggerate idle CPU effects when we pre-allocate + var maxThreads = Math.Max(4, Environment.ProcessorCount)*100; Console.WriteLine("Starting Idle Cpu Test with Following Configuration"); Console.WriteLine("MaxThreads: {0}", maxThreads); var settings = new DedicatedThreadPoolSettings(maxThreads); var threadPool = new Concurrency.DedicatedThreadPool(settings); + var concurrentBag = new ConcurrentBag(); + + void DoWork(int count) + { + Console.WriteLine("Count: {0}", count); + Thread.Sleep(100); + concurrentBag.Add(Thread.CurrentThread.ManagedThreadId); + if (count % 3 == 0) + threadPool.QueueUserWorkItem(() => DoWork(ThreadLocalRandom.Current.Next(0,7))); + } + + + + foreach (var i in Enumerable.Range(0, maxThreads*100)) + { + threadPool.QueueUserWorkItem(() => + { + DoWork(i); + }); + } + + threadPool.QueueUserWorkItem(() => + { + Console.WriteLine("Found {0} active threads", concurrentBag.ToArray().Distinct().Count()); + }); // force background Helios threads to run - await Task.Delay(TimeSpan.FromMinutes(2)); + await Task.Delay(TimeSpan.FromMinutes(3)); + + Console.WriteLine("Exited with {0} active threads", concurrentBag.ToArray().Distinct().Count()); } } }