99using System . Runtime . CompilerServices ;
1010using System . Runtime . ExceptionServices ;
1111using System . Threading ;
12+ using System . Threading . Tasks ;
1213using Datadog . Trace . Logging ;
1314using Datadog . Trace . Vendors . StatsdClient ;
1415
@@ -39,8 +40,11 @@ internal class RuntimeMetricsWriter : IDisposable
3940
4041 private readonly TimeSpan _delay ;
4142
43+ #if NETFRAMEWORK
44+ private readonly Task _pushEventsTask ;
45+ #else
4246 private readonly Timer _timer ;
43-
47+ #endif
4448 private readonly IRuntimeMetricsListener _listener ;
4549
4650 private readonly bool _enableProcessMetrics ;
@@ -120,7 +124,11 @@ internal RuntimeMetricsWriter(IDogStatsd statsd, TimeSpan delay, bool inAzureApp
120124 Log . Warning ( ex , "Unable to initialize runtime listener, some runtime metrics will be missing" ) ;
121125 }
122126
127+ #if NETFRAMEWORK
128+ _pushEventsTask = Task . Factory . StartNew ( PushEvents , TaskCreationOptions . LongRunning ) ;
129+ #else
123130 _timer = new Timer ( _ => PushEvents ( ) , null , delay , Timeout . InfiniteTimeSpan ) ;
131+ #endif
124132 }
125133
126134 /// <summary>
@@ -137,6 +145,12 @@ public void Dispose()
137145 }
138146
139147 Log . Debug ( "Disposing Runtime Metrics timer" ) ;
148+ #if NETFRAMEWORK
149+ if ( _pushEventsTask . Wait ( TimeSpan . FromMilliseconds ( 5_000 ) ) )
150+ {
151+ Log . Warning ( "Failed to dispose Runtime Metrics timer after 5 seconds" ) ;
152+ }
153+ #else
140154 // Callbacks can occur after the Dispose() method overload has been called,
141155 // because the timer queues callbacks for execution by thread pool threads.
142156 // Using the Dispose(WaitHandle) method overload to waits until all callbacks have completed.
@@ -148,7 +162,7 @@ public void Dispose()
148162 Log . Warning ( "Failed to dispose Runtime Metrics timer after 5 seconds" ) ;
149163 }
150164 }
151-
165+ #endif
152166 Log . Debug ( "Disposing other resources for Runtime Metrics" ) ;
153167 AppDomain . CurrentDomain . FirstChanceException -= FirstChanceException ;
154168 // We don't dispose runtime metrics on .NET Core because of https://github.com/dotnet/runtime/issues/103480
@@ -256,6 +270,23 @@ internal void PushEvents()
256270 {
257271 var callbackExecutionDuration = DateTime . UtcNow - now ;
258272
273+ #if NETFRAMEWORK
274+ // Ideally we'd wait for the full time, but we need to make sure we shutdown in a relatively timely fashion
275+ const int loopDurationMs = 200 ;
276+ var newDelay = ( int ) ( _delay - callbackExecutionDuration ) . TotalMilliseconds ;
277+
278+ // Missed it, so just reset
279+ if ( newDelay <= 0 )
280+ {
281+ newDelay = ( int ) _delay . TotalMilliseconds ;
282+ }
283+
284+ while ( newDelay > 0 && Volatile . Read ( ref _disposed ) == 0 )
285+ {
286+ Thread . Sleep ( Math . Min ( newDelay , loopDurationMs ) ) ;
287+ newDelay -= loopDurationMs ;
288+ }
289+ #else
259290 var newDelay = _delay - callbackExecutionDuration ;
260291
261292 if ( newDelay < TimeSpan . Zero )
@@ -270,6 +301,7 @@ internal void PushEvents()
270301 catch ( ObjectDisposedException )
271302 {
272303 }
304+ #endif
273305 }
274306 }
275307
0 commit comments