Update Reference Sources to .NET Framework 4.6.1
[mono.git] / mcs / class / referencesource / mscorlib / system / AppContext / AppContext.cs
1 // ==++==
2 // 
3 //   Copyright (c) Microsoft Corporation.  All rights reserved.
4 // 
5 // ==--==
6
7 using System.Collections.Generic;
8
9 namespace System
10 {
11     public static class AppContext
12     {
13         [Flags]
14         private enum SwitchValueState
15         {
16             HasFalseValue = 0x1,
17             HasTrueValue = 0x2,
18             HasLookedForOverride = 0x4,
19             UnknownValue = 0x8 // Has no default and could not find an override
20         }
21         private static Dictionary<string, SwitchValueState> s_switchMap = new Dictionary<string, SwitchValueState>();
22         private static readonly object s_syncLock = new object();
23
24         public static string BaseDirectory
25         {
26 #if FEATURE_CORECLR
27             [System.Security.SecuritySafeCritical]
28 #endif
29             get
30             {
31                 // The value of APP_CONTEXT_BASE_DIRECTORY key has to be a string and it is not allowed to be any other type. 
32                 // Otherwise the caller will get invalid cast exception
33                 return (string) AppDomain.CurrentDomain.GetData("APP_CONTEXT_BASE_DIRECTORY") ?? AppDomain.CurrentDomain.BaseDirectory;
34             }
35         }
36
37         #region Switch APIs
38
39         static AppContext()
40         {
41             // populate the AppContext with the default set of values
42             AppContextDefaultValues.PopulateDefaultValues();
43         }
44
45         /// <summary>
46         /// Try to get the value of the switch.
47         /// </summary>
48         /// <param name="switchName">The name of the switch</param>
49         /// <param name="isEnabled">A variable where to place the value of the switch</param>
50         /// <returns>A return value of true represents that the switch was set and <paramref name="isEnabled"/> contains the value of the switch</returns>
51         public static bool TryGetSwitch(string switchName, out bool isEnabled)
52         {
53             if (switchName == null)
54                 throw new ArgumentNullException("switchName");
55             if (switchName.Length == 0)
56                 throw new ArgumentException(Environment.GetResourceString("Argument_EmptyName"), "switchName");
57
58             // By default, the switch is not enabled.
59             isEnabled = false;
60
61             SwitchValueState switchValue;
62             lock (s_switchMap)
63             {
64                 if (s_switchMap.TryGetValue(switchName, out switchValue))
65                 {
66                     // The value is in the dictionary. 
67                     // There are 3 cases here:
68                     // 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).
69                     //    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
70                     //             Will be unknown to mscorlib.dll and we want to prevent checking the overrides every time we check this switch
71                     // 2. The switch has a valid value AND we have read the overrides for it
72                     //    Example: TryGetSwitch is called for a switch set via SetSwitch
73                     // 3. The switch has the default value and we need to check for overrides
74                     //    Example: TryGetSwitch is called for the first time for a switch that has a default value 
75
76                     // 1. The value is unknown
77                     if (switchValue == SwitchValueState.UnknownValue)
78                     {
79                         isEnabled = false;
80                         return false;
81                     }
82
83                     // We get the value of isEnabled from the value that we stored in the dictionary
84                     isEnabled = (switchValue & SwitchValueState.HasTrueValue) == SwitchValueState.HasTrueValue; 
85
86                     // 2. The switch has a valid value AND we have checked for overrides
87                     if ((switchValue & SwitchValueState.HasLookedForOverride) == SwitchValueState.HasLookedForOverride)
88                     {
89                         return true;
90                     }
91
92                     // 3. The switch has a valid value, but we need to check for overrides.
93                     // Regardless of whether or not the switch has an override, we need to update the value to reflect
94                     // the fact that we checked for overrides. 
95                     bool overrideValue;
96                     if (AppContextDefaultValues.TryGetSwitchOverride(switchName, out overrideValue))
97                     {
98                         // we found an override!
99                         isEnabled = overrideValue;
100                     }
101
102                     // Update the switch in the dictionary to mark it as 'checked for override'
103                     s_switchMap[switchName] = (isEnabled ? SwitchValueState.HasTrueValue : SwitchValueState.HasFalseValue)
104                                                 | SwitchValueState.HasLookedForOverride;
105
106                     return true;
107                 }
108                 else
109                 {
110                     // The value is NOT in the dictionary
111                     // In this case we need to see if we have an override defined for the value.
112                     // There are 2 cases:
113                     // 1. The value has an override specified. In this case we need to add the value to the dictionary 
114                     //    and mark it as checked for overrides
115                     //    Example: In a servicing event, System.Xml introduces a switch and an override is specified.
116                     //             The value is not found in mscorlib (as System.Xml ships independent of mscorlib)
117                     // 2. The value does not have an override specified
118                     //    In this case, we want to capture the fact that we looked for a value and found nothing by adding 
119                     //    an entry in the dictionary with the 'sentinel' value of 'SwitchValueState.UnknownValue'.
120                     //    Example: This will prevent us from trying to find overrides for values that we don't have in the dictionary
121
122                     // 1. The value has an override specified.
123                     bool overrideValue;
124                     if (AppContextDefaultValues.TryGetSwitchOverride(switchName, out overrideValue))
125                     {
126                         isEnabled = overrideValue;
127
128                         // Update the switch in the dictionary to mark it as 'checked for override'
129                         s_switchMap[switchName] = (isEnabled ? SwitchValueState.HasTrueValue : SwitchValueState.HasFalseValue)
130                                                     | SwitchValueState.HasLookedForOverride;
131
132                         return true;
133                     }
134
135                     // 2. The value does not have an override.
136                     s_switchMap[switchName] = SwitchValueState.UnknownValue;
137                 }
138             }
139             return false; // we did not find a value for the switch
140         }
141
142         /// <summary>
143         /// Assign a switch a value
144         /// </summary>
145         /// <param name="switchName">The name of the switch</param>
146         /// <param name="isEnabled">The value to assign</param>
147         public static void SetSwitch(string switchName, bool isEnabled)
148         {
149             if (switchName == null)
150                 throw new ArgumentNullException("switchName");
151             if (switchName.Length == 0)
152                 throw new ArgumentException(Environment.GetResourceString("Argument_EmptyName"), "switchName");
153
154             lock (s_syncLock)
155             {
156                 // Store the new value and the fact that we checked in the dictionary
157                 s_switchMap[switchName] = (isEnabled ? SwitchValueState.HasTrueValue : SwitchValueState.HasFalseValue)
158                                             | SwitchValueState.HasLookedForOverride;
159             }
160         }
161
162         /// <summary>
163         /// This method is going to be called from the AppContextDefaultValues class when setting up the 
164         /// default values for the switches. !!!! This method is called during the static constructor so it does not
165         /// take a lock !!!! If you are planning to use this outside of that, please ensure proper locking.
166         /// </summary>
167         internal static void DefineSwitchDefault(string switchName, bool isEnabled)
168         {
169             s_switchMap[switchName] = isEnabled ? SwitchValueState.HasTrueValue : SwitchValueState.HasFalseValue;
170         }
171         #endregion
172     }
173 }