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