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