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