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/.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 b918e85..4ac5f45 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,11 @@ 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 +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 @@ -43,6 +47,14 @@ 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 + {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 @@ -51,5 +63,10 @@ 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} + {F0A0B730-337D-4FE2-AA41-54535CF70726} = {741F51D5-FB48-4AE3-837E-3C32FAC6584E} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {DD52A4C9-1308-4946-82E0-9AD4884CAA85} EndGlobalSection EndGlobal 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 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/Dockerfile b/src/tests/Helios.DedicatedThreadPool.IdleCpu.Program/Dockerfile new file mode 100644 index 0000000..9ad29f9 --- /dev/null +++ b/src/tests/Helios.DedicatedThreadPool.IdleCpu.Program/Dockerfile @@ -0,0 +1,8 @@ +#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 + +COPY ./bin/Release/netcoreapp3.1/publish /app + +ENTRYPOINT ["dotnet", "Helios.DedicatedThreadPool.IdleCpu.Program.dll"] \ No newline at end of file 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..c6939e0 --- /dev/null +++ b/src/tests/Helios.DedicatedThreadPool.IdleCpu.Program/Helios.DedicatedThreadPool.IdleCpu.Program.csproj @@ -0,0 +1,17 @@ + + + + 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 new file mode 100644 index 0000000..57cb8f8 --- /dev/null +++ b/src/tests/Helios.DedicatedThreadPool.IdleCpu.Program/Program.cs @@ -0,0 +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) + { + // 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(3)); + + Console.WriteLine("Exited with {0} active threads", concurrentBag.ToArray().Distinct().Count()); + } + } +} 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..5a2b5ac 100644 --- a/src/tests/Helios.DedicatedThreadPool.Tests/DedicatedThreadPoolTaskSchedulerTests.cs +++ b/src/tests/Helios.DedicatedThreadPool.Tests/DedicatedThreadPoolTaskSchedulerTests.cs @@ -3,33 +3,25 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using NUnit.Framework; +using Xunit; namespace Helios.Concurrency.Tests { - [TestFixture] - 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(); @@ -51,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(); @@ -74,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(); + } } } 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 + + + + + +