| File: System\Diagnostics\Switch.cs | Web Access |
| Project: src\runtime\src\libraries\System.Diagnostics.TraceSource\src\System.Diagnostics.TraceSource.csproj (System.Diagnostics.TraceSource) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Runtime.InteropServices; using System.Threading; namespace System.Diagnostics { /// <devdoc> /// <para>Provides an <see langword='abstract '/>base class to /// create new debugging and tracing switches.</para> /// </devdoc> public abstract class Switch { private readonly string? _description; private readonly string _displayName; private int _switchSetting; private volatile bool _initialized; private bool _initializing; private volatile string? _switchValueString = string.Empty; private readonly string _defaultValue; private static readonly List<WeakReference<Switch>> s_switches = new List<WeakReference<Switch>>(); private static int s_LastCollectionCount; private StringDictionary? _attributes; private object InitializedLock => field ?? Interlocked.CompareExchange(ref field, new object(), null) ?? field; /// <devdoc> /// <para>Initializes a new instance of the <see cref='System.Diagnostics.Switch'/> /// class.</para> /// </devdoc> protected Switch(string displayName, string? description) : this(displayName, description, "0") { } protected Switch(string displayName, string? description, string defaultSwitchValue) { // displayName is used as a hashtable key, so it can never be null. _displayName = displayName ?? string.Empty; _description = description; // Add a weakreference to this switch and cleanup invalid references lock (s_switches) { _pruneCachedSwitches(); s_switches.Add(new WeakReference<Switch>(this)); } _defaultValue = defaultSwitchValue; } private static void _pruneCachedSwitches() { lock (s_switches) { if (s_LastCollectionCount != GC.CollectionCount(2)) { List<WeakReference<Switch>> buffer = new List<WeakReference<Switch>>(s_switches.Count); for (int i = 0; i < s_switches.Count; i++) { if (s_switches[i].TryGetTarget(out _)) { buffer.Add(s_switches[i]); } } if (buffer.Count < s_switches.Count) { s_switches.Clear(); s_switches.AddRange(buffer); s_switches.TrimExcess(); } s_LastCollectionCount = GC.CollectionCount(2); } } } /// <devdoc> /// <para>Gets a name used to identify the switch.</para> /// </devdoc> public string DisplayName { get { return _displayName; } } /// <devdoc> /// <para>Gets a description of the switch.</para> /// </devdoc> public string Description { get { return _description ?? string.Empty; } } public StringDictionary Attributes { get { Initialize(); return _attributes ??= new StringDictionary(); } } /// <devdoc> /// <para> /// Indicates the current setting for this switch. /// </para> /// </devdoc> protected int SwitchSetting { get { if (!_initialized) { if (InitializeWithStatus()) { OnSwitchSettingChanged(); } } return _switchSetting; } set { bool didUpdate = false; lock (InitializedLock) { _initialized = true; if (_switchSetting != value) { _switchSetting = value; didUpdate = true; } } if (didUpdate) { OnSwitchSettingChanged(); } } } protected internal virtual string[]? GetSupportedAttributes() => null; internal void SetSwitchValues(int switchSetting, string switchValueString) { Initialize(); Debug.Assert(switchValueString is not null, "Unexpected 'switchValueString' null value"); lock (InitializedLock) { _switchSetting = switchSetting; _switchValueString = switchValueString; } } /// <summary> /// The default value assigned in the constructor. /// </summary> public string DefaultValue => _defaultValue; public string Value { get { Initialize(); return _switchValueString!; } set { Initialize(); _switchValueString = value; OnValueChanged(); } } /// <summary> /// Occurs when a <see cref="Switch"/> needs to be initialized. /// </summary> public static event EventHandler<InitializingSwitchEventArgs>? Initializing; internal void OnInitializing() { Initializing?.Invoke(null, new InitializingSwitchEventArgs(this)); TraceUtils.VerifyAttributes(Attributes, GetSupportedAttributes(), this); } private void Initialize() { InitializeWithStatus(); } private bool InitializeWithStatus() { if (!_initialized) { lock (InitializedLock) { if (_initialized || _initializing) { return false; } // This method is re-entrant during initialization, since calls to OnValueChanged() in subclasses could end up having InitializeWithStatus() // called again, we don't want to get caught in an infinite loop. _initializing = true; _switchValueString = null; try { OnInitializing(); } catch (Exception) { _initialized = false; _initializing = false; throw; } if (_switchValueString == null) { _switchValueString = _defaultValue; OnValueChanged(); } _initialized = true; _initializing = false; } } return true; } /// <devdoc> /// This method is invoked when a switch setting has been changed. It will /// be invoked the first time a switch reads its value from the registry /// or environment, and then it will be invoked each time the switch's /// value is changed. /// </devdoc> protected virtual void OnSwitchSettingChanged() { } protected virtual void OnValueChanged() { SwitchSetting = int.Parse(Value, CultureInfo.InvariantCulture); } internal static void RefreshAll() { lock (s_switches) { _pruneCachedSwitches(); for (int i = 0; i < s_switches.Count; i++) { if (s_switches[i].TryGetTarget(out Switch? swtch)) { swtch.Refresh(); } } } } /// <summary> /// Refreshes the trace configuration data. /// </summary> public void Refresh() { lock (InitializedLock) { _initialized = false; Initialize(); } } } }