3 // Copyright (c) Microsoft Corporation. All rights reserved.
7 // NOTE: This file should not be included in mscorlib. This should only be included in FX libraries that need to provide switches
9 using System.Collections.Generic;
10 using System.Reflection;
11 using System.Runtime.CompilerServices;
12 using System.Threading;
16 internal static partial class LocalAppContext
18 private delegate bool TryGetSwitchDelegate(string switchName, out bool value);
20 private static TryGetSwitchDelegate TryGetSwitchFromCentralAppContext;
21 private static bool s_canForwardCalls;
23 private static Dictionary<string, bool> s_switchMap = new Dictionary<string, bool>();
24 private static readonly object s_syncLock = new object();
26 private static bool DisableCaching { get; set; }
28 static LocalAppContext()
30 // Try to setup the callback into the central AppContext
31 s_canForwardCalls = SetupDelegate();
33 // Populate the default values of the local app context
34 AppContextDefaultValues.PopulateDefaultValues();
36 // Cache the value of the switch that help with testing
37 DisableCaching = IsSwitchEnabled(@"TestSwitch.LocalAppContext.DisableCaching");
40 public static bool IsSwitchEnabled(string switchName)
42 if (s_canForwardCalls)
44 bool isEnabledCentrally;
45 if (TryGetSwitchFromCentralAppContext(switchName, out isEnabledCentrally))
47 // we found the switch, so return whatever value it has
48 return isEnabledCentrally;
50 // if we could not get the value from the central authority, try the local storage.
53 return IsSwitchEnabledLocal(switchName);
56 private static bool IsSwitchEnabledLocal(string switchName)
58 // read the value from the set of local defaults
59 bool isEnabled, isPresent;
62 isPresent = s_switchMap.TryGetValue(switchName, out isEnabled);
65 // If the value is in the set of local switches, reutrn the value
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'
76 private static bool SetupDelegate()
78 Type appContextType = typeof(object).Assembly.GetType("System.AppContext");
79 if (appContextType == null)
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
91 // Create delegate if we found the method.
92 TryGetSwitchFromCentralAppContext = (TryGetSwitchDelegate)Delegate.CreateDelegate(typeof(TryGetSwitchDelegate), method);
97 [MethodImpl(MethodImplOptions.AggressiveInlining)]
98 internal static bool GetCachedSwitchValue(string switchName, ref int switchValue)
100 if (switchValue < 0) return false;
101 if (switchValue > 0) return true;
103 return GetCachedSwitchValueInternal(switchName, ref switchValue);
106 private static bool GetCachedSwitchValueInternal(string switchName, ref int switchValue)
108 if (LocalAppContext.DisableCaching)
110 return LocalAppContext.IsSwitchEnabled(switchName);
113 bool isEnabled = LocalAppContext.IsSwitchEnabled(switchName);
114 switchValue = isEnabled ? 1 /*true*/ : -1 /*false*/;
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.
123 internal static void DefineSwitchDefault(string switchName, bool initialValue)
125 s_switchMap[switchName] = initialValue;