Merge pull request #2034 from alexrp/ctx-cleanup
[mono.git] / mcs / class / corlib / System.Runtime.Remoting.Contexts / Context.cs
1 //
2 // System.Runtime.Remoting.Contexts.Context..cs
3 //
4 // Author:
5 //   Miguel de Icaza (miguel@ximian.com)
6 //   Lluis Sanchez Gual (lluis@ideary.com)
7 //   Patrik Torstensson
8 //
9 // (C) Ximian, Inc.  http://www.ximian.com
10 //
11
12 //
13 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
14 //
15 // Permission is hereby granted, free of charge, to any person obtaining
16 // a copy of this software and associated documentation files (the
17 // "Software"), to deal in the Software without restriction, including
18 // without limitation the rights to use, copy, modify, merge, publish,
19 // distribute, sublicense, and/or sell copies of the Software, and to
20 // permit persons to whom the Software is furnished to do so, subject to
21 // the following conditions:
22 // 
23 // The above copyright notice and this permission notice shall be
24 // included in all copies or substantial portions of the Software.
25 // 
26 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
30 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
31 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
32 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33 //
34
35 using System.Collections;
36 using System.Collections.Generic;
37 using System.Threading;
38 using System.Runtime.Remoting;
39 using System.Runtime.Remoting.Proxies;
40 using System.Runtime.Remoting.Activation;
41 using System.Runtime.Remoting.Messaging;
42 using System.Runtime.Remoting.Lifetime;
43 using System.Runtime.CompilerServices;
44 using System.Runtime.InteropServices;
45
46
47 namespace System.Runtime.Remoting.Contexts {
48
49         [System.Runtime.InteropServices.ComVisible (true)]
50         [StructLayout (LayoutKind.Sequential)]
51         public class Context 
52         {
53 #pragma warning disable 169, 414
54                 #region Sync with domain-internals.h
55                 int domain_id;
56                 int context_id;
57                 UIntPtr static_data; /* GC-tracked */
58                 UIntPtr data;
59                 #endregion
60 #pragma warning restore 169, 414
61
62                 // Name is significant; used by the runtime.
63                 [ContextStatic]
64                 static object[] local_slots;
65
66                 // Default server context sink chain
67                 static IMessageSink default_server_context_sink;
68
69                 // The sink chain that has to be used by all calls entering the context
70                 IMessageSink server_context_sink_chain = null;
71
72                 // The sink chain that has to be used by all calls exiting the context
73                 IMessageSink client_context_sink_chain = null;
74
75                 List<IContextProperty> context_properties;
76 //              bool frozen;
77                 
78                 static int global_count;
79
80                 volatile LocalDataStoreHolder _localDataStore;
81
82                 static LocalDataStoreMgr _localDataStoreMgr = new LocalDataStoreMgr();
83
84                 static DynamicPropertyCollection global_dynamic_properties;
85                 DynamicPropertyCollection context_dynamic_properties;
86                 ContextCallbackObject callback_object = null;
87
88                 [MethodImpl (MethodImplOptions.InternalCall)]
89                 extern static void RegisterContext (Context ctx);
90
91                 [MethodImpl (MethodImplOptions.InternalCall)]
92                 extern static void ReleaseContext (Context ctx);
93                 
94                 public Context ()
95                 {
96                         domain_id = Thread.GetDomainID();
97                         context_id = Interlocked.Increment (ref global_count);
98
99                         RegisterContext (this);
100                 }
101
102                 ~Context ()
103                 {
104                         ReleaseContext (this);
105                 }
106
107                 public static Context DefaultContext {
108                         get {
109                                 return AppDomain.InternalGetDefaultContext ();
110                         }
111                 }
112
113                 public virtual int ContextID {
114                         get {
115                                 return context_id;
116                         }
117                 }
118
119                 public virtual IContextProperty[] ContextProperties
120                 {
121                         get 
122                         {
123                                 if (context_properties == null)
124                                         return new IContextProperty[0];
125                                 
126                                 return context_properties.ToArray ();
127                         }
128                 }
129                 
130                 internal bool IsDefaultContext
131                 {
132                         get { return context_id == 0; }
133                 }
134
135                 internal bool NeedsContextSink
136                 {
137                         get {
138                                 return context_id != 0 || 
139                                         (global_dynamic_properties != null && global_dynamic_properties.HasProperties) || 
140                                         (context_dynamic_properties != null && context_dynamic_properties.HasProperties);
141                         }
142                 }
143
144                 public static bool RegisterDynamicProperty(IDynamicProperty prop, ContextBoundObject obj, Context ctx)
145                 {
146                         DynamicPropertyCollection col = GetDynamicPropertyCollection (obj, ctx);
147                         return col.RegisterDynamicProperty (prop);
148                 }
149
150                 public static bool UnregisterDynamicProperty(string name, ContextBoundObject obj, Context ctx)
151                 {
152                         DynamicPropertyCollection col = GetDynamicPropertyCollection (obj, ctx);
153                         return col.UnregisterDynamicProperty (name);
154                 }
155                 
156                 static DynamicPropertyCollection GetDynamicPropertyCollection(ContextBoundObject obj, Context ctx)
157                 {
158                         if (ctx == null && obj != null)
159                         {
160                                 if (RemotingServices.IsTransparentProxy(obj))
161                                 {
162                                         RealProxy rp = RemotingServices.GetRealProxy (obj);
163                                         return rp.ObjectIdentity.ClientDynamicProperties;
164                                 }
165                                 else
166                                 {
167 #if FEATURE_REMOTING
168                                         return obj.ObjectIdentity.ServerDynamicProperties;
169 #else
170                                         throw new NotSupportedException ();
171 #endif                                  
172                                 }
173                         }
174                         else if (ctx != null && obj == null)
175                         {
176                                 if (ctx.context_dynamic_properties == null) ctx.context_dynamic_properties = new DynamicPropertyCollection ();
177                                 return ctx.context_dynamic_properties;
178                         }
179                         else if (ctx == null && obj == null)
180                         {
181                                 if (global_dynamic_properties == null) global_dynamic_properties = new DynamicPropertyCollection ();
182                                 return global_dynamic_properties;
183                         }
184                         else
185                                 throw new ArgumentException ("Either obj or ctx must be null");
186                 }
187                 
188                 internal static void NotifyGlobalDynamicSinks  (bool start, IMessage req_msg, bool client_site, bool async)
189                 {
190                         if (global_dynamic_properties != null && global_dynamic_properties.HasProperties) 
191                                 global_dynamic_properties.NotifyMessage (start, req_msg, client_site, async);
192                 }
193
194                 internal static bool HasGlobalDynamicSinks
195                 {
196                         get { return (global_dynamic_properties != null && global_dynamic_properties.HasProperties); }
197                 }
198
199                 internal void NotifyDynamicSinks  (bool start, IMessage req_msg, bool client_site, bool async)
200                 {
201                         if (context_dynamic_properties != null && context_dynamic_properties.HasProperties) 
202                                 context_dynamic_properties.NotifyMessage (start, req_msg, client_site, async);
203                 }
204
205                 internal bool HasDynamicSinks
206                 {
207                         get { return (context_dynamic_properties != null && context_dynamic_properties.HasProperties); }
208                 }
209
210                 internal bool HasExitSinks
211                 {
212                         get
213                         {
214                                 // Needs to go through the client context sink if there are custom
215                                 // client context or dynamic sinks.
216
217                                 return ( !(GetClientContextSinkChain() is ClientContextTerminatorSink) || HasDynamicSinks || HasGlobalDynamicSinks);
218                         }
219                 }
220
221                 public virtual IContextProperty GetProperty (string name)
222                 {
223                         if (context_properties == null)
224                                 return null;
225
226                         foreach (IContextProperty p in context_properties)
227                                 if (p.Name == name)
228                                         return p;
229                         
230                         return null;
231                 }
232
233                 public virtual void SetProperty (IContextProperty prop)
234                 {
235                         if (prop == null)
236                                 throw new ArgumentNullException ("IContextProperty");
237                         if (this == DefaultContext)
238                                 throw new InvalidOperationException ("Can not add properties to " +
239                                                                      "default context");
240 //                      if (frozen)
241 //                              throw new InvalidOperationException ("Context is Frozen");
242                         
243                         if (context_properties == null)
244                                 context_properties = new List<IContextProperty> ();
245
246                         context_properties.Add (prop);
247                 }
248
249                 public virtual void Freeze ()
250                 {
251                         if (context_properties != null)
252                         {
253                                 foreach (IContextProperty prop in context_properties)
254                                         prop.Freeze (this);
255                         }
256                 }
257
258                 public override string ToString()
259                 {
260                         return "ContextID: " + context_id;
261                 }
262
263                 internal IMessageSink GetServerContextSinkChain()
264                 {
265                         if (server_context_sink_chain == null)
266                         {
267                                 if (default_server_context_sink == null)
268                                         default_server_context_sink = new ServerContextTerminatorSink();
269
270                                 server_context_sink_chain = default_server_context_sink;
271
272                                 if (context_properties != null) {
273                                         // Enumerate in reverse order
274                                         for (int n = context_properties.Count-1; n>=0; n--) {
275                                                 IContributeServerContextSink contributor = context_properties[n] as IContributeServerContextSink;
276                                                 if (contributor != null)
277                                                         server_context_sink_chain = contributor.GetServerContextSink (server_context_sink_chain);
278                                         }
279                                 }
280                         }
281                         return server_context_sink_chain;
282                 }
283
284                 internal IMessageSink GetClientContextSinkChain()
285                 {
286                         if (client_context_sink_chain == null)
287                         {
288                                 client_context_sink_chain = new ClientContextTerminatorSink (this);
289
290                                 if (context_properties != null) {
291                                         foreach (IContextProperty prop in context_properties) {
292                                                 IContributeClientContextSink contributor = prop as IContributeClientContextSink;
293                                                 if (contributor != null)
294                                                         client_context_sink_chain = contributor.GetClientContextSink (client_context_sink_chain);
295                                         }
296                                 }
297                         }
298                         return client_context_sink_chain;
299                 }
300
301                 internal IMessageSink CreateServerObjectSinkChain (MarshalByRefObject obj, bool forceInternalExecute)
302                 {
303                         IMessageSink objectSink = new StackBuilderSink (obj, forceInternalExecute);
304                         objectSink = new ServerObjectTerminatorSink (objectSink);
305                         objectSink = new Lifetime.LeaseSink (objectSink);
306
307                         if (context_properties != null)
308                         {
309                                 // Contribute object sinks in reverse order
310                                 for (int n = context_properties.Count-1; n >= 0; n--)
311                                 {
312                                         IContextProperty prop = (IContextProperty) context_properties[n];
313                                         IContributeObjectSink contributor = prop as IContributeObjectSink;
314                                         if (contributor != null)
315                                                 objectSink = contributor.GetObjectSink (obj, objectSink);
316                                 }
317                         }
318                         return objectSink;
319                 }
320
321                 internal IMessageSink CreateEnvoySink (MarshalByRefObject serverObject)
322                 {
323                         IMessageSink sink = EnvoyTerminatorSink.Instance;
324                         if (context_properties != null)
325                         {
326                                 foreach (IContextProperty prop in context_properties)
327                                 {
328                                         IContributeEnvoySink contributor = prop as IContributeEnvoySink;
329                                         if (contributor != null)
330                                                 sink = contributor.GetEnvoySink (serverObject, sink);
331                                 }
332                         }
333                         return sink;
334                 }
335
336                 internal static Context SwitchToContext (Context newContext)
337                 {
338                         return AppDomain.InternalSetContext (newContext);
339                 }
340
341                 internal static Context CreateNewContext (IConstructionCallMessage msg)
342                 {
343                         // Create the new context
344
345                         Context newContext = new Context();
346
347                         foreach (IContextProperty prop in msg.ContextProperties)
348                         {
349                                 if (newContext.GetProperty (prop.Name) == null)
350                                         newContext.SetProperty (prop);
351                         }
352                         newContext.Freeze();
353
354
355                         // Ask each context property whether the new context is OK
356
357                         foreach (IContextProperty prop in msg.ContextProperties)
358                                 if (!prop.IsNewContextOK (newContext)) 
359                                         throw new RemotingException("A context property did not approve the candidate context for activating the object");
360
361                         return newContext;
362                 }
363                 
364                 public void DoCallBack (CrossContextDelegate deleg)
365                 {
366                         lock (this)
367                         {
368                                 if (callback_object == null) {
369                                         Context oldContext = Context.SwitchToContext (this);
370                                         callback_object = new ContextCallbackObject ();
371                                         Context.SwitchToContext (oldContext);
372                                 }
373                         }
374                         
375                         callback_object.DoCallBack (deleg);
376                 }
377
378                 private LocalDataStore MyLocalStore 
379                 {
380                         get 
381                         { 
382                                 if (_localDataStore == null)
383                                 {
384                                         // It's OK to lock the manager here because it is going to lock
385                                         // itself anyway.
386                                         lock (_localDataStoreMgr)
387                                         {
388                                                 if (_localDataStore == null)
389                                                 {
390                                                         // The local store has not yet been created for this thread.
391                                                         _localDataStore = _localDataStoreMgr.CreateLocalDataStore();
392                                                 }
393                                         }
394                                 }
395                                 return _localDataStore.Store;
396                         }
397                 }
398
399                 public static LocalDataStoreSlot AllocateDataSlot ()
400                 {
401                         return _localDataStoreMgr.AllocateDataSlot ();
402                 }
403
404                 public static LocalDataStoreSlot AllocateNamedDataSlot (string name)
405                 {
406                         return _localDataStoreMgr.AllocateNamedDataSlot (name);
407                 }
408
409                 public static void FreeNamedDataSlot (string name)
410                 {
411                         _localDataStoreMgr.FreeNamedDataSlot (name);
412                 }
413
414                 public static LocalDataStoreSlot GetNamedDataSlot (string name)
415                 {
416                         return _localDataStoreMgr.GetNamedDataSlot (name);
417                 }
418
419                 public static object GetData (LocalDataStoreSlot slot)
420                 {
421                         return Thread.CurrentContext.MyLocalStore.GetData (slot);
422                 }
423
424                 public static void SetData (LocalDataStoreSlot slot, object data)
425                 {
426                         Thread.CurrentContext.MyLocalStore.SetData (slot, data);
427                 }
428         }
429
430         class DynamicPropertyCollection
431         {
432                 ArrayList _properties = new ArrayList();
433
434                 class DynamicPropertyReg
435                 {
436                         public IDynamicProperty Property;
437                         public IDynamicMessageSink Sink;
438                 }
439
440                 public bool HasProperties
441                 {
442                         get { return _properties.Count > 0; }
443                 }
444
445                 public bool RegisterDynamicProperty(IDynamicProperty prop)
446                 {
447                         lock (this)
448                         {
449                                 if (FindProperty (prop.Name) != -1) 
450                                         throw new InvalidOperationException ("Another property by this name already exists");
451
452                                 // Make a copy, do not interfere with threads running dynamic sinks
453                                 ArrayList newProps = new ArrayList (_properties);
454
455                                 DynamicPropertyReg reg = new DynamicPropertyReg();
456                                 reg.Property = prop;
457                                 IContributeDynamicSink contributor = prop as IContributeDynamicSink;
458                                 if (contributor != null) reg.Sink = contributor.GetDynamicSink ();
459                                 newProps.Add (reg);
460
461                                 _properties = newProps;
462
463                                 return true;    // When should be false?
464                         }
465                 }
466
467                 public bool UnregisterDynamicProperty(string name)
468                 {
469                         lock (this)
470                         {
471                                 int i = FindProperty (name);
472                                 if (i == -1) throw new RemotingException ("A property with the name " + name + " was not found");
473
474                                 _properties.RemoveAt (i);
475                                 return true;    // When should be false?
476                         }
477                 }
478
479                 public void NotifyMessage (bool start, IMessage msg, bool client_site, bool async)
480                 {
481                         ArrayList props = _properties;
482                         if (start)
483                         {
484                                 foreach (DynamicPropertyReg reg in props)
485                                         if (reg.Sink != null) reg.Sink.ProcessMessageStart (msg, client_site, async);
486                         }
487                         else
488                         {
489                                 foreach (DynamicPropertyReg reg in props)
490                                         if (reg.Sink != null) reg.Sink.ProcessMessageFinish (msg, client_site, async);
491                         }
492                 }
493
494                 int FindProperty (string name)
495                 {
496                         for (int n=0; n<_properties.Count; n++)
497                                 if (((DynamicPropertyReg)_properties[n]).Property.Name == name)
498                                         return n;
499                         return -1;
500                 }
501         }
502         
503         class ContextCallbackObject: ContextBoundObject
504         {
505                 public void DoCallBack (CrossContextDelegate deleg)
506                 {
507                 }
508         }
509 }