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