1 // Copyright (c) Microsoft. All rights reserved.
2 // Licensed under the MIT license. See LICENSE file in the project root for full license information.
5 using System.Collections.Generic;
6 using System.Diagnostics.Contracts;
9 namespace System.Threading
12 // AsyncLocal<T> represents "ambient" data that is local to a given asynchronous control flow, such as an
13 // async method. For example, say you want to associate a culture with a given async flow:
15 // static AsyncLocal<Culture> s_currentCulture = new AsyncLocal<Culture>();
17 // static async Task SomeOperationAsync(Culture culture)
19 // s_currentCulture.Value = culture;
24 // static async Task FooAsync()
26 // PrintStringWithCulture(s_currentCulture.Value);
29 // AsyncLocal<T> also provides optional notifications when the value associated with the current thread
30 // changes, either because it was explicitly changed by setting the Value property, or implicitly changed
31 // when the thread encountered an "await" or other context transition. For example, we might want our
32 // current culture to be communicated to the OS as well:
34 // static AsyncLocal<Culture> s_currentCulture = new AsyncLocal<Culture>(
37 // NativeMethods.SetThreadCulture(args.CurrentValue.LCID);
40 public sealed class AsyncLocal<T> : IAsyncLocal
42 [SecurityCritical] // critical because this action will terminate the process if it throws.
43 private readonly Action<AsyncLocalValueChangedArgs<T>> m_valueChangedHandler;
46 // Constructs an AsyncLocal<T> that does not receive change notifications.
53 // Constructs an AsyncLocal<T> with a delegate that is called whenever the current value changes
57 public AsyncLocal(Action<AsyncLocalValueChangedArgs<T>> valueChangedHandler)
59 m_valueChangedHandler = valueChangedHandler;
64 [SecuritySafeCritical]
68 throw new NotImplementedException ();
70 object obj = ExecutionContext.GetLocalValue(this);
71 return (obj == null) ? default(T) : (T)obj;
74 [SecuritySafeCritical]
78 throw new NotImplementedException ();
80 ExecutionContext.SetLocalValue(this, value, m_valueChangedHandler != null);
86 void IAsyncLocal.OnValueChanged(object previousValueObj, object currentValueObj, bool contextChanged)
88 Contract.Assert(m_valueChangedHandler != null);
89 T previousValue = previousValueObj == null ? default(T) : (T)previousValueObj;
90 T currentValue = currentValueObj == null ? default(T) : (T)currentValueObj;
91 m_valueChangedHandler(new AsyncLocalValueChangedArgs<T>(previousValue, currentValue, contextChanged));
96 // Interface to allow non-generic code in ExecutionContext to call into the generic AsyncLocal<T> type.
98 internal interface IAsyncLocal
101 void OnValueChanged(object previousValue, object currentValue, bool contextChanged);
104 public struct AsyncLocalValueChangedArgs<T>
106 public T PreviousValue { get; private set; }
107 public T CurrentValue { get; private set; }
110 // If the value changed because we changed to a different ExecutionContext, this is true. If it changed
111 // because someone set the Value property, this is false.
113 public bool ThreadContextChanged { get; private set; }
115 internal AsyncLocalValueChangedArgs(T previousValue, T currentValue, bool contextChanged)
118 PreviousValue = previousValue;
119 CurrentValue = currentValue;
120 ThreadContextChanged = contextChanged;