Merge pull request #1909 from esdrubal/reflection
[mono.git] / mcs / class / corlib / coreclr / AsyncLocal.cs
1 // Copyright (c) Microsoft. All rights reserved.
2 // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
4 using System;
5 using System.Collections.Generic;
6 using System.Diagnostics.Contracts;
7 using System.Security;
8
9 namespace System.Threading
10 {
11     //
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:
14     //
15     // static AsyncLocal<Culture> s_currentCulture = new AsyncLocal<Culture>();
16     //
17     // static async Task SomeOperationAsync(Culture culture)
18     // {
19     //    s_currentCulture.Value = culture;
20     //
21     //    await FooAsync();
22     // }
23     //
24     // static async Task FooAsync()
25     // {
26     //    PrintStringWithCulture(s_currentCulture.Value);
27     // }
28     //
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:
33     //
34     // static AsyncLocal<Culture> s_currentCulture = new AsyncLocal<Culture>(
35     //   args =>
36     //   {
37     //      NativeMethods.SetThreadCulture(args.CurrentValue.LCID);
38     //   });
39     //
40     public sealed class AsyncLocal<T> : IAsyncLocal
41     {
42         [SecurityCritical] // critical because this action will terminate the process if it throws.
43         private readonly Action<AsyncLocalValueChangedArgs<T>> m_valueChangedHandler;
44
45         //
46         // Constructs an AsyncLocal<T> that does not receive change notifications.
47         //
48         public AsyncLocal() 
49         {
50         }
51
52         //
53         // Constructs an AsyncLocal<T> with a delegate that is called whenever the current value changes
54         // on any thread.
55         //
56         [SecurityCritical]
57         public AsyncLocal(Action<AsyncLocalValueChangedArgs<T>> valueChangedHandler) 
58         {
59             m_valueChangedHandler = valueChangedHandler;
60         }
61
62         public T Value
63         {
64             [SecuritySafeCritical]
65             get 
66             { 
67 #if MONO
68                 throw new NotImplementedException ();
69 #else                
70                 object obj = ExecutionContext.GetLocalValue(this);
71                 return (obj == null) ? default(T) : (T)obj;
72 #endif                
73             }
74             [SecuritySafeCritical]
75             set 
76             {
77 #if MONO
78                 throw new NotImplementedException ();
79 #else                
80                 ExecutionContext.SetLocalValue(this, value, m_valueChangedHandler != null); 
81 #endif
82             }
83         }
84
85         [SecurityCritical]
86         void IAsyncLocal.OnValueChanged(object previousValueObj, object currentValueObj, bool contextChanged)
87         {
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));
92         }
93     }
94
95     //
96     // Interface to allow non-generic code in ExecutionContext to call into the generic AsyncLocal<T> type.
97     //
98     internal interface IAsyncLocal
99     {
100         [SecurityCritical]
101         void OnValueChanged(object previousValue, object currentValue, bool contextChanged);
102     }
103
104     public struct AsyncLocalValueChangedArgs<T>
105     {
106         public T PreviousValue { get; private set; }
107         public T CurrentValue { get; private set; }
108         
109         //
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.
112         //
113         public bool ThreadContextChanged { get; private set; }
114
115         internal AsyncLocalValueChangedArgs(T previousValue, T currentValue, bool contextChanged)
116             : this()
117         {
118             PreviousValue = previousValue;
119             CurrentValue = currentValue;
120             ThreadContextChanged = contextChanged;
121         }
122     }
123 }