Merge pull request #1322 from StephenMcConnel/bug23532
[mono.git] / mcs / class / corlib / System.Threading / ExecutionContext.cs
1 // 
2 // System.Threading.ExecutionContext.cs
3 //
4 // Authors:
5 //      Lluis Sanchez (lluis@novell.com)
6 //      Sebastien Pouliot  <sebastien@ximian.com>
7 //  Marek Safar (marek.safar@gmail.com)
8 //
9 // Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com)
10 // Copyright (C) 2014 Xamarin Inc (http://www.xamarin.com)
11 //
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
19 // 
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
22 // 
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 //
31
32 using System.Runtime.InteropServices;
33 using System.Runtime.Serialization;
34 using System.Security;
35 using System.Security.Permissions;
36 using System.Runtime.Remoting.Messaging;
37 using System.Collections.Generic;
38
39 namespace System.Threading {
40         [Serializable]
41         public sealed partial class ExecutionContext : ISerializable
42 #if NET_4_0
43            , IDisposable
44 #endif
45         {
46                 internal struct Switcher
47                 {
48                         readonly ExecutionContext ec;
49                         readonly LogicalCallContext _lcc;
50                         readonly bool _suppressFlow;
51                         readonly bool _capture;
52                         readonly Dictionary<string, object> local_data;
53                         readonly bool copy_on_write;
54
55                         public Switcher (ExecutionContext ec)
56                         {
57                                 this.ec = ec;
58                                 this._lcc = ec._lcc;
59                                 this._suppressFlow = ec._suppressFlow;
60                                 this._capture = ec._capture;
61                                 this.local_data = ec.local_data;
62                                 this.copy_on_write = ec.CopyOnWrite;
63                         }
64
65                         public bool IsEmpty {
66                                 get {
67                                         return ec == null;
68                                 }
69                         }
70
71                         public void Restore (ExecutionContext ec)
72                         {
73                                 ec._lcc = this._lcc;
74                                 ec._suppressFlow = this._suppressFlow;
75                                 ec._capture = this._capture;
76                                 ec.local_data = this.local_data;
77                                 ec.CopyOnWrite = this.copy_on_write;
78                         }
79                 }
80
81 #if !MOBILE
82                 private SecurityContext _sc;
83 #endif
84                 private LogicalCallContext _lcc;
85                 private bool _suppressFlow;
86                 private bool _capture;
87                 Dictionary<string, object> local_data;
88
89                 internal ExecutionContext ()
90                 {
91                 }
92
93                 private ExecutionContext (ExecutionContext ec)
94                 {
95                         CloneData (ec);
96
97                         _suppressFlow = ec._suppressFlow;
98                         _capture = true;
99                 }
100
101                 void CloneData (ExecutionContext ec)
102                 {
103 #if !MOBILE
104                         if (ec._sc != null)
105                                 _sc = new SecurityContext (ec._sc);
106 #endif
107                         if (ec._lcc != null)
108                                 _lcc = (LogicalCallContext) ec._lcc.Clone ();
109                 }
110                 
111                 [MonoTODO]
112                 internal ExecutionContext (SerializationInfo info, StreamingContext context)
113                 {
114                         throw new NotImplementedException ();
115                 }
116
117                 public static ExecutionContext Capture ()
118                 {
119                         return Capture (true, false);
120                 }
121                 
122                 internal static ExecutionContext Capture (bool captureSyncContext, bool nullOnEmpty)
123                 {
124                         var thread = Thread.CurrentThread;
125                         if (nullOnEmpty && !thread.HasExecutionContext)
126                                 return null;
127
128                         var ec = thread.ExecutionContext;
129                         if (ec.FlowSuppressed)
130                                 return null;
131
132                         if (nullOnEmpty
133 #if !MOBILE
134                          && ec._sc == null
135 #endif
136                                 && (ec._lcc == null || !ec._lcc.HasInfo))
137                                 return null;
138
139                         ExecutionContext capture = new ExecutionContext (ec);
140 #if !MOBILE
141                         if (SecurityManager.SecurityEnabled)
142                                 capture.SecurityContext = SecurityContext.Capture ();
143 #endif
144                         return capture;
145                 }
146                 
147                 public ExecutionContext CreateCopy ()
148                 {
149                         if (!_capture)
150                                 throw new InvalidOperationException ();
151
152                         return new ExecutionContext (this);
153                 }
154                 
155 #if NET_4_0
156                 public void Dispose ()
157                 {
158 #if !MOBILE
159                         if (_sc != null)
160                                 _sc.Dispose ();
161 #endif
162                 }
163 #endif
164
165                 [MonoTODO]
166                 [ReflectionPermission (SecurityAction.Demand, MemberAccess = true)]
167                 public void GetObjectData (SerializationInfo info, StreamingContext context)
168                 {
169                         if (info == null)
170                                 throw new ArgumentNullException ("info");
171                         throw new NotImplementedException ();
172                 }
173                 
174                 // internal stuff
175
176                 internal LogicalCallContext LogicalCallContext {
177                         get {
178                                 if (_lcc == null)
179                                         _lcc = new LogicalCallContext ();
180                                 return _lcc;
181                         }
182                         set {
183                                 _lcc = value;
184                         }
185                 }
186
187                 internal Dictionary<string, object> DataStore {
188                         get {
189                                 if (local_data == null)
190                                         local_data = new Dictionary<string, object> ();
191                                 return local_data;
192                         }
193                         set {
194                                 local_data = value;
195                         }
196                 }
197
198 #if !MOBILE
199                 internal SecurityContext SecurityContext {
200                         get {
201                                 if (_sc == null)
202                                         _sc = new SecurityContext ();
203                                 return _sc;
204                         }
205                         set { _sc = value; }
206                 }
207 #endif
208
209                 internal bool FlowSuppressed {
210                         get { return _suppressFlow; }
211                         set { _suppressFlow = value; }
212                 }
213
214                 internal bool CopyOnWrite { get; set; }
215
216                 public static bool IsFlowSuppressed ()
217                 {
218                         return Current.FlowSuppressed;
219                 }
220
221                 public static void RestoreFlow ()
222                 {
223                         ExecutionContext ec = Current;
224                         if (!ec.FlowSuppressed)
225                                 throw new InvalidOperationException ();
226
227                         ec.FlowSuppressed = false;
228                 }
229
230                 [SecurityPermission (SecurityAction.LinkDemand, Infrastructure = true)]
231                 public static void Run (ExecutionContext executionContext, ContextCallback callback, object state)
232                 {
233                         if (executionContext == null) {
234                                 throw new InvalidOperationException (Locale.GetText (
235                                         "Null ExecutionContext"));
236                         }
237
238                         var prev = Current;
239                         try {
240                                 Thread.CurrentThread.ExecutionContext = executionContext;
241                                 callback (state);
242                         } finally {
243                                 Thread.CurrentThread.ExecutionContext = prev;
244                         }
245                 }
246
247                 public static AsyncFlowControl SuppressFlow ()
248                 {
249                         Thread t = Thread.CurrentThread;
250                         t.ExecutionContext.FlowSuppressed = true;
251                         return new AsyncFlowControl (t, AsyncFlowControlType.Execution);
252                 }
253
254                 internal static LogicalCallContext CreateLogicalCallContext (bool createEmpty)
255                 {
256                         var lcc = Current._lcc;
257                         if (lcc == null) {
258                                 if (createEmpty)
259                                         lcc = new LogicalCallContext ();
260
261                                 return lcc;
262                         }
263
264                         return (LogicalCallContext) lcc.Clone ();
265                 }
266
267                 internal void FreeNamedDataSlot (string name)
268                 {
269                         if (_lcc != null)
270                                 _lcc.FreeNamedDataSlot (name);
271
272                         if (local_data != null)
273                                 local_data.Remove (name);
274                 }
275
276                 internal static ExecutionContext Current {
277                         get {
278                                 return Thread.CurrentThread.ExecutionContext;
279                         }
280                 }
281
282                 internal static ExecutionContext GetCurrentWritable ()
283                 {
284                         var current = Thread.CurrentThread.ExecutionContext;
285                         if (current.CopyOnWrite) {
286                                 current.CopyOnWrite = false;
287                                 current.CloneData (current);
288                         }
289
290                         return current;
291                 }
292         }
293 }