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