Updates referencesource to .NET 4.7
[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
12     // 
13     // The AppContext class consists of a collection of APIs that applications can use to reason about the context
14     // in which they are running.
15     // 
16     // One of the APIs that are available to the application (and the framework) are the ones related to the 
17     // configuration switches (SetSwitch, TryGetSwitch). Those APIs are used to set and retrieve the value of a switch. 
18     // 
19     // Inside the framework we use those APIs to provide backward compatibility for our in-place updates. In order for
20     // that to work in as many cases as possible we have intentionally coded the APIs to use very low level concepts.
21     // For instance, we are directly P/Invoking into the Registry calls and so that we don't initialize the infrastructure
22     // for accessing registry. Doing that would allow us to use AppContext switches inside the Registry code.
23     // 
24     public static class AppContext
25     {
26         [Flags]
27         private enum SwitchValueState
28         {
29             HasFalseValue = 0x1,
30             HasTrueValue = 0x2,
31             HasLookedForOverride = 0x4,
32             UnknownValue = 0x8 // Has no default and could not find an override
33         }
34         
35         // The Dictionary is intentionally left without specifying the comparer. StringComparer.Ordinal for instance will
36         // end up pulling in Globalization since all StringComparers are initialized in the static constructor (including the
37         // ones that use CultureInfo).
38         // Not specifying a comparer means that the dictionary will end up with a GenericEqualityComparer<string> comparer 
39         private static readonly Dictionary<string, SwitchValueState> s_switchMap = new Dictionary<string, SwitchValueState>();
40         private static volatile bool s_defaultsInitialized = false;
41
42         public static string BaseDirectory
43         {
44 #if FEATURE_CORECLR
45             [System.Security.SecuritySafeCritical]
46 #endif
47             get
48             {
49                 // The value of APP_CONTEXT_BASE_DIRECTORY key has to be a string and it is not allowed to be any other type. 
50                 // Otherwise the caller will get invalid cast exception
51                 return (string) AppDomain.CurrentDomain.GetData("APP_CONTEXT_BASE_DIRECTORY") ?? AppDomain.CurrentDomain.BaseDirectory;
52             }
53         }
54
55         public static string TargetFrameworkName
56         {
57             get
58             {
59                 // Forward the value that is set on the current domain.
60                 return AppDomain.CurrentDomain.SetupInformation.TargetFrameworkName;
61             }
62         }
63
64 #if FEATURE_CORECLR
65         [System.Security.SecuritySafeCritical]
66 #endif
67         public static object GetData(string name)
68         {
69             return AppDomain.CurrentDomain.GetData(name);
70         }
71
72         #region Switch APIs
73
74         private static void InitializeDefaultSwitchValues()
75         {
76             // To save a method call into this method, we are first checking
77             // the value of s_defaultsInitialized in the caller.
78             lock (s_switchMap)
79             {
80                 if (s_defaultsInitialized == false)
81                 {
82 #if !MONO
83                     // populate the AppContext with the default set of values
84                     AppContextDefaultValues.PopulateDefaultValues();
85 #endif
86                     s_defaultsInitialized = true;
87                 }
88             }
89         }        
90
91         /// <summary>
92         /// Try to get the value of the switch.
93         /// </summary>
94         /// <param name="switchName">The name of the switch</param>
95         /// <param name="isEnabled">A variable where to place the value of the switch</param>
96         /// <returns>A return value of true represents that the switch was set and <paramref name="isEnabled"/> contains the value of the switch</returns>
97         public static bool TryGetSwitch(string switchName, out bool isEnabled)
98         {
99             if (switchName == null)
100                 throw new ArgumentNullException("switchName");
101             if (switchName.Length == 0)
102                 throw new ArgumentException(Environment.GetResourceString("Argument_EmptyName"), "switchName");
103
104             if (s_defaultsInitialized == false)
105             {
106                 InitializeDefaultSwitchValues();
107             }
108 #if DEBUG
109             BCLDebug.Assert(s_defaultsInitialized == true, "AppContext defaults should have been initialized.");
110 #endif
111
112             // By default, the switch is not enabled.
113             isEnabled = false;
114
115             SwitchValueState switchValue;
116             lock (s_switchMap)
117             {
118                 if (s_switchMap.TryGetValue(switchName, out switchValue))
119                 {
120                     // The value is in the dictionary. 
121                     // There are 3 cases here:
122                     // 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).
123                     //    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
124                     //             Will be unknown to mscorlib.dll and we want to prevent checking the overrides every time we check this switch
125                     // 2. The switch has a valid value AND we have read the overrides for it
126                     //    Example: TryGetSwitch is called for a switch set via SetSwitch
127                     // 3. The switch has the default value and we need to check for overrides
128                     //    Example: TryGetSwitch is called for the first time for a switch that has a default value 
129
130                     // 1. The value is unknown
131                     if (switchValue == SwitchValueState.UnknownValue)
132                     {
133                         isEnabled = false;
134                         return false;
135                     }
136
137                     // We get the value of isEnabled from the value that we stored in the dictionary
138                     isEnabled = (switchValue & SwitchValueState.HasTrueValue) == SwitchValueState.HasTrueValue; 
139
140                     // 2. The switch has a valid value AND we have checked for overrides
141                     if ((switchValue & SwitchValueState.HasLookedForOverride) == SwitchValueState.HasLookedForOverride)
142                     {
143                         return true;
144                     }
145 #if !MONO
146                     // 3. The switch has a valid value, but we need to check for overrides.
147                     // Regardless of whether or not the switch has an override, we need to update the value to reflect
148                     // the fact that we checked for overrides. 
149                     bool overrideValue;
150                     if (AppContextDefaultValues.TryGetSwitchOverride(switchName, out overrideValue))
151                     {
152                         // we found an override!
153                         isEnabled = overrideValue;
154                     }
155 #endif
156                     // Update the switch in the dictionary to mark it as 'checked for override'
157                     s_switchMap[switchName] = (isEnabled ? SwitchValueState.HasTrueValue : SwitchValueState.HasFalseValue)
158                                                 | SwitchValueState.HasLookedForOverride;
159
160                     return true;
161                 }
162                 else
163                 {
164                     // The value is NOT in the dictionary
165                     // In this case we need to see if we have an override defined for the value.
166                     // There are 2 cases:
167                     // 1. The value has an override specified. In this case we need to add the value to the dictionary 
168                     //    and mark it as checked for overrides
169                     //    Example: In a servicing event, System.Xml introduces a switch and an override is specified.
170                     //             The value is not found in mscorlib (as System.Xml ships independent of mscorlib)
171                     // 2. The value does not have an override specified
172                     //    In this case, we want to capture the fact that we looked for a value and found nothing by adding 
173                     //    an entry in the dictionary with the 'sentinel' value of 'SwitchValueState.UnknownValue'.
174                     //    Example: This will prevent us from trying to find overrides for values that we don't have in the dictionary
175 #if !MONO
176                     // 1. The value has an override specified.
177                     bool overrideValue;
178                     if (AppContextDefaultValues.TryGetSwitchOverride(switchName, out overrideValue))
179                     {
180                         isEnabled = overrideValue;
181
182                         // Update the switch in the dictionary to mark it as 'checked for override'
183                         s_switchMap[switchName] = (isEnabled ? SwitchValueState.HasTrueValue : SwitchValueState.HasFalseValue)
184                                                     | SwitchValueState.HasLookedForOverride;
185
186                         return true;
187                     }
188 #endif
189                     // 2. The value does not have an override.
190                     s_switchMap[switchName] = SwitchValueState.UnknownValue;
191                 }
192             }
193             return false; // we did not find a value for the switch
194         }
195
196         /// <summary>
197         /// Assign a switch a value
198         /// </summary>
199         /// <param name="switchName">The name of the switch</param>
200         /// <param name="isEnabled">The value to assign</param>
201         public static void SetSwitch(string switchName, bool isEnabled)
202         {
203             if (switchName == null)
204                 throw new ArgumentNullException("switchName");
205             if (switchName.Length == 0)
206                 throw new ArgumentException(Environment.GetResourceString("Argument_EmptyName"), "switchName");
207
208             if (s_defaultsInitialized == false)
209             {
210                 InitializeDefaultSwitchValues();
211             }
212 #if DEBUG
213             BCLDebug.Assert(s_defaultsInitialized == true, "AppContext defaults should have been initialized.");
214 #endif
215
216             SwitchValueState switchValue = (isEnabled ? SwitchValueState.HasTrueValue : SwitchValueState.HasFalseValue)
217                                              | SwitchValueState.HasLookedForOverride;
218             lock (s_switchMap)
219             {
220                 // Store the new value and the fact that we checked in the dictionary
221                 s_switchMap[switchName] = switchValue;
222             }
223         }
224
225         /// <summary>
226         /// This method is going to be called from the AppContextDefaultValues class when setting up the 
227         /// default values for the switches.
228         /// </summary>
229         internal static void DefineSwitchDefault(string switchName, bool isEnabled)
230         {
231 #if DEBUG
232             BCLDebug.Assert(System.Threading.Monitor.IsEntered(s_switchMap), "Expected the method to be called within a lock");
233 #endif
234
235             s_switchMap[switchName] = isEnabled ? SwitchValueState.HasTrueValue : SwitchValueState.HasFalseValue;
236         }
237
238         /// <summary>
239         /// This method is going to be called from the AppContextDefaultValues class when setting up the 
240         /// override default values for the switches.
241         /// </summary>
242         internal static void DefineSwitchOverride(string switchName, bool isEnabled)
243         {
244 #if DEBUG
245             BCLDebug.Assert(System.Threading.Monitor.IsEntered(s_switchMap), "Expected the method to be called within a lock");
246 #endif
247
248             s_switchMap[switchName] = (isEnabled ? SwitchValueState.HasTrueValue : SwitchValueState.HasFalseValue) 
249                                         | SwitchValueState.HasLookedForOverride;
250         }
251         #endregion
252     }
253 }