Merge pull request #3389 from lambdageek/bug-43099
[mono.git] / mcs / class / referencesource / System / InternalApis / Clr / inc / LocalAppContext.cs
1 // ==++==
2 // 
3 //   Copyright (c) Microsoft Corporation.  All rights reserved.
4 // 
5 // ==--==
6
7 // There are cases where we have multiple assemblies that are going to import this file and 
8 // if they are going to also have InternalsVisibleTo between them, there will be a compiler warning
9 // that the type is found both in the source and in a referenced assembly. The compiler will prefer 
10 // the version of the type defined in the source
11 //
12 // In order to disable the warning for this type we are disabling this warning for this entire file.
13 #pragma warning disable 436
14
15 // NOTE: This file should not be included in mscorlib. This should only be included in FX libraries that need to provide switches
16 using System;
17 using System.Collections.Generic;
18 using System.Reflection;
19 using System.Runtime.CompilerServices;
20 using System.Threading;
21
22 namespace System
23 {
24     internal static partial class LocalAppContext
25     {
26         private delegate bool TryGetSwitchDelegate(string switchName, out bool value);
27
28         private static TryGetSwitchDelegate TryGetSwitchFromCentralAppContext;
29         private static bool s_canForwardCalls;
30
31         private static Dictionary<string, bool> s_switchMap = new Dictionary<string, bool>();
32         private static readonly object s_syncLock = new object();
33
34         private static bool DisableCaching { get; set; }
35
36         static LocalAppContext()
37         {
38             // Try to setup the callback into the central AppContext
39             s_canForwardCalls = SetupDelegate();
40
41             // Populate the default values of the local app context 
42             AppContextDefaultValues.PopulateDefaultValues();
43
44             // Cache the value of the switch that help with testing
45             DisableCaching = IsSwitchEnabled(@"TestSwitch.LocalAppContext.DisableCaching");
46         }
47
48         public static bool IsSwitchEnabled(string switchName)
49         {
50             if (s_canForwardCalls)
51             {
52                 bool isEnabledCentrally;
53                 if (TryGetSwitchFromCentralAppContext(switchName, out isEnabledCentrally))
54                 {
55                     // we found the switch, so return whatever value it has
56                     return isEnabledCentrally;
57                 }
58                 // if we could not get the value from the central authority, try the local storage.
59             }
60
61             return IsSwitchEnabledLocal(switchName);
62         }
63
64         private static bool IsSwitchEnabledLocal(string switchName)
65         {
66             // read the value from the set of local defaults
67             bool isEnabled, isPresent;
68             lock (s_switchMap)
69             {
70                 isPresent = s_switchMap.TryGetValue(switchName, out isEnabled);
71             }
72
73             // If the value is in the set of local switches, reutrn the value
74             if (isPresent)
75             {
76                 return isEnabled;
77             }
78
79             // if we could not find the switch name, we should return 'false'
80             // This will preserve the concept of switches been 'off' unless explicitly set to 'on'
81             return false;
82         }
83
84         private static bool SetupDelegate()
85         {
86             Type appContextType = typeof(object).Assembly.GetType("System.AppContext");
87             if (appContextType == null)
88                 return false;
89
90             MethodInfo method = appContextType.GetMethod(
91                                             "TryGetSwitch",  // the method name
92                                             BindingFlags.Static | BindingFlags.Public,  // binding flags
93                                             null, // use the default binder
94                                             new Type[] { typeof(string), typeof(bool).MakeByRefType() },
95                                             null); // parameterModifiers - this is ignored by the default binder 
96             if (method == null)
97                 return false;
98
99             // Create delegate if we found the method.
100             TryGetSwitchFromCentralAppContext = (TryGetSwitchDelegate)Delegate.CreateDelegate(typeof(TryGetSwitchDelegate), method);
101
102             return true;
103         }
104
105         [MethodImpl(MethodImplOptions.AggressiveInlining)]
106         internal static bool GetCachedSwitchValue(string switchName, ref int switchValue)
107         {
108             if (switchValue < 0) return false;
109             if (switchValue > 0) return true;
110
111             return GetCachedSwitchValueInternal(switchName, ref switchValue);
112         }
113
114         private static bool GetCachedSwitchValueInternal(string switchName, ref int switchValue)
115         {
116             if (LocalAppContext.DisableCaching)
117             {
118                 return LocalAppContext.IsSwitchEnabled(switchName);
119             }
120
121             bool isEnabled = LocalAppContext.IsSwitchEnabled(switchName);
122             switchValue = isEnabled ? 1 /*true*/ : -1 /*false*/;
123             return isEnabled;
124         }
125
126         /// <summary>
127         /// This method is going to be called from the AppContextDefaultValues class when setting up the 
128         /// default values for the switches. !!!! This method is called during the static constructor so it does not
129         /// take a lock !!!! If you are planning to use this outside of that, please ensure proper locking.
130         /// </summary>
131         internal static void DefineSwitchDefault(string switchName, bool initialValue)
132         {
133             s_switchMap[switchName] = initialValue;
134         }
135     }
136 }
137
138 #pragma warning restore 436