Skip to content

Commit d8c545c

Browse files
committed
Simplify initialization of RuntimeMetrics
- Trigger the RuntimeMetrics initialization only when actually needed in the MeterListener constructor. - Delete the lock-ordering workaround and wrong comment introduced in #105259. Trigger the RuntimeMetrics initialization only when actually needed should make the lock-ordering workarond unnecessary.
1 parent e7f3ff1 commit d8c545c

File tree

3 files changed

+13
-40
lines changed

3 files changed

+13
-40
lines changed

src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/Instrument.cs

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -89,17 +89,6 @@ protected void Publish()
8989
return;
9090
}
9191

92-
// MeterListener has a static constructor that creates runtime metrics instruments.
93-
// We need to ensure this static constructor is called before starting to publish the instrument.
94-
// This is necessary because creating runtime metrics instruments will cause re-entry to the Publish method,
95-
// potentially resulting in a deadlock due to the SyncObject lock.
96-
// Sequence of the deadlock:
97-
// 1. An application creates an early instrument (e.g., Counter) before the MeterListener static constructor is executed.
98-
// 2. Instrument.Publish is called and enters the SyncObject lock.
99-
// 3. Within the lock block, MeterListener is called, triggering its static constructor.
100-
// 4. The static constructor creates runtime metrics instruments, causing re-entry to Instrument.Publish and leading to a deadlock.
101-
RuntimeHelpers.RunClassConstructor(typeof(MeterListener).TypeHandle);
102-
10392
List<MeterListener>? allListeners = null;
10493
lock (Instrument.SyncObject)
10594
{

src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/MeterListener.cs

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,19 +33,17 @@ public sealed class MeterListener : IDisposable
3333
private MeasurementCallback<double> _doubleMeasurementCallback = (instrument, measurement, tags, state) => { /* no-op */ };
3434
private MeasurementCallback<decimal> _decimalMeasurementCallback = (instrument, measurement, tags, state) => { /* no-op */ };
3535

36-
static MeterListener()
36+
/// <summary>
37+
/// Creates a MeterListener object.
38+
/// </summary>
39+
public MeterListener()
3740
{
3841
#if NET9_0_OR_GREATER
3942
// This ensures that the static Meter gets created before any listeners exist.
40-
_ = RuntimeMetrics.IsEnabled();
43+
RuntimeMetrics.EnsureInitialized();
4144
#endif
4245
}
4346

44-
/// <summary>
45-
/// Creates a MeterListener object.
46-
/// </summary>
47-
public MeterListener() { }
48-
4947
/// <summary>
5048
/// Callbacks to get notification when an instrument is published.
5149
/// </summary>

src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/RuntimeMetrics.cs

Lines changed: 8 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@ internal static class RuntimeMetrics
2020

2121
private static readonly int s_maxGenerations = Math.Min(GC.GetGCMemoryInfo().GenerationInfo.Length, s_genNames.Length);
2222

23+
public static void EnsureInitialized()
24+
{
25+
// Dummy method to ensure that the static constructor have run and created the meters
26+
}
27+
2328
static RuntimeMetrics()
2429
{
2530
AppDomain.CurrentDomain.FirstChanceException += (source, e) =>
@@ -33,6 +38,8 @@ static RuntimeMetrics()
3338
};
3439
}
3540

41+
#pragma warning disable CA1823 // suppress unused fields warning, as the fields are used to keep the meters alive
42+
3643
private static readonly ObservableCounter<long> s_gcCollections = s_meter.CreateObservableCounter(
3744
"dotnet.gc.collections",
3845
GetGarbageCollectionCounts,
@@ -156,28 +163,7 @@ static RuntimeMetrics()
156163
unit: "s",
157164
description: "CPU time used by the process.");
158165

159-
public static bool IsEnabled()
160-
{
161-
return s_gcCollections.Enabled
162-
|| s_processWorkingSet.Enabled
163-
|| s_gcHeapTotalAllocated.Enabled
164-
|| s_gcLastCollectionMemoryCommitted.Enabled
165-
|| s_gcLastCollectionHeapSize.Enabled
166-
|| s_gcLastCollectionFragmentationSize.Enabled
167-
|| s_gcPauseTime.Enabled
168-
|| s_jitCompiledSize.Enabled
169-
|| s_jitCompiledMethodCount.Enabled
170-
|| s_jitCompilationTime.Enabled
171-
|| s_monitorLockContention.Enabled
172-
|| s_timerCount.Enabled
173-
|| s_threadPoolThreadCount.Enabled
174-
|| s_threadPoolCompletedWorkItems.Enabled
175-
|| s_threadPoolQueueLength.Enabled
176-
|| s_assembliesCount.Enabled
177-
|| s_exceptions.Enabled
178-
|| s_processCpuCount.Enabled
179-
|| s_processCpuTime?.Enabled is true;
180-
}
166+
#pragma warning restore CA1823
181167

182168
private static IEnumerable<Measurement<long>> GetGarbageCollectionCounts()
183169
{

0 commit comments

Comments
 (0)