Expose and test Timer.ActiveCount
authorKoundinya Veluri <kouvel@microsoft.com>
Mon, 10 Jun 2019 21:24:55 +0000 (14:24 -0700)
committerStephen Toub <stoub@microsoft.com>
Wed, 12 Jun 2019 23:55:13 +0000 (19:55 -0400)
- Depends on https://github.com/dotnet/coreclr/pull/25061
- Fixes https://github.com/dotnet/corefx/issues/38422

Commit migrated from https://github.com/dotnet/corefx/commit/e2c3de82029f2187b3b0ad36a060e5f7e2ac0e93

src/libraries/System.Threading.Timer/ref/System.Threading.Timer.cs
src/libraries/System.Threading.Timer/src/ApiCompatBaseline.uapaot.txt [new file with mode: 0644]
src/libraries/System.Threading.Timer/tests/System.Threading.Timer.Tests.csproj
src/libraries/System.Threading.Timer/tests/TimerMetricsTests.cs [new file with mode: 0644]

index ba2fa66..7f660bb 100644 (file)
@@ -20,6 +20,7 @@ namespace System.Threading
         public bool Change(System.TimeSpan dueTime, System.TimeSpan period) { throw null; }
         [System.CLSCompliantAttribute(false)]
         public bool Change(uint dueTime, uint period) { throw null; }
+        public static long ActiveCount { get { throw null; } }
         public void Dispose() { }
         public bool Dispose(System.Threading.WaitHandle notifyObject) { throw null; }
         public System.Threading.Tasks.ValueTask DisposeAsync() { throw null; }
diff --git a/src/libraries/System.Threading.Timer/src/ApiCompatBaseline.uapaot.txt b/src/libraries/System.Threading.Timer/src/ApiCompatBaseline.uapaot.txt
new file mode 100644 (file)
index 0000000..1acb8f0
--- /dev/null
@@ -0,0 +1,2 @@
+Compat issues with assembly System.Threading.Timer:
+MembersMustExist : Member 'System.Threading.Timer.ActiveCount.get()' does not exist in the implementation but it does exist in the contract.
index 5c8fae3..4928271 100644 (file)
@@ -10,5 +10,9 @@
     <Compile Include="TimerChangeTests.cs" />
     <Compile Include="TimerFiringTests.cs" />
     <Compile Include="TimerDisposeTests.cs" />
+    <Compile Include="TimerMetricsTests.cs" />
+    <Compile Include="$(CommonTestPath)\System\Threading\ThreadTestHelpers.cs">
+      <Link>CommonTest\System\Threading\ThreadTestHelpers.cs</Link>
+    </Compile>
   </ItemGroup>
 </Project>
\ No newline at end of file
diff --git a/src/libraries/System.Threading.Timer/tests/TimerMetricsTests.cs b/src/libraries/System.Threading.Timer/tests/TimerMetricsTests.cs
new file mode 100644 (file)
index 0000000..cff37b6
--- /dev/null
@@ -0,0 +1,76 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tests;
+using Microsoft.DotNet.RemoteExecutor;
+using Xunit;
+
+public static partial class TimerMetricsTests
+{
+    [Fact]
+    public static void CountTest()
+    {
+        RemoteExecutor.Invoke(() =>
+        {
+            const int TimersPerThread = 64;
+            int processorCount = Environment.ProcessorCount;
+            int totalTimerCount = processorCount * TimersPerThread;
+
+            var timers = new List<Timer>(totalTimerCount);
+            TimerCallback timerCallback = _ => { };
+            var startCreateTimerThreads = new ManualResetEvent(false);
+            Action createTimerThreadStart = () =>
+            {
+                startCreateTimerThreads.WaitOne();
+                for (int i = 0; i < TimersPerThread; ++i)
+                {
+                    lock (timers)
+                    {
+                        timers.Add(
+                            new Timer(
+                                timerCallback,
+                                null,
+                                ThreadTestHelpers.UnexpectedTimeoutMilliseconds,
+                                ThreadTestHelpers.UnexpectedTimeoutMilliseconds));
+                        Assert.True(Timer.ActiveCount >= timers.Count);
+                    }
+                }
+            };
+            var waitsForThread = new Action[processorCount];
+            for (int i = 0; i < processorCount; ++i)
+            {
+                Thread t = ThreadTestHelpers.CreateGuardedThread(out waitsForThread[i], createTimerThreadStart);
+                t.IsBackground = true;
+                t.Start();
+            }
+
+            startCreateTimerThreads.Set();
+            foreach (Action waitForThread in waitsForThread)
+            {
+                waitForThread();
+            }
+
+            // To leave some room for unknown timers to be scheduled and removed, remove a large number of timers at a time and
+            // verify that the timer count has decreased
+            while (timers.Count > 0)
+            {
+                long timerCountBeforeRemove = Timer.ActiveCount;
+                int endIndex = timers.Count - processorCount * 8;
+                for (int i = timers.Count - 1; i >= Math.Max(0, endIndex); --i)
+                {
+                    timers[i].Dispose();
+                    timers.RemoveAt(i);
+                }
+
+                if (endIndex >= 0)
+                {
+                    Assert.True(Timer.ActiveCount < timerCountBeforeRemove);
+                }
+            }
+        }).Dispose();
+    }
+}