diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/Instrument.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/Instrument.cs index 8b87501f26a695..20c4607f927b1f 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/Instrument.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/Instrument.cs @@ -89,17 +89,6 @@ protected void Publish() return; } - // MeterListener has a static constructor that creates runtime metrics instruments. - // We need to ensure this static constructor is called before starting to publish the instrument. - // This is necessary because creating runtime metrics instruments will cause re-entry to the Publish method, - // potentially resulting in a deadlock due to the SyncObject lock. - // Sequence of the deadlock: - // 1. An application creates an early instrument (e.g., Counter) before the MeterListener static constructor is executed. - // 2. Instrument.Publish is called and enters the SyncObject lock. - // 3. Within the lock block, MeterListener is called, triggering its static constructor. - // 4. The static constructor creates runtime metrics instruments, causing re-entry to Instrument.Publish and leading to a deadlock. - RuntimeHelpers.RunClassConstructor(typeof(MeterListener).TypeHandle); - List? allListeners = null; lock (Instrument.SyncObject) { diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/MeterListener.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/MeterListener.cs index 8466c1ac0fff97..5bbb4c7dd5ab58 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/MeterListener.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/MeterListener.cs @@ -33,19 +33,17 @@ public sealed class MeterListener : IDisposable private MeasurementCallback _doubleMeasurementCallback = (instrument, measurement, tags, state) => { /* no-op */ }; private MeasurementCallback _decimalMeasurementCallback = (instrument, measurement, tags, state) => { /* no-op */ }; - static MeterListener() + /// + /// Creates a MeterListener object. + /// + public MeterListener() { #if NET9_0_OR_GREATER // This ensures that the static Meter gets created before any listeners exist. - _ = RuntimeMetrics.IsEnabled(); + RuntimeMetrics.EnsureInitialized(); #endif } - /// - /// Creates a MeterListener object. - /// - public MeterListener() { } - /// /// Callbacks to get notification when an instrument is published. /// diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/RuntimeMetrics.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/RuntimeMetrics.cs index 4af7755b21266a..b7449a552a6bcd 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/RuntimeMetrics.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/RuntimeMetrics.cs @@ -20,6 +20,11 @@ internal static class RuntimeMetrics private static readonly int s_maxGenerations = Math.Min(GC.GetGCMemoryInfo().GenerationInfo.Length, s_genNames.Length); + public static void EnsureInitialized() + { + // Dummy method to ensure that the static constructor have run and created the meters + } + static RuntimeMetrics() { AppDomain.CurrentDomain.FirstChanceException += (source, e) => @@ -33,6 +38,8 @@ static RuntimeMetrics() }; } +#pragma warning disable CA1823 // suppress unused fields warning, as the fields are used to keep the meters alive + private static readonly ObservableCounter s_gcCollections = s_meter.CreateObservableCounter( "dotnet.gc.collections", GetGarbageCollectionCounts, @@ -156,28 +163,7 @@ static RuntimeMetrics() unit: "s", description: "CPU time used by the process."); - public static bool IsEnabled() - { - return s_gcCollections.Enabled - || s_processWorkingSet.Enabled - || s_gcHeapTotalAllocated.Enabled - || s_gcLastCollectionMemoryCommitted.Enabled - || s_gcLastCollectionHeapSize.Enabled - || s_gcLastCollectionFragmentationSize.Enabled - || s_gcPauseTime.Enabled - || s_jitCompiledSize.Enabled - || s_jitCompiledMethodCount.Enabled - || s_jitCompilationTime.Enabled - || s_monitorLockContention.Enabled - || s_timerCount.Enabled - || s_threadPoolThreadCount.Enabled - || s_threadPoolCompletedWorkItems.Enabled - || s_threadPoolQueueLength.Enabled - || s_assembliesCount.Enabled - || s_exceptions.Enabled - || s_processCpuCount.Enabled - || s_processCpuTime?.Enabled is true; - } +#pragma warning restore CA1823 private static IEnumerable> GetGarbageCollectionCounts() {