3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 // <OWNER>Microsoft</OWNER>
8 using System
.Collections
.Generic
;
9 using System
.Diagnostics
.Contracts
;
10 using System
.Security
;
12 namespace System
.Threading
15 // AsyncLocal<T> represents "ambient" data that is local to a given asynchronous control flow, such as an
16 // async method. For example, say you want to associate a culture with a given async flow:
18 // static AsyncLocal<Culture> s_currentCulture = new AsyncLocal<Culture>();
20 // static async Task SomeOperationAsync(Culture culture)
22 // s_currentCulture.Value = culture;
27 // static async Task FooAsync()
29 // PrintStringWithCulture(s_currentCulture.Value);
32 // AsyncLocal<T> also provides optional notifications when the value associated with the current thread
33 // changes, either because it was explicitly changed by setting the Value property, or implicitly changed
34 // when the thread encountered an "await" or other context transition. For example, we might want our
35 // current culture to be communicated to the OS as well:
37 // static AsyncLocal<Culture> s_currentCulture = new AsyncLocal<Culture>(
40 // NativeMethods.SetThreadCulture(args.CurrentValue.LCID);
43 public sealed class AsyncLocal
<T
> : IAsyncLocal
45 [SecurityCritical
] // critical because this action will terminate the process if it throws.
46 private readonly Action
<AsyncLocalValueChangedArgs
<T
>> m_valueChangedHandler
;
49 // Constructs an AsyncLocal<T> that does not receive change notifications.
56 // Constructs an AsyncLocal<T> with a delegate that is called whenever the current value changes
60 public AsyncLocal(Action
<AsyncLocalValueChangedArgs
<T
>> valueChangedHandler
)
62 m_valueChangedHandler
= valueChangedHandler
;
67 [SecuritySafeCritical
]
70 object obj
= ExecutionContext
.GetLocalValue(this);
71 return (obj
== null) ? default(T
) : (T
)obj
;
73 [SecuritySafeCritical
]
76 ExecutionContext
.SetLocalValue(this, value, m_valueChangedHandler
!= null);
81 void IAsyncLocal
.OnValueChanged(object previousValueObj
, object currentValueObj
, bool contextChanged
)
83 Contract
.Assert(m_valueChangedHandler
!= null);
84 T previousValue
= previousValueObj
== null ? default(T
) : (T
)previousValueObj
;
85 T currentValue
= currentValueObj
== null ? default(T
) : (T
)currentValueObj
;
86 m_valueChangedHandler(new AsyncLocalValueChangedArgs
<T
>(previousValue
, currentValue
, contextChanged
));
91 // Interface to allow non-generic code in ExecutionContext to call into the generic AsyncLocal<T> type.
93 internal interface IAsyncLocal
96 void OnValueChanged(object previousValue
, object currentValue
, bool contextChanged
);
99 public struct AsyncLocalValueChangedArgs
<T
>
101 public T PreviousValue { get; private set; }
102 public T CurrentValue { get; private set; }
105 // If the value changed because we changed to a different ExecutionContext, this is true. If it changed
106 // because someone set the Value property, this is false.
108 public bool ThreadContextChanged { get; private set; }
110 internal AsyncLocalValueChangedArgs(T previousValue
, T currentValue
, bool contextChanged
)
113 PreviousValue
= previousValue
;
114 CurrentValue
= currentValue
;
115 ThreadContextChanged
= contextChanged
;