CrossAppDomainChannel.cs: Make _ContextID an object that fixes bug #422491.
[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.Threading;
37 using System.Runtime.Remoting;
38 using System.Runtime.Remoting.Proxies;
39 using System.Runtime.Remoting.Activation;
40 using System.Runtime.Remoting.Messaging;
41 using System.Runtime.Remoting.Lifetime;
42
43 namespace System.Runtime.Remoting.Contexts {
44
45 #if NET_2_0
46         [System.Runtime.InteropServices.ComVisible (true)]
47 #endif
48         public class Context 
49         {
50 #pragma warning disable 169, 414
51                 #region Sync with domain-internals.h
52                 int domain_id;
53                 int context_id;
54                 UIntPtr static_data; /* GC-tracked */
55                 #endregion
56 #pragma warning restore 169, 414
57
58                 // Default server context sink chain
59                 static IMessageSink default_server_context_sink;
60
61                 // The sink chain that has to be used by all calls entering the context
62                 IMessageSink server_context_sink_chain = null;
63
64                 // The sink chain that has to be used by all calls exiting the context
65                 IMessageSink client_context_sink_chain = null;
66
67                 object[] datastore;
68                 ArrayList context_properties;
69                 bool frozen;
70                 
71                 static int global_count;
72                 static Hashtable namedSlots = new Hashtable ();
73
74                 static DynamicPropertyCollection global_dynamic_properties;
75                 DynamicPropertyCollection context_dynamic_properties;
76                 ContextCallbackObject callback_object = null;
77                 
78                 public Context ()
79                 {
80                         domain_id = Thread.GetDomainID();
81                         context_id = 1 + global_count++;
82                 }
83
84                 ~Context ()
85                 {
86                 }
87
88                 public static Context DefaultContext {
89                         get {
90                                 return AppDomain.InternalGetDefaultContext ();
91                         }
92                 }
93
94                 public virtual int ContextID {
95                         get {
96                                 return context_id;
97                         }
98                 }
99
100                 public virtual IContextProperty[] ContextProperties
101                 {
102                         get 
103                         {
104                                 if (context_properties == null) return new IContextProperty[0];
105                                 else return (IContextProperty[]) context_properties.ToArray (typeof(IContextProperty[]));
106                         }
107                 }
108                 
109                 internal bool IsDefaultContext
110                 {
111                         get { return context_id == 0; }
112                 }
113
114                 internal bool NeedsContextSink
115                 {
116                         get {
117                                 return context_id != 0 || 
118                                         (global_dynamic_properties != null && global_dynamic_properties.HasProperties) || 
119                                         (context_dynamic_properties != null && context_dynamic_properties.HasProperties);
120                         }
121                 }
122
123                 public static bool RegisterDynamicProperty(IDynamicProperty prop, ContextBoundObject obj, Context ctx)
124                 {
125                         DynamicPropertyCollection col = GetDynamicPropertyCollection (obj, ctx);
126                         return col.RegisterDynamicProperty (prop);
127                 }
128
129                 public static bool UnregisterDynamicProperty(string name, ContextBoundObject obj, Context ctx)
130                 {
131                         DynamicPropertyCollection col = GetDynamicPropertyCollection (obj, ctx);
132                         return col.UnregisterDynamicProperty (name);
133                 }
134                 
135                 static DynamicPropertyCollection GetDynamicPropertyCollection(ContextBoundObject obj, Context ctx)
136                 {
137                         if (ctx == null && obj != null)
138                         {
139                                 if (RemotingServices.IsTransparentProxy(obj))
140                                 {
141                                         RealProxy rp = RemotingServices.GetRealProxy (obj);
142                                         return rp.ObjectIdentity.ClientDynamicProperties;
143                                 }
144                                 else
145                                         return obj.ObjectIdentity.ServerDynamicProperties;
146                         }
147                         else if (ctx != null && obj == null)
148                         {
149                                 if (ctx.context_dynamic_properties == null) ctx.context_dynamic_properties = new DynamicPropertyCollection ();
150                                 return ctx.context_dynamic_properties;
151                         }
152                         else if (ctx == null && obj == null)
153                         {
154                                 if (global_dynamic_properties == null) global_dynamic_properties = new DynamicPropertyCollection ();
155                                 return global_dynamic_properties;
156                         }
157                         else
158                                 throw new ArgumentException ("Either obj or ctx must be null");
159                 }
160                 
161                 internal static void NotifyGlobalDynamicSinks  (bool start, IMessage req_msg, bool client_site, bool async)
162                 {
163                         if (global_dynamic_properties != null && global_dynamic_properties.HasProperties) 
164                                 global_dynamic_properties.NotifyMessage (start, req_msg, client_site, async);
165                 }
166
167                 internal static bool HasGlobalDynamicSinks
168                 {
169                         get { return (global_dynamic_properties != null && global_dynamic_properties.HasProperties); }
170                 }
171
172                 internal void NotifyDynamicSinks  (bool start, IMessage req_msg, bool client_site, bool async)
173                 {
174                         if (context_dynamic_properties != null && context_dynamic_properties.HasProperties) 
175                                 context_dynamic_properties.NotifyMessage (start, req_msg, client_site, async);
176                 }
177
178                 internal bool HasDynamicSinks
179                 {
180                         get { return (context_dynamic_properties != null && context_dynamic_properties.HasProperties); }
181                 }
182
183                 internal bool HasExitSinks
184                 {
185                         get
186                         {
187                                 // Needs to go through the client context sink if there are custom
188                                 // client context or dynamic sinks.
189
190                                 return ( !(GetClientContextSinkChain() is ClientContextTerminatorSink) || HasDynamicSinks || HasGlobalDynamicSinks);
191                         }
192                 }
193
194                 public virtual IContextProperty GetProperty (string name)
195                 {
196                         if (context_properties == null)
197                                 return null;
198
199                         foreach (IContextProperty p in context_properties)
200                                 if (p.Name == name)
201                                         return p;
202                         
203                         return null;
204                 }
205
206                 public virtual void SetProperty (IContextProperty prop)
207                 {
208                         if (prop == null)
209                                 throw new ArgumentNullException ("IContextProperty");
210                         if (this == DefaultContext)
211                                 throw new InvalidOperationException ("Can not add properties to " +
212                                                                      "default context");
213                         if (frozen)
214                                 throw new InvalidOperationException ("Context is Frozen");
215                         
216                         if (context_properties == null)
217                                 context_properties = new ArrayList ();
218
219                         context_properties.Add (prop);
220                 }
221
222                 public virtual void Freeze ()
223                 {
224                         if (context_properties != null)
225                         {
226                                 foreach (IContextProperty prop in context_properties)
227                                         prop.Freeze (this);
228                         }
229                 }
230
231                 public override string ToString()
232                 {
233                         return "ContextID: " + context_id;
234                 }
235
236                 internal IMessageSink GetServerContextSinkChain()
237                 {
238                         if (server_context_sink_chain == null)
239                         {
240                                 if (default_server_context_sink == null)
241                                         default_server_context_sink = new ServerContextTerminatorSink();
242
243                                 server_context_sink_chain = default_server_context_sink;
244
245                                 if (context_properties != null) {
246                                         // Enumerate in reverse order
247                                         for (int n = context_properties.Count-1; n>=0; n--) {
248                                                 IContributeServerContextSink contributor = context_properties[n] as IContributeServerContextSink;
249                                                 if (contributor != null)
250                                                         server_context_sink_chain = contributor.GetServerContextSink (server_context_sink_chain);
251                                         }
252                                 }
253                         }
254                         return server_context_sink_chain;
255                 }
256
257                 internal IMessageSink GetClientContextSinkChain()
258                 {
259                         if (client_context_sink_chain == null)
260                         {
261                                 client_context_sink_chain = new ClientContextTerminatorSink (this);
262
263                                 if (context_properties != null) {
264                                         foreach (IContextProperty prop in context_properties) {
265                                                 IContributeClientContextSink contributor = prop as IContributeClientContextSink;
266                                                 if (contributor != null)
267                                                         client_context_sink_chain = contributor.GetClientContextSink (client_context_sink_chain);
268                                         }
269                                 }
270                         }
271                         return client_context_sink_chain;
272                 }
273
274                 internal IMessageSink CreateServerObjectSinkChain (MarshalByRefObject obj, bool forceInternalExecute)
275                 {
276                         IMessageSink objectSink = new StackBuilderSink (obj, forceInternalExecute);
277                         objectSink = new ServerObjectTerminatorSink (objectSink);
278                         objectSink = new Lifetime.LeaseSink (objectSink);
279
280                         if (context_properties != null)
281                         {
282                                 // Contribute object sinks in reverse order
283                                 for (int n = context_properties.Count-1; n >= 0; n--)
284                                 {
285                                         IContextProperty prop = (IContextProperty) context_properties[n];
286                                         IContributeObjectSink contributor = prop as IContributeObjectSink;
287                                         if (contributor != null)
288                                                 objectSink = contributor.GetObjectSink (obj, objectSink);
289                                 }
290                         }
291                         return objectSink;
292                 }
293
294                 internal IMessageSink CreateEnvoySink (MarshalByRefObject serverObject)
295                 {
296                         IMessageSink sink = EnvoyTerminatorSink.Instance;
297                         if (context_properties != null)
298                         {
299                                 foreach (IContextProperty prop in context_properties)
300                                 {
301                                         IContributeEnvoySink contributor = prop as IContributeEnvoySink;
302                                         if (contributor != null)
303                                                 sink = contributor.GetEnvoySink (serverObject, sink);
304                                 }
305                         }
306                         return sink;
307                 }
308
309                 internal static Context SwitchToContext (Context newContext)
310                 {
311                         return AppDomain.InternalSetContext (newContext);
312                 }
313
314                 internal static Context CreateNewContext (IConstructionCallMessage msg)
315                 {
316                         // Create the new context
317
318                         Context newContext = new Context();
319
320                         foreach (IContextProperty prop in msg.ContextProperties)
321                         {
322                                 if (newContext.GetProperty (prop.Name) == null)
323                                         newContext.SetProperty (prop);
324                         }
325                         newContext.Freeze();
326
327
328                         // Ask each context property whether the new context is OK
329
330                         foreach (IContextProperty prop in msg.ContextProperties)
331                                 if (!prop.IsNewContextOK (newContext)) 
332                                         throw new RemotingException("A context property did not approve the candidate context for activating the object");
333
334                         return newContext;
335                 }
336                 
337                 public void DoCallBack (CrossContextDelegate deleg)
338                 {
339                         lock (this)
340                         {
341                                 if (callback_object == null) {
342                                         Context oldContext = Context.SwitchToContext (this);
343                                         callback_object = new ContextCallbackObject ();
344                                         Context.SwitchToContext (oldContext);
345                                 }
346                         }
347                         
348                         callback_object.DoCallBack (deleg);
349                 }
350                 
351                 public static LocalDataStoreSlot AllocateDataSlot ()
352                 {
353                         return new LocalDataStoreSlot (false);
354                 }
355                 
356                 public static LocalDataStoreSlot AllocateNamedDataSlot (string name)
357                 {
358                         lock (namedSlots.SyncRoot)
359                         {
360                                 LocalDataStoreSlot slot = AllocateDataSlot ();
361                                 namedSlots.Add (name, slot);
362                                 return slot;
363                         }
364                 }
365                 
366                 public static void FreeNamedDataSlot (string name)
367                 {
368                         lock (namedSlots.SyncRoot)
369                         {
370                                 namedSlots.Remove (name);
371                         }
372                 }
373                 
374                 public static object GetData (LocalDataStoreSlot slot)
375                 {
376                         Context ctx = Thread.CurrentContext;
377                         
378                         lock (ctx)
379                         {
380                                 if (ctx.datastore != null && slot.slot < ctx.datastore.Length)
381                                         return ctx.datastore [slot.slot];
382                                 return null;
383                         }
384                 }
385                 
386                 public static LocalDataStoreSlot GetNamedDataSlot (string name)
387                 {
388                         lock (namedSlots.SyncRoot)
389                         {
390                                 LocalDataStoreSlot slot = namedSlots [name] as LocalDataStoreSlot;
391                                 if (slot == null) return AllocateNamedDataSlot (name);
392                                 else return slot;
393                         }
394                 }
395                 
396                 public static void SetData (LocalDataStoreSlot slot, object data)
397                 {
398                         Context ctx = Thread.CurrentContext;
399                         lock (ctx)
400                         {
401                                 if (ctx.datastore == null) {
402                                         ctx.datastore = new object [slot.slot + 2];
403                                 } else if (slot.slot >= ctx.datastore.Length) {
404                                         object[] nslots = new object [slot.slot + 2];
405                                         ctx.datastore.CopyTo (nslots, 0);
406                                         ctx.datastore = nslots;
407                                 }
408                                 ctx.datastore [slot.slot] = data;
409                         }
410                 }
411         }
412
413         class DynamicPropertyCollection
414         {
415                 ArrayList _properties = new ArrayList();
416
417                 class DynamicPropertyReg
418                 {
419                         public IDynamicProperty Property;
420                         public IDynamicMessageSink Sink;
421                 }
422
423                 public bool HasProperties
424                 {
425                         get { return _properties.Count > 0; }
426                 }
427
428                 public bool RegisterDynamicProperty(IDynamicProperty prop)
429                 {
430                         lock (this)
431                         {
432                                 if (FindProperty (prop.Name) != -1) 
433                                         throw new InvalidOperationException ("Another property by this name already exists");
434
435                                 // Make a copy, do not interfere with threads running dynamic sinks
436                                 ArrayList newProps = new ArrayList (_properties);
437
438                                 DynamicPropertyReg reg = new DynamicPropertyReg();
439                                 reg.Property = prop;
440                                 IContributeDynamicSink contributor = prop as IContributeDynamicSink;
441                                 if (contributor != null) reg.Sink = contributor.GetDynamicSink ();
442                                 newProps.Add (reg);
443
444                                 _properties = newProps;
445
446                                 return true;    // When should be false?
447                         }
448                 }
449
450                 public bool UnregisterDynamicProperty(string name)
451                 {
452                         lock (this)
453                         {
454                                 int i = FindProperty (name);
455                                 if (i == -1) throw new RemotingException ("A property with the name " + name + " was not found");
456
457                                 _properties.RemoveAt (i);
458                                 return true;    // When should be false?
459                         }
460                 }
461
462                 public void NotifyMessage (bool start, IMessage msg, bool client_site, bool async)
463                 {
464                         ArrayList props = _properties;
465                         if (start)
466                         {
467                                 foreach (DynamicPropertyReg reg in props)
468                                         if (reg.Sink != null) reg.Sink.ProcessMessageStart (msg, client_site, async);
469                         }
470                         else
471                         {
472                                 foreach (DynamicPropertyReg reg in props)
473                                         if (reg.Sink != null) reg.Sink.ProcessMessageFinish (msg, client_site, async);
474                         }
475                 }
476
477                 int FindProperty (string name)
478                 {
479                         for (int n=0; n<_properties.Count; n++)
480                                 if (((DynamicPropertyReg)_properties[n]).Property.Name == name)
481                                         return n;
482                         return -1;
483                 }
484         }
485         
486         class ContextCallbackObject: ContextBoundObject
487         {
488                 public void DoCallBack (CrossContextDelegate deleg)
489                 {
490                 }
491         }
492 }