private ExecutionContext?ShallowClone(bool isFlowSuppressed) { Debug.Assert(isFlowSuppressed != m_isFlowSuppressed); if (m_localValues == null || AsyncLocalValueMap.IsEmpty(m_localValues)) { return(isFlowSuppressed ? DefaultFlowSuppressed : null); // implies the default context } return(new ExecutionContext(m_localValues, m_localChangeNotifications, isFlowSuppressed)); }
private ExecutionContext?ShallowClone(bool isFlowSuppressed) { Debug.Assert(isFlowSuppressed != m_isFlowSuppressed); if (m_localValues == null || AsyncLocalValueMap.IsEmpty(m_localValues)) { #pragma warning disable CA1825 // Avoid unnecessary zero-length array allocations return(isFlowSuppressed ? (s_defaultFlowSuppressed ??= new ExecutionContext(AsyncLocalValueMap.Empty, new IAsyncLocal[0], isFlowSuppressed: true)) : null); // implies the default context #pragma warning restore CA1825 } return(new ExecutionContext(m_localValues, m_localChangeNotifications, isFlowSuppressed)); }
internal static void SetLocalValue(IAsyncLocal local, object newValue, bool needChangeNotifications) { ExecutionContext mutableExecutionContext = Thread.CurrentThread.GetMutableExecutionContext(); object obj = null; bool flag = mutableExecutionContext._localValues != null && mutableExecutionContext._localValues.TryGetValue(local, out obj); if (obj == newValue) { return; } IAsyncLocalValueMap asyncLocalValueMap = mutableExecutionContext._localValues; if (asyncLocalValueMap == null) { asyncLocalValueMap = AsyncLocalValueMap.Create(local, newValue, !needChangeNotifications); } else { asyncLocalValueMap = asyncLocalValueMap.Set(local, newValue, !needChangeNotifications); } mutableExecutionContext._localValues = asyncLocalValueMap; if (needChangeNotifications) { if (!flag) { IAsyncLocal[] array = mutableExecutionContext._localChangeNotifications; if (array == null) { array = new IAsyncLocal[] { local }; } else { int num = array.Length; Array.Resize <IAsyncLocal>(ref array, num + 1); array[num] = local; } mutableExecutionContext._localChangeNotifications = array; } local.OnValueChanged(obj, newValue, false); } }
internal static void SetLocalValue(IAsyncLocal local, object newValue, bool needChangeNotifications) { ExecutionContext current = Thread.CurrentThread.ExecutionContext; object previousValue = null; bool hadPreviousValue = false; if (current != null) { hadPreviousValue = current.m_localValues.TryGetValue(local, out previousValue); } if (previousValue == newValue) { return; } // Regarding 'treatNullValueAsNonexistent: !needChangeNotifications' below: // - When change notifications are not necessary for this IAsyncLocal, there is no observable difference between // storing a null value and removing the IAsyncLocal from 'm_localValues' // - When change notifications are necessary for this IAsyncLocal, the IAsyncLocal's absence in 'm_localValues' // indicates that this is the first value change for the IAsyncLocal and it needs to be registered for change // notifications. So in this case, a null value must be stored in 'm_localValues' to indicate that the IAsyncLocal // is already registered for change notifications. IAsyncLocal[] newChangeNotifications = null; IAsyncLocalValueMap newValues; bool isFlowSuppressed = false; if (current != null) { isFlowSuppressed = current.m_isFlowSuppressed; newValues = current.m_localValues.Set(local, newValue, treatNullValueAsNonexistent: !needChangeNotifications); newChangeNotifications = current.m_localChangeNotifications; } else { // First AsyncLocal newValues = AsyncLocalValueMap.Create(local, newValue, treatNullValueAsNonexistent: !needChangeNotifications); } // // Either copy the change notification array, or create a new one, depending on whether we need to add a new item. // if (needChangeNotifications) { if (hadPreviousValue) { Debug.Assert(newChangeNotifications != null); Debug.Assert(Array.IndexOf(newChangeNotifications, local) >= 0); } else if (newChangeNotifications == null) { newChangeNotifications = new IAsyncLocal[1] { local }; } else { int newNotificationIndex = newChangeNotifications.Length; Array.Resize(ref newChangeNotifications, newNotificationIndex + 1); newChangeNotifications[newNotificationIndex] = local; } } Thread.CurrentThread.ExecutionContext = (!isFlowSuppressed && AsyncLocalValueMap.IsEmpty(newValues)) ? null : // No values, return to Default context new ExecutionContext(newValues, newChangeNotifications, isFlowSuppressed); if (needChangeNotifications) { local.OnValueChanged(previousValue, newValue, contextChanged: false); } }