diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/CounterGroup.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/CounterGroup.cs index b07896fb0c2493..bbfb533c699e73 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/CounterGroup.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/CounterGroup.cs @@ -162,8 +162,8 @@ private void EnableTimer(float pollingIntervalInSeconds) if (s_pollingThread == null) { s_pollingThreadSleepEvent = new AutoResetEvent(false); + s_needsResetCounterGroupEvent = new AutoResetEvent(false); s_counterGroupEnabledList = new List(); - s_needsResetCounterGroup = []; s_pollingThread = new Thread(PollForValues) { IsBackground = true, @@ -172,19 +172,18 @@ private void EnableTimer(float pollingIntervalInSeconds) s_pollingThread.InternalUnsafeStart(); } + s_needsResetCounterGroup = this; if (!s_counterGroupEnabledList!.Contains(this)) { s_counterGroupEnabledList.Add(this); } - if (!s_needsResetCounterGroup!.Contains(this)) - { - s_needsResetCounterGroup.Add(this); - } - // notify the polling thread that the polling interval may have changed and the sleep should // be recomputed s_pollingThreadSleepEvent!.Set(); + + // reset counters before leaving this method to prevent them from being overwritten + s_needsResetCounterGroupEvent!.WaitOne(); } } @@ -193,7 +192,8 @@ private void DisableTimer() Debug.Assert(Monitor.IsEntered(s_counterGroupLock)); _pollingIntervalInMilliseconds = 0; s_counterGroupEnabledList?.Remove(this); - s_needsResetCounterGroup?.Remove(this); + if (s_needsResetCounterGroup == this) + s_needsResetCounterGroup = null; } private void OnTimer() @@ -247,7 +247,9 @@ private void OnTimer() private static AutoResetEvent? s_pollingThreadSleepEvent; private static List? s_counterGroupEnabledList; - private static List? s_needsResetCounterGroup; + private static CounterGroup? s_needsResetCounterGroup; + private static AutoResetEvent? s_needsResetCounterGroupEvent; + private static void PollForValues() { AutoResetEvent? sleepEvent = null; @@ -257,18 +259,13 @@ private static void PollForValues() // calling into the callbacks can cause a re-entrancy into CounterGroup.Enable() // and result in a deadlock. (See https://github.com/dotnet/runtime/issues/40190 for details) var onTimers = new List(); - var countersToReset = new List(); while (true) { + ResetCounterGroupIfNeeded(); + int sleepDurationInMilliseconds = int.MaxValue; lock (s_counterGroupLock) { - foreach (CounterGroup counterGroup in s_needsResetCounterGroup!) - { - countersToReset.AddRange(counterGroup._counters); - } - s_needsResetCounterGroup.Clear(); - sleepEvent = s_pollingThreadSleepEvent; foreach (CounterGroup counterGroup in s_counterGroupEnabledList!) { @@ -284,23 +281,6 @@ private static void PollForValues() } } - foreach (DiagnosticCounter counter in countersToReset) - { - if (counter is IncrementingEventCounter ieCounter) - { - ieCounter.UpdateMetric(); - } - else if (counter is IncrementingPollingCounter ipCounter) - { - ipCounter.UpdateMetric(); - } - else if (counter is EventCounter eCounter) - { - eCounter.ResetStatistics(); - } - } - countersToReset.Clear(); - foreach (CounterGroup onTimer in onTimers) { onTimer.OnTimer(); @@ -314,6 +294,36 @@ private static void PollForValues() } } + private static void ResetCounterGroupIfNeeded() + { + if (s_needsResetCounterGroup == null) + { + return; + } + + // we can access _counters without s_counterGroupLock here, because the lock is held by a thread waiting on s_needsResetCounterGroupEvent, + // so the counters can't be changed until s_needsResetCounterGroupEvent is fired + foreach (DiagnosticCounter counter in s_needsResetCounterGroup._counters) + { + if (counter is IncrementingEventCounter ieCounter) + { + ieCounter.UpdateMetric(); + } + else if (counter is IncrementingPollingCounter ipCounter) + { + ipCounter.UpdateMetric(); + } + else if (counter is EventCounter eCounter) + { + eCounter.ResetStatistics(); + } + } + + s_needsResetCounterGroup = null; + s_needsResetCounterGroupEvent!.Set(); + } + + #endregion // Timer Processing }