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 0a3362e015d1a7..8b87501f26a695 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 @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.ComponentModel; +using System.Runtime.CompilerServices; namespace System.Diagnostics.Metrics { @@ -88,6 +89,17 @@ 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 deef123f52a41e..8466c1ac0fff97 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 @@ -293,7 +293,7 @@ public void Dispose() } } - // Publish is called from Instrument.Publish + // GetAllListeners is called from Instrument.Publish inside Instrument.SyncObject lock. internal static List? GetAllListeners() => s_allStartedListeners.Count == 0 ? null : new List(s_allStartedListeners); [MethodImpl(MethodImplOptions.AggressiveInlining)]