@@ -21,6 +21,7 @@ internal class CounterGroup
21
21
{
22
22
private readonly EventSource _eventSource ;
23
23
private readonly List < DiagnosticCounter > _counters ;
24
+ private static readonly object s_counterGroupLock = new object ( ) ;
24
25
25
26
internal CounterGroup ( EventSource eventSource )
26
27
{
@@ -31,13 +32,13 @@ internal CounterGroup(EventSource eventSource)
31
32
32
33
internal void Add ( DiagnosticCounter eventCounter )
33
34
{
34
- lock ( this ) // Lock the CounterGroup
35
+ lock ( s_counterGroupLock ) // Lock the CounterGroup
35
36
_counters . Add ( eventCounter ) ;
36
37
}
37
38
38
39
internal void Remove ( DiagnosticCounter eventCounter )
39
40
{
40
- lock ( this ) // Lock the CounterGroup
41
+ lock ( s_counterGroupLock ) // Lock the CounterGroup
41
42
_counters . Remove ( eventCounter ) ;
42
43
}
43
44
@@ -56,17 +57,17 @@ private void OnEventSourceCommand(object? sender, EventCommandEventArgs e)
56
57
57
58
if ( e . Arguments . TryGetValue ( "EventCounterIntervalSec" , out string ? valueStr ) && float . TryParse ( valueStr , out float value ) )
58
59
{
59
- lock ( this ) // Lock the CounterGroup
60
+ lock ( s_counterGroupLock ) // Lock the CounterGroup
60
61
{
61
62
EnableTimer ( value ) ;
62
63
}
63
64
}
64
65
}
65
66
else if ( e . Command == EventCommand . Disable )
66
67
{
67
- lock ( this )
68
+ lock ( s_counterGroupLock )
68
69
{
69
- _pollingIntervalInMilliseconds = 0 ;
70
+ DisableTimer ( ) ;
70
71
}
71
72
}
72
73
}
@@ -79,11 +80,10 @@ private void OnEventSourceCommand(object? sender, EventCommandEventArgs e)
79
80
// this table provides the mapping from EventSource -> CounterGroup
80
81
// which represents this 'attached' information.
81
82
private static WeakReference < CounterGroup > [ ] ? s_counterGroups ;
82
- private static readonly object s_counterGroupsLock = new object ( ) ;
83
83
84
84
private static void EnsureEventSourceIndexAvailable ( int eventSourceIndex )
85
85
{
86
- Debug . Assert ( Monitor . IsEntered ( s_counterGroupsLock ) ) ;
86
+ Debug . Assert ( Monitor . IsEntered ( s_counterGroupLock ) ) ;
87
87
if ( CounterGroup . s_counterGroups == null )
88
88
{
89
89
CounterGroup . s_counterGroups = new WeakReference < CounterGroup > [ eventSourceIndex + 1 ] ;
@@ -98,7 +98,7 @@ private static void EnsureEventSourceIndexAvailable(int eventSourceIndex)
98
98
99
99
internal static CounterGroup GetCounterGroup ( EventSource eventSource )
100
100
{
101
- lock ( s_counterGroupsLock )
101
+ lock ( s_counterGroupLock )
102
102
{
103
103
int eventSourceIndex = EventListener . EventSourceIndex ( eventSource ) ;
104
104
EnsureEventSourceIndexAvailable ( eventSourceIndex ) ;
@@ -120,32 +120,20 @@ internal static CounterGroup GetCounterGroup(EventSource eventSource)
120
120
121
121
private DateTime _timeStampSinceCollectionStarted ;
122
122
private int _pollingIntervalInMilliseconds ;
123
- private Timer ? _pollingTimer ;
124
-
125
- private void DisposeTimer ( )
126
- {
127
- Debug . Assert ( Monitor . IsEntered ( this ) ) ;
128
- if ( _pollingTimer != null )
129
- {
130
- _pollingTimer . Dispose ( ) ;
131
- _pollingTimer = null ;
132
- }
133
- }
123
+ private DateTime _nextPollingTimeStamp ;
134
124
135
125
private void EnableTimer ( float pollingIntervalInSeconds )
136
126
{
137
- Debug . Assert ( Monitor . IsEntered ( this ) ) ;
127
+ Debug . Assert ( Monitor . IsEntered ( s_counterGroupLock ) ) ;
138
128
if ( pollingIntervalInSeconds <= 0 )
139
129
{
140
- DisposeTimer ( ) ;
141
130
_pollingIntervalInMilliseconds = 0 ;
142
131
}
143
132
else if ( _pollingIntervalInMilliseconds == 0 || pollingIntervalInSeconds * 1000 < _pollingIntervalInMilliseconds )
144
133
{
145
- Debug . WriteLine ( "Polling interval changed at " + DateTime . UtcNow . ToString ( "mm.ss.ffffff" ) ) ;
146
134
_pollingIntervalInMilliseconds = ( int ) ( pollingIntervalInSeconds * 1000 ) ;
147
- DisposeTimer ( ) ;
148
135
ResetCounters ( ) ; // Reset statistics for counters before we start the thread.
136
+
149
137
_timeStampSinceCollectionStarted = DateTime . UtcNow ;
150
138
// Don't capture the current ExecutionContext and its AsyncLocals onto the timer causing them to live forever
151
139
bool restoreFlow = false ;
@@ -157,7 +145,25 @@ private void EnableTimer(float pollingIntervalInSeconds)
157
145
restoreFlow = true ;
158
146
}
159
147
160
- _pollingTimer = new Timer ( s => ( ( CounterGroup ) s ! ) . OnTimer ( null ) , this , _pollingIntervalInMilliseconds , _pollingIntervalInMilliseconds ) ;
148
+ _nextPollingTimeStamp = DateTime . UtcNow + new TimeSpan ( 0 , 0 , ( int ) pollingIntervalInSeconds ) ;
149
+
150
+ // Create the polling thread and init all the shared state if needed
151
+ if ( s_pollingThread == null )
152
+ {
153
+ s_pollingThreadSleepEvent = new AutoResetEvent ( false ) ;
154
+ s_counterGroupEnabledList = new List < CounterGroup > ( ) ;
155
+ s_pollingThread = new Thread ( PollForValues ) { IsBackground = true } ;
156
+ s_pollingThread . Start ( ) ;
157
+ }
158
+
159
+ if ( ! s_counterGroupEnabledList ! . Contains ( this ) )
160
+ {
161
+ s_counterGroupEnabledList . Add ( this ) ;
162
+ }
163
+
164
+ // notify the polling thread that the polling interval may have changed and the sleep should
165
+ // be recomputed
166
+ s_pollingThreadSleepEvent ! . Set ( ) ;
161
167
}
162
168
finally
163
169
{
@@ -168,9 +174,15 @@ private void EnableTimer(float pollingIntervalInSeconds)
168
174
}
169
175
}
170
176
177
+ private void DisableTimer ( )
178
+ {
179
+ _pollingIntervalInMilliseconds = 0 ;
180
+ s_counterGroupEnabledList ? . Remove ( this ) ;
181
+ }
182
+
171
183
private void ResetCounters ( )
172
184
{
173
- lock ( this ) // Lock the CounterGroup
185
+ lock ( s_counterGroupLock ) // Lock the CounterGroup
174
186
{
175
187
foreach ( var counter in _counters )
176
188
{
@@ -190,63 +202,65 @@ private void ResetCounters()
190
202
}
191
203
}
192
204
193
- private void OnTimer ( object ? state )
205
+ private void OnTimer ( )
194
206
{
195
- Debug . WriteLine ( "Timer fired at " + DateTime . UtcNow . ToString ( "mm.ss.ffffff" ) ) ;
196
- lock ( this ) // Lock the CounterGroup
207
+ Debug . Assert ( Monitor . IsEntered ( s_counterGroupLock ) ) ;
208
+ if ( _eventSource . IsEnabled ( ) )
197
209
{
198
- if ( _eventSource . IsEnabled ( ) )
199
- {
200
- DateTime now = DateTime . UtcNow ;
201
- TimeSpan elapsed = now - _timeStampSinceCollectionStarted ;
210
+ DateTime now = DateTime . UtcNow ;
211
+ TimeSpan elapsed = now - _timeStampSinceCollectionStarted ;
202
212
203
- foreach ( var counter in _counters )
204
- {
205
- counter . WritePayload ( ( float ) elapsed . TotalSeconds , _pollingIntervalInMilliseconds ) ;
206
- }
207
- _timeStampSinceCollectionStarted = now ;
208
- }
209
- else
213
+ foreach ( var counter in _counters )
210
214
{
211
- DisposeTimer ( ) ;
215
+ counter . WritePayload ( ( float ) elapsed . TotalSeconds , _pollingIntervalInMilliseconds ) ;
212
216
}
217
+ _timeStampSinceCollectionStarted = now ;
218
+
219
+ do
220
+ {
221
+ _nextPollingTimeStamp += new TimeSpan ( 0 , 0 , 0 , 0 , _pollingIntervalInMilliseconds ) ;
222
+ } while ( _nextPollingTimeStamp <= now ) ;
213
223
}
214
224
}
215
225
216
- #region PCL timer hack
217
226
218
- #if ES_BUILD_PCL
219
- internal delegate void TimerCallback ( object state ) ;
220
-
221
- internal sealed class Timer : CancellationTokenSource , IDisposable
222
- {
223
- private int _period ;
224
- private TimerCallback _callback ;
225
- private object _state ;
226
227
227
- internal Timer ( TimerCallback callback , object state , int dueTime , int period )
228
- {
229
- _callback = callback ;
230
- _state = state ;
231
- _period = period ;
232
- Schedule ( dueTime ) ;
233
- }
228
+ private static Thread ? s_pollingThread ;
229
+ // Used for sleeping for a certain amount of time while allowing the thread to be woken up
230
+ private static AutoResetEvent ? s_pollingThreadSleepEvent ;
234
231
235
- private void Schedule ( int dueTime )
236
- {
237
- Task . Delay ( dueTime , Token ) . ContinueWith ( OnTimer , null , CancellationToken . None , TaskContinuationOptions . ExecuteSynchronously | TaskContinuationOptions . OnlyOnRanToCompletion , TaskScheduler . Default ) ;
238
- }
232
+ private static List < CounterGroup > ? s_counterGroupEnabledList ;
239
233
240
- private void OnTimer ( Task t , object s )
234
+ private static void PollForValues ( )
235
+ {
236
+ AutoResetEvent ? sleepEvent = null ;
237
+ while ( true )
241
238
{
242
- Schedule ( _period ) ;
243
- _callback ( _state ) ;
239
+ int sleepDurationInMilliseconds = Int32 . MaxValue ;
240
+ lock ( s_counterGroupLock )
241
+ {
242
+ sleepEvent = s_pollingThreadSleepEvent ;
243
+ foreach ( CounterGroup counterGroup in s_counterGroupEnabledList ! )
244
+ {
245
+ DateTime now = DateTime . UtcNow ;
246
+ if ( counterGroup . _nextPollingTimeStamp < now + new TimeSpan ( 0 , 0 , 0 , 0 , 1 ) )
247
+ {
248
+ counterGroup . OnTimer ( ) ;
249
+ }
250
+
251
+ int millisecondsTillNextPoll = ( int ) ( ( counterGroup . _nextPollingTimeStamp - now ) . TotalMilliseconds ) ;
252
+ millisecondsTillNextPoll = Math . Max ( 1 , millisecondsTillNextPoll ) ;
253
+ sleepDurationInMilliseconds = Math . Min ( sleepDurationInMilliseconds , millisecondsTillNextPoll ) ;
254
+ }
255
+ }
256
+ if ( sleepDurationInMilliseconds == Int32 . MaxValue )
257
+ {
258
+ sleepDurationInMilliseconds = - 1 ; // WaitOne uses -1 to mean infinite
259
+ }
260
+ sleepEvent ? . WaitOne ( sleepDurationInMilliseconds ) ;
244
261
}
245
-
246
- public new void Dispose ( ) { base . Cancel ( ) ; }
247
262
}
248
- #endif
249
- #endregion // PCL timer hack
263
+
250
264
251
265
#endregion // Timer Processing
252
266
0 commit comments