diff --git a/src/System.Private.CoreLib/src/System/Threading/Timer.Unix.cs b/src/System.Private.CoreLib/src/System/Threading/Timer.Unix.cs
index 9a51dfbd057..f5ec9e54ee0 100644
--- a/src/System.Private.CoreLib/src/System/Threading/Timer.Unix.cs
+++ b/src/System.Private.CoreLib/src/System/Threading/Timer.Unix.cs
@@ -19,12 +19,12 @@ internal partial class TimerQueue
/// This event is used by the timer thread to wait for timer expiration. It is also
/// used to notify the timer thread that a new timer has been set.
///
- private static AutoResetEvent s_timerEvent;
+ private AutoResetEvent _timerEvent;
///
/// This field stores the value of next timer that the timer thread should install.
///
- private static volatile int s_nextTimerDuration;
+ private volatile int _nextTimerDuration;
private TimerQueue(int id)
{
@@ -35,21 +35,21 @@ private bool SetTimer(uint actualDuration)
// Note: AutoResetEvent.WaitOne takes an Int32 value as a timeout.
// The TimerQueue code ensures that timer duration is not greater than max Int32 value
Debug.Assert(actualDuration <= (uint)int.MaxValue);
- s_nextTimerDuration = (int)actualDuration;
+ _nextTimerDuration = (int)actualDuration;
// If this is the first time the timer is set then we need to create a thread that
// will manage and respond to timer requests. Otherwise, simply signal the timer thread
// to notify it that the timer duration has changed.
- if (s_timerEvent == null)
+ if (_timerEvent == null)
{
- s_timerEvent = new AutoResetEvent(false);
+ _timerEvent = new AutoResetEvent(false);
RuntimeThread thread = RuntimeThread.Create(TimerThread, 0);
thread.IsBackground = true; // Keep this thread from blocking process shutdown
thread.Start();
}
else
{
- s_timerEvent.Set();
+ _timerEvent.Set();
}
return true;
@@ -65,7 +65,7 @@ private void TimerThread()
int currentTimerInterval;
// Get wait time for the next timer
- currentTimerInterval = Interlocked.Exchange(ref s_nextTimerDuration, Timeout.Infinite);
+ currentTimerInterval = Interlocked.Exchange(ref _nextTimerDuration, Timeout.Infinite);
for (;;)
{
@@ -73,7 +73,7 @@ private void TimerThread()
// We will be woken up because either 1) the wait times out, which will indicate that
// the current timer has expired and/or 2) the TimerQueue installs a new (earlier) timer.
int startWait = TickCount;
- bool timerHasExpired = !s_timerEvent.WaitOne(currentTimerInterval);
+ bool timerHasExpired = !_timerEvent.WaitOne(currentTimerInterval);
uint elapsedTime = (uint)(TickCount - startWait);
// The timer event can be set after this thread reads the new timer interval but before it enters
@@ -99,11 +99,11 @@ private void TimerThread()
// When FireNextTimers() installs a new timer, it also sets the timer event.
// Reset the event so the timer thread is not woken up right away unnecessary.
- s_timerEvent.Reset();
+ _timerEvent.Reset();
currentTimerInterval = Timeout.Infinite;
}
- int nextTimerInterval = Interlocked.Exchange(ref s_nextTimerDuration, Timeout.Infinite);
+ int nextTimerInterval = Interlocked.Exchange(ref _nextTimerDuration, Timeout.Infinite);
if (nextTimerInterval != Timeout.Infinite)
{
currentTimerInterval = nextTimerInterval;
diff --git a/tests/src/Simple/Threading/Threading.cs b/tests/src/Simple/Threading/Threading.cs
index 67fb50d6fa1..93879a47289 100644
--- a/tests/src/Simple/Threading/Threading.cs
+++ b/tests/src/Simple/Threading/Threading.cs
@@ -38,6 +38,9 @@ public static int Main()
//Console.WriteLine(" WaitSubsystemTests.MutexMaximumReacquireCountTest");
//WaitSubsystemTests.MutexMaximumReacquireCountTest();
+ Console.WriteLine(" TimersCreatedConcurrentlyOnDifferentThreadsAllFire");
+ TimerTests.TimersCreatedConcurrentlyOnDifferentThreadsAllFire();
+
Console.WriteLine(" ThreadPoolTests.RunProcessorCountItemsInParallel");
ThreadPoolTests.RunProcessorCountItemsInParallel();
@@ -837,6 +840,62 @@ public static void MutexMaximumReacquireCountTest()
}
}
+internal static class TimerTests
+{
+ [Fact]
+ public static void TimersCreatedConcurrentlyOnDifferentThreadsAllFire()
+ {
+ int processorCount = Environment.ProcessorCount;
+
+ int timerTickCount = 0;
+ TimerCallback timerCallback = data => Interlocked.Increment(ref timerTickCount);
+
+ var threadStarted = new AutoResetEvent(false);
+ var createTimers = new ManualResetEvent(false);
+ var timers = new Timer[processorCount];
+ Action