// ==++== // // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== using System.Collections.Generic; namespace System { public static class AppContext { [Flags] private enum SwitchValueState { HasFalseValue = 0x1, HasTrueValue = 0x2, HasLookedForOverride = 0x4, UnknownValue = 0x8 // Has no default and could not find an override } private static Dictionary s_switchMap = new Dictionary(); private static readonly object s_syncLock = new object(); public static string BaseDirectory { #if FEATURE_CORECLR [System.Security.SecuritySafeCritical] #endif get { // The value of APP_CONTEXT_BASE_DIRECTORY key has to be a string and it is not allowed to be any other type. // Otherwise the caller will get invalid cast exception return (string) AppDomain.CurrentDomain.GetData("APP_CONTEXT_BASE_DIRECTORY") ?? AppDomain.CurrentDomain.BaseDirectory; } } #region Switch APIs static AppContext() { // populate the AppContext with the default set of values AppContextDefaultValues.PopulateDefaultValues(); } /// /// Try to get the value of the switch. /// /// The name of the switch /// A variable where to place the value of the switch /// A return value of true represents that the switch was set and contains the value of the switch public static bool TryGetSwitch(string switchName, out bool isEnabled) { if (switchName == null) throw new ArgumentNullException("switchName"); if (switchName.Length == 0) throw new ArgumentException(Environment.GetResourceString("Argument_EmptyName"), "switchName"); // By default, the switch is not enabled. isEnabled = false; SwitchValueState switchValue; lock (s_switchMap) { if (s_switchMap.TryGetValue(switchName, out switchValue)) { // The value is in the dictionary. // There are 3 cases here: // 1. The value of the switch is 'unknown'. This means that the switch name is not known to the system (either via defaults or checking overrides). // Example: This is the case when, during a servicing event, a switch is added to System.Xml which ships before mscorlib. The value of the switch // Will be unknown to mscorlib.dll and we want to prevent checking the overrides every time we check this switch // 2. The switch has a valid value AND we have read the overrides for it // Example: TryGetSwitch is called for a switch set via SetSwitch // 3. The switch has the default value and we need to check for overrides // Example: TryGetSwitch is called for the first time for a switch that has a default value // 1. The value is unknown if (switchValue == SwitchValueState.UnknownValue) { isEnabled = false; return false; } // We get the value of isEnabled from the value that we stored in the dictionary isEnabled = (switchValue & SwitchValueState.HasTrueValue) == SwitchValueState.HasTrueValue; // 2. The switch has a valid value AND we have checked for overrides if ((switchValue & SwitchValueState.HasLookedForOverride) == SwitchValueState.HasLookedForOverride) { return true; } // 3. The switch has a valid value, but we need to check for overrides. // Regardless of whether or not the switch has an override, we need to update the value to reflect // the fact that we checked for overrides. bool overrideValue; if (AppContextDefaultValues.TryGetSwitchOverride(switchName, out overrideValue)) { // we found an override! isEnabled = overrideValue; } // Update the switch in the dictionary to mark it as 'checked for override' s_switchMap[switchName] = (isEnabled ? SwitchValueState.HasTrueValue : SwitchValueState.HasFalseValue) | SwitchValueState.HasLookedForOverride; return true; } else { // The value is NOT in the dictionary // In this case we need to see if we have an override defined for the value. // There are 2 cases: // 1. The value has an override specified. In this case we need to add the value to the dictionary // and mark it as checked for overrides // Example: In a servicing event, System.Xml introduces a switch and an override is specified. // The value is not found in mscorlib (as System.Xml ships independent of mscorlib) // 2. The value does not have an override specified // In this case, we want to capture the fact that we looked for a value and found nothing by adding // an entry in the dictionary with the 'sentinel' value of 'SwitchValueState.UnknownValue'. // Example: This will prevent us from trying to find overrides for values that we don't have in the dictionary // 1. The value has an override specified. bool overrideValue; if (AppContextDefaultValues.TryGetSwitchOverride(switchName, out overrideValue)) { isEnabled = overrideValue; // Update the switch in the dictionary to mark it as 'checked for override' s_switchMap[switchName] = (isEnabled ? SwitchValueState.HasTrueValue : SwitchValueState.HasFalseValue) | SwitchValueState.HasLookedForOverride; return true; } // 2. The value does not have an override. s_switchMap[switchName] = SwitchValueState.UnknownValue; } } return false; // we did not find a value for the switch } /// /// Assign a switch a value /// /// The name of the switch /// The value to assign public static void SetSwitch(string switchName, bool isEnabled) { if (switchName == null) throw new ArgumentNullException("switchName"); if (switchName.Length == 0) throw new ArgumentException(Environment.GetResourceString("Argument_EmptyName"), "switchName"); lock (s_syncLock) { // Store the new value and the fact that we checked in the dictionary s_switchMap[switchName] = (isEnabled ? SwitchValueState.HasTrueValue : SwitchValueState.HasFalseValue) | SwitchValueState.HasLookedForOverride; } } /// /// This method is going to be called from the AppContextDefaultValues class when setting up the /// default values for the switches. !!!! This method is called during the static constructor so it does not /// take a lock !!!! If you are planning to use this outside of that, please ensure proper locking. /// internal static void DefineSwitchDefault(string switchName, bool isEnabled) { s_switchMap[switchName] = isEnabled ? SwitchValueState.HasTrueValue : SwitchValueState.HasFalseValue; } #endregion } }