//------------------------------------------------------------------------------ // // Copyright (c) Microsoft Corporation. All rights reserved. // //------------------------------------------------------------------------------ /* */ #if !CONFIGURATION_DEP using SwitchElementsCollection = System.Object; #endif namespace System.Diagnostics { using System; using System.Security; using System.Security.Permissions; using System.Threading; using System.Runtime.InteropServices; using Microsoft.Win32; using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; using System.Globalization; using System.Configuration; #if XML_DEP using System.Xml.Serialization; #endif using System.Diagnostics.CodeAnalysis; /// /// Provides an base class to /// create new debugging and tracing switches. /// public abstract class Switch { private SwitchElementsCollection switchSettings; private readonly string description; private readonly string displayName; private int switchSetting = 0; private volatile bool initialized = false; private bool initializing = false; private volatile string switchValueString = String.Empty; private StringDictionary attributes; private string defaultValue; private object m_intializedLock; private static List switches = new List(); private static int s_LastCollectionCount; private object IntializedLock { [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "Reviewed for thread-safety")] get { if (m_intializedLock == null) { Object o = new Object(); Interlocked.CompareExchange(ref m_intializedLock, o, null); } return m_intializedLock; } } /// /// Initializes a new instance of the /// class. /// 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. if (displayName == null) displayName = string.Empty; this.displayName = displayName; this.description = description; // Add a weakreference to this switch and cleanup invalid references lock (switches) { _pruneCachedSwitches(); switches.Add(new WeakReference(this)); } defaultValue = defaultSwitchValue; } private static void _pruneCachedSwitches() { lock (switches) { if (s_LastCollectionCount != GC.CollectionCount(2)) { List buffer = new List(switches.Count); for (int i = 0; i < switches.Count; i++) { Switch s = ((Switch)switches[i].Target); if (s != null) { buffer.Add(switches[i]); } } if (buffer.Count < switches.Count) { switches.Clear(); switches.AddRange(buffer); switches.TrimExcess(); } s_LastCollectionCount = GC.CollectionCount(2); } } } #if XML_DEP [XmlIgnore] #endif public StringDictionary Attributes { get { Initialize(); if (attributes == null) attributes = new StringDictionary(); return attributes; } } /// /// Gets a name used to identify the switch. /// public string DisplayName { get { return displayName; } } /// /// Gets a description of the switch. /// public string Description { get { return (description == null) ? string.Empty : description; } } /// /// /// Indicates the current setting for this switch. /// /// protected int SwitchSetting { [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "reviewed for thread-safety")] get { if (!initialized) { if (InitializeWithStatus()) OnSwitchSettingChanged(); } return switchSetting; } [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "reviewed for thread-safety")] set { bool didUpdate = false; lock (IntializedLock) { initialized = true; if (switchSetting != value) { switchSetting = value; didUpdate = true; } } if (didUpdate) { OnSwitchSettingChanged(); } } } protected string Value { get { Initialize(); return switchValueString; } set { Initialize(); switchValueString = value; #if CONFIGURATION_DEP try { OnValueChanged(); } catch (ArgumentException e) { throw new ConfigurationErrorsException(SR.GetString(SR.BadConfigSwitchValue, DisplayName), e); } catch (FormatException e) { throw new ConfigurationErrorsException(SR.GetString(SR.BadConfigSwitchValue, DisplayName), e); } catch (OverflowException e) { throw new ConfigurationErrorsException(SR.GetString(SR.BadConfigSwitchValue, DisplayName), e); } #else OnValueChanged(); #endif } } private void Initialize() { InitializeWithStatus(); } private bool InitializeWithStatus() { if (!initialized) { lock (IntializedLock) { if (initialized || initializing) { return false; } // This method is re-entrent during intitialization, 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; if (switchSettings == null) { if (!InitializeConfigSettings()) { initialized = true; initializing = false; return false; } } #if CONFIGURATION_DEP if (switchSettings != null) { SwitchElement mySettings = switchSettings[displayName]; if (mySettings != null) { string value = mySettings.Value; if (value != null) { this.Value = value; } else this.Value = defaultValue; try { TraceUtils.VerifyAttributes(mySettings.Attributes, GetSupportedAttributes(), this); } catch (ConfigurationException) { // if VerifyAttributes throws, clean up a little bit so we're not in a bad state. initialized = false; initializing = false; throw; } attributes = new StringDictionary(); attributes.ReplaceHashtable(mySettings.Attributes); } else { // We don't use the property here because we don't want to catch exceptions // and rethrow them as ConfigurationException. In this case there's no config. switchValueString = defaultValue; OnValueChanged(); } } else { #endif // We don't use the property here because we don't want to catch exceptions // and rethrow them as ConfigurationException. In this case there's no config. switchValueString = defaultValue; OnValueChanged(); #if CONFIGURATION_DEP } #endif initialized = true; initializing = false; } } return true; } private bool InitializeConfigSettings() { if (switchSettings != null) return true; #if CONFIGURATION_DEP if (!DiagnosticsConfiguration.CanInitialize()) return false; // This hashtable is case-insensitive. switchSettings = DiagnosticsConfiguration.SwitchSettings; #endif return true; } virtual protected internal string[] GetSupportedAttributes() { return null; } /// /// 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. /// protected virtual void OnSwitchSettingChanged() { } protected virtual void OnValueChanged() { SwitchSetting = Int32.Parse(Value, CultureInfo.InvariantCulture); } internal static void RefreshAll() { lock (switches) { _pruneCachedSwitches(); for (int i=0; i