private void OnTimer() { if (_eventSource.IsEnabled()) { DateTime now; TimeSpan elapsed; int pollingIntervalInMilliseconds; DiagnosticCounter[] counters; lock (s_counterGroupLock) { now = DateTime.UtcNow; elapsed = now - _timeStampSinceCollectionStarted; pollingIntervalInMilliseconds = _pollingIntervalInMilliseconds; counters = new DiagnosticCounter[_counters.Count]; _counters.CopyTo(counters); } // MUST keep out of the scope of s_counterGroupLock because this will cause WritePayload // callback can be re-entrant to CounterGroup (i.e. it's possible it calls back into EnableTimer() // above, since WritePayload callback can contain user code that can invoke EventSource constructor // and lead to a deadlock. (See https://github.com/dotnet/runtime/issues/40190 for details) foreach (DiagnosticCounter counter in counters) { // NOTE: It is still possible for a race condition to occur here. An example is if the session // that subscribed to these batch of counters was disabled and it was immediately enabled in // a different session, some of the counter data that was supposed to be written to the old // session can now "overflow" into the new session. // This problem pre-existed to this change (when we used to hold lock in the call to WritePayload): // the only difference being the old behavior caused the entire batch of counters to be either // written to the old session or the new session. The behavior change is not being treated as a // significant problem to address for now, but we can come back and address it if it turns out to // be an actual issue. counter.WritePayload((float)elapsed.TotalSeconds, pollingIntervalInMilliseconds); } lock (s_counterGroupLock) { _timeStampSinceCollectionStarted = now; TimeSpan delta = now - _nextPollingTimeStamp; delta = _pollingIntervalInMilliseconds > delta.TotalMilliseconds ? TimeSpan.FromMilliseconds(_pollingIntervalInMilliseconds) : delta; if (_pollingIntervalInMilliseconds > 0) { _nextPollingTimeStamp += TimeSpan.FromMilliseconds(_pollingIntervalInMilliseconds * Math.Ceiling(delta.TotalMilliseconds / _pollingIntervalInMilliseconds)); } } } }
internal void Remove(DiagnosticCounter eventCounter) { lock (s_counterGroupLock) // Lock the CounterGroup _counters.Remove(eventCounter); }
internal void Add(DiagnosticCounter eventCounter) { lock (s_counterGroupLock) // Lock the CounterGroup _counters.Add(eventCounter); }