6959edaf974541aa72744b83c06fc94a1de05fc6
[mono.git] / mcs / class / referencesource / mscorlib / system / runtime / remoting / identityholder.cs
1 // ==++==
2 // 
3 //   Copyright (c) Microsoft Corporation.  All rights reserved.
4 // 
5 // ==--==
6 namespace System.Runtime.Remoting {
7     using System.Globalization;
8     using System.Threading;
9     using System.Runtime.CompilerServices;
10     using System.Runtime.InteropServices;
11     using System.Runtime.Remoting;
12     using System.Runtime.Remoting.Contexts;
13     using System.Runtime.Remoting.Proxies;
14     using System.Runtime.Remoting.Messaging;
15     using System.Runtime.ConstrainedExecution;
16     using System.Reflection;
17     using System;
18     //  IdentityHolder maintains a lookup service for remoting identities. The methods
19     //  provided by it are used during calls to Wrap, UnWrap, Marshal, Unmarshal etc.
20     //
21     using System.Collections;
22     using System.Diagnostics.Contracts;
23
24     // This is just a internal struct to hold the various flags
25     // that get passed for different flavors of idtable operations
26     // just so that we do not have too many separate boolean parameters
27     // all over the place (eg. xxxIdentity(id,uri, true, false, true);)
28     internal struct IdOps
29     {
30         internal const int None           = 0x00000000;
31         internal const int GenerateURI    = 0x00000001;
32         internal const int StrongIdentity = 0x00000002;
33         internal const int IsInitializing = 0x00000004;    // Identity has not been fully initialized yet
34
35         internal static bool bStrongIdentity(int flags)
36         {
37             return (flags&StrongIdentity)!=0;
38         }
39
40         internal static bool bIsInitializing(int flags)
41         {
42             return (flags & IsInitializing) != 0;
43         }
44     }
45
46     // Internal enum to specify options for SetIdentity
47     [Serializable]
48     internal enum DuplicateIdentityOption
49     {
50         Unique,      // -throw an exception if there is already an identity in the table
51         UseExisting, // -if there is already an identity in the table, then use that one.
52                      //    (could happen in a Connect ----, but we don't care which identity we get)
53     } // enum DuplicateIdentityOption
54     
55     
56     internal sealed class IdentityHolder
57     {
58         // private static Timer CleanupTimer = null;
59         // private const  int CleanupInterval = 60000;           // 1 minute.
60
61         // private static Object staticSyncObject = new Object();
62         private static volatile int SetIDCount=0;
63         private const int CleanUpCountInterval = 0x40;
64         private const int INFINITE = 0x7fffffff;
65
66         private static Hashtable _URITable = new Hashtable();
67         private static volatile Context _cachedDefaultContext = null;
68
69            
70         internal static Hashtable URITable 
71         {
72             get { return _URITable; }
73         } 
74
75         internal static Context DefaultContext
76         {
77             [System.Security.SecurityCritical]  // auto-generated
78             get
79             {
80                 if (_cachedDefaultContext == null)
81                 {
82                     _cachedDefaultContext = Thread.GetDomain().GetDefaultContext();
83                 }
84                 return _cachedDefaultContext;
85             }
86         }
87
88         // NOTE!!!: This must be used to convert any uri into something that can
89         //   be used as a key in the URITable!!!
90         private static String MakeURIKey(String uri) 
91         { 
92             return Identity.RemoveAppNameOrAppGuidIfNecessary(
93                 uri.ToLower(CultureInfo.InvariantCulture)); 
94         }       
95         
96         private static String MakeURIKeyNoLower(String uri) 
97         { 
98             return Identity.RemoveAppNameOrAppGuidIfNecessary(uri); 
99         }       
100
101         internal static ReaderWriterLock TableLock 
102         {
103             get { return Thread.GetDomain().RemotingData.IDTableLock;}
104         }
105
106
107         //  Cycles through the table periodically and cleans up expired entries.
108         //
109         private static void CleanupIdentities(Object state)
110         {
111             // <
112             Contract.Assert(
113                 Thread.GetDomain().RemotingData.IDTableLock.IsWriterLockHeld,
114                 "ID Table being cleaned up without taking a lock!");
115
116             IDictionaryEnumerator e = URITable.GetEnumerator();
117             ArrayList removeList = new ArrayList();
118             while (e.MoveNext())
119             {
120                 Object o = e.Value;
121                 WeakReference wr = o as WeakReference;
122                 if ((null != wr) && (null == wr.Target))
123                 {
124                     removeList.Add(e.Key);
125                 }
126             }
127             
128             foreach (String key in removeList)
129             {
130                 URITable.Remove(key);
131             }
132         }
133
134         [System.Security.SecurityCritical]  // auto-generated
135         internal static void FlushIdentityTable()
136         {
137             // We need to guarantee that finally is not interrupted so that the lock is released.
138             // TableLock has a long path without reliability contract.  To avoid adding contract on
139             // the path, we will use ReaderWriterLock directly.
140             ReaderWriterLock rwlock = TableLock;
141             bool takeAndRelease = !rwlock.IsWriterLockHeld;
142
143             RuntimeHelpers.PrepareConstrainedRegions();
144             try{
145                 if (takeAndRelease)
146                     rwlock.AcquireWriterLock(INFINITE);
147                 CleanupIdentities(null);
148             }
149             finally{
150                 if(takeAndRelease && rwlock.IsWriterLockHeld){
151                     rwlock.ReleaseWriterLock();
152                 }
153             }
154         }    
155
156         private IdentityHolder() {          // this is a singleton object. Can't construct it.
157         }
158
159
160         //  Looks up the identity corresponding to a URI.
161         //
162         [System.Security.SecurityCritical]  // auto-generated
163         internal static Identity ResolveIdentity(String URI)
164         {
165             if (URI == null)
166                 throw new ArgumentNullException("URI");
167             Contract.EndContractBlock();
168         
169             Identity id;
170             // We need to guarantee that finally is not interrupted so that the lock is released.
171             // TableLock has a long path without reliability contract.  To avoid adding contract on
172             // the path, we will use ReaderWriterLock directly.
173             ReaderWriterLock rwlock = TableLock;
174             bool takeAndRelease = !rwlock.IsReaderLockHeld;
175
176             RuntimeHelpers.PrepareConstrainedRegions();
177             try
178             {
179                 if (takeAndRelease)
180                     rwlock.AcquireReaderLock(INFINITE);
181
182                 Message.DebugOut("ResolveIdentity:: URI: " + URI + "\n");       
183                 Message.DebugOut("ResolveIdentity:: table.count: " + URITable.Count + "\n");
184                 //Console.WriteLine("\n ResolveID: URI = " + URI);
185                 // This may be called both in the client process and the server process (loopback case).
186                 id = ResolveReference(URITable[MakeURIKey(URI)]);
187             }
188             finally
189             {
190                 if (takeAndRelease && rwlock.IsReaderLockHeld){
191                     rwlock.ReleaseReaderLock();
192                 }
193             }
194             return id;
195         } // ResolveIdentity
196
197
198         // If the identity isn't found, this version will just return
199         //   null instead of asserting (this version doesn't need to
200         //   take a lock).
201         [System.Security.SecurityCritical]  // auto-generated
202         internal static Identity CasualResolveIdentity(String uri)
203         {
204             if (uri == null)
205                 return null;
206
207             Identity id = CasualResolveReference(URITable[MakeURIKeyNoLower(uri)]);
208             if (id == null) {
209                 id = CasualResolveReference(URITable[MakeURIKey(uri)]);
210
211                 // DevDiv 720951 and 911924:
212                 // CreateWellKnownObject inserts the Identity into the URITable before
213                 // it is fully initialized.  This can cause a race condition if another
214                 // concurrent operation re-enters this code and attempts to use it. 
215                 // If we discover this situation, behave as if it is not in the URITable.
216                 // This falls into the code below to call CreateWellKnownObject again.
217                 // That method operates under a lock and will not return until it
218                 // has been fully initialized.  It will not create or initialize twice.
219                 if (id == null || id.IsInitializing)
220                 {
221                     // Check if this a well-known object which needs to be faulted in
222                     id = RemotingConfigHandler.CreateWellKnownObject(uri);                
223                 }
224             }
225
226             return id;
227         } // CasualResolveIdentity
228         
229
230         private static Identity ResolveReference(Object o)
231         {
232             Contract.Assert(
233                 TableLock.IsReaderLockHeld || TableLock.IsWriterLockHeld ,
234                 "Should have locked the ID Table!");
235             WeakReference wr = o as WeakReference;    
236             if (null != wr)
237             {
238                 return((Identity) wr.Target);
239             }
240             else
241             {
242                 return((Identity) o);
243             }
244         } // ResolveReference
245
246         private static Identity CasualResolveReference(Object o)
247         {
248             WeakReference wr = o as WeakReference;    
249             if (null != wr)
250             {
251                 return((Identity) wr.Target);
252             }
253             else
254             {
255                 return((Identity) o);
256             }
257         } // CasualResolveReference
258
259        //
260        //
261         // This is typically called when we need to create/establish
262         // an identity for a serverObject.               
263         [System.Security.SecurityCritical]  // auto-generated
264         internal static ServerIdentity FindOrCreateServerIdentity(
265             MarshalByRefObject obj,  String objURI, int flags) 
266         {
267             Message.DebugOut("Entered FindOrCreateServerIdentity \n");
268                     
269             ServerIdentity srvID = null;
270
271             bool fServer;
272             srvID = (ServerIdentity) MarshalByRefObject.GetIdentity(obj, out fServer);
273
274             if (srvID == null)
275             {
276                 // Create a new server identity and add it to the
277                 // table. IdentityHolder will take care of ----s
278                 Context serverCtx = null;
279                 
280                 if (obj is ContextBoundObject)
281                 {
282                     serverCtx = Thread.CurrentContext;
283                 }
284                 else
285                 {
286                     serverCtx = DefaultContext;
287                 }
288                 Contract.Assert(null != serverCtx, "null != serverCtx");
289
290                 ServerIdentity serverID = new ServerIdentity(obj, serverCtx);
291
292                 // Set the identity depending on whether we have the server or proxy
293                 if(fServer)
294                 {
295                     srvID = obj.__RaceSetServerIdentity(serverID);
296                     Contract.Assert(srvID == MarshalByRefObject.GetIdentity(obj), "Bad ID state!" );             
297                 }
298                 else
299                 {
300                     RealProxy rp = null;
301                     rp = RemotingServices.GetRealProxy(obj);
302                     Contract.Assert(null != rp, "null != rp");
303
304                     rp.IdentityObject = serverID;
305                     srvID = (ServerIdentity) rp.IdentityObject;
306                 }
307
308                 // DevDiv 720951 and 911924:
309                 // CreateWellKnownObject creates a ServerIdentity and places it in URITable
310                 // before it is fully initialized.  This transient flag is set to to prevent
311                 // other concurrent operations from using it.  CreateWellKnownObject is the
312                 // only code path that sets this flag, and by default it is false.
313                 if (IdOps.bIsInitializing(flags))
314                 {
315                     srvID.IsInitializing = true;
316                 }
317
318                 Message.DebugOut("Created ServerIdentity \n");
319             }
320
321 #if false
322             // Check that we are asked to create the identity for the same
323             // URI as the one already associated with the server object.
324             // It is an error to associate two URIs with the same server 
325             // object
326             // GopalK: Try eliminating the test because it is also done by GetOrCreateIdentity
327             if ((null != objURI) && (null != srvID.ObjURI))
328             {
329                 if (string.Compare(objURI, srvID.ObjURI, StringComparison.OrdinalIgnoreCase) == 0) // case-insensitive compare
330                 {
331                     Message.DebugOut("Trying to associate a URI with identity again .. throwing execption \n");
332                     throw new RemotingException(
333                         String.Format(
334                             Environment.GetResourceString(
335                                 "Remoting_ResetURI"),
336                             srvID.ObjURI, objURI));
337                 }
338             }
339 #endif
340
341             // NOTE: for purely x-context cases we never execute this ...
342             // the server ID is not put in the ID table. 
343             if ( IdOps.bStrongIdentity(flags) )
344             {
345                 // We need to guarantee that finally is not interrupted so that the lock is released.
346                 // TableLock has a long path without reliability contract.  To avoid adding contract on
347                 // the path, we will use ReaderWriterLock directly.
348                 ReaderWriterLock rwlock = TableLock;
349                 bool takeAndRelease = !rwlock.IsWriterLockHeld;
350
351                 RuntimeHelpers.PrepareConstrainedRegions();
352                 try
353                 {
354                     if (takeAndRelease)
355                         rwlock.AcquireWriterLock(INFINITE);
356
357                     // It is possible that we are marshaling out of this app-domain
358                     // for the first time. We need to do two things
359                     // (1) If there is no URI associated with the identity then go ahead 
360                     // and generate one.
361                     // (2) Add the identity to the URI -> Identity map if not already present
362                     // (For purely x-context cases we don't need the URI)   
363                     // (3) If the object ref is null, then this object hasn't been
364                     // marshalled yet.
365                     // (4) if id was created through SetObjectUriForMarshal, it would be
366                     // in the ID table
367                     if ((srvID.ObjURI == null) ||
368                        (srvID.IsInIDTable() == false))
369                     {
370                         // we are marshalling a server object, so there should not be a
371                         //   a different identity at this location.
372                         SetIdentity(srvID, objURI, DuplicateIdentityOption.Unique);
373                     }
374
375                     // If the object is marked as disconnect, mark it as connected
376                     if(srvID.IsDisconnected())
377                             srvID.SetFullyConnected();
378                 }
379                 finally
380                 {
381                     if (takeAndRelease && rwlock.IsWriterLockHeld)
382                     {
383                         rwlock.ReleaseWriterLock();
384                     }
385                 }
386             }
387
388             Message.DebugOut("Leaving FindOrCreateServerIdentity \n");
389             Contract.Assert(null != srvID,"null != srvID");
390             return srvID;                
391         }
392
393         //
394         //
395         // This is typically called when we are unmarshaling an objectref
396         // in order to create a client side identity for a remote server
397         // object.
398         [System.Security.SecurityCritical]  // auto-generated
399         internal static Identity FindOrCreateIdentity(
400             String objURI, String URL, ObjRef objectRef)
401         {
402             Identity idObj = null;
403
404             Contract.Assert(null != objURI,"null != objURI");
405
406             bool bWellKnown = (URL != null);
407
408             // Lookup the object in the identity table
409             // for well-known objects we user the URL
410             // as the hash-key (instead of just the objUri)
411             idObj = ResolveIdentity(bWellKnown ? URL : objURI);
412             if (bWellKnown &&
413                 (idObj != null) &&
414                 (idObj is ServerIdentity))
415             {
416                 // We are trying to do a connect to a server wellknown object.
417                 throw new RemotingException(
418                     String.Format(
419                         CultureInfo.CurrentCulture, Environment.GetResourceString(
420                             "Remoting_WellKnown_CantDirectlyConnect"),
421                         URL));                            
422             }
423                  
424             if (null == idObj)
425             {
426                 // There is no entry for this uri in the IdTable.
427                 Message.DebugOut("RemotingService::FindOrCreateIdentity: Creating Identity\n");
428
429                 // This identity is being encountered for the first time.
430                 // We have to do the following things
431                 // (1) Create an identity object for the proxy
432                 // (2) Add the identity to the identity table
433                 // (3) Create a proxy for the object represented by the objref      
434                 
435                 // Create a new identity
436                 // <EMAIL>GopalK:</EMAIL> Identity should get only one string that is used for everything
437                 idObj = new Identity(objURI, URL);                         
438
439                 // We need to guarantee that finally is not interrupted so that the lock is released.
440                 // TableLock has a long path without reliability contract.  To avoid adding contract on
441                 // the path, we will use ReaderWriterLock directly.
442                 ReaderWriterLock rwlock = TableLock;
443                 bool takeAndRelease = !rwlock.IsWriterLockHeld;
444
445                 RuntimeHelpers.PrepareConstrainedRegions();
446                 try
447                 {                
448                     // Add it to the identity table
449                     if (takeAndRelease)
450                         rwlock.AcquireWriterLock(INFINITE);
451
452                     // SetIdentity will give the correct Id if we raced
453                     // between the ResolveIdentity call above and now.
454                     //   (we are unmarshaling, and the server should guarantee
455                     //    that the uri is unique, so we will use an existing identity
456                     //    in case of a ----)
457                     idObj = SetIdentity(idObj, null, DuplicateIdentityOption.UseExisting);
458
459                     idObj.RaceSetObjRef(objectRef);
460                 }
461                 finally
462                 {
463                     if (takeAndRelease && rwlock.IsWriterLockHeld)
464                     {
465                         rwlock.ReleaseWriterLock();
466                     }                
467                 }
468             }
469             else
470             {
471                 Message.DebugOut("RemotingService::FindOrCreateIdentity: Found Identity!\n");
472             }
473             Contract.Assert(null != idObj,"null != idObj");
474             return idObj;                
475         }
476
477
478         //  Creates an identity entry. 
479         //  This is used by Unmarshal and Marshal to generate the URI to identity 
480         //  mapping
481         //  
482         //
483         [System.Security.SecurityCritical]  // auto-generated
484         private static Identity SetIdentity(
485             Identity idObj, String URI, DuplicateIdentityOption duplicateOption)
486         {
487             // NOTE: This function assumes that a lock has been taken 
488             // by the calling function
489             // idObj could be for a transparent proxy or a server object        
490             Message.DebugOut("SetIdentity:: domainid: " + Thread.GetDomainID() + "\n");
491             Contract.Assert(null != idObj,"null != idObj");
492             
493             // WriterLock must already be taken when SetIdentity is called!
494             Contract.Assert(
495                 TableLock.IsWriterLockHeld,
496                 "Should have write-locked the ID Table!");
497
498             // flag to denote that the id being set is a ServerIdentity
499             bool bServerIDSet = idObj is ServerIdentity;
500                 
501             if (null == idObj.URI)
502             {
503                 // No URI has been associated with this identity. It must be a 
504                 // server identity getting marshaled out of the app domain for 
505                 // the first time.
506                 Contract.Assert(bServerIDSet,"idObj should be ServerIdentity");
507
508                 // Set the URI on the idObj (generating one if needed)
509                 idObj.SetOrCreateURI(URI);
510
511                 // If objectref is non-null make sure both have same URIs
512                 // (the URI in the objectRef could have potentially been reset
513                 // in a past external call to Disconnect()
514                 if (idObj.ObjectRef != null)
515                 {
516                     idObj.ObjectRef.URI = idObj.URI;
517                 }
518                 Message.DebugOut("SetIdentity: Generated URI " + URI + " for identity");
519             }
520
521             // If we have come this far then there is no URI to identity
522             // mapping present. Go ahead and create one.
523
524             // ID should have a URI by now.
525             Contract.Assert(null != idObj.URI,"null != idObj.URI");
526
527             // See if this identity is already present in the Uri table
528             String uriKey = MakeURIKey(idObj.URI);
529             Object o = URITable[uriKey];
530
531             // flag to denote that the id found in the table is a ServerIdentity
532             bool bServerID;
533             if (null != o)
534             {
535                 // We found an identity (or a WeakRef to one) for the URI provided
536                 WeakReference wr = o as WeakReference;
537                 Identity idInTable = null;
538                 if (wr != null)
539                 {
540                     // The object we found is a weak referece to an identity
541                     
542                     // This could be an identity for a client side
543                     // proxy 
544                     // OR
545                     // a server identity which has been weakened since its life
546                     // is over.
547                     idInTable = (Identity) wr.Target;
548
549                     bServerID = idInTable is ServerIdentity;
550
551                     // If we find a weakRef for a ServerId we will be converting
552                     // it to a strong one before releasing the IdTable lock.
553                     Contract.Assert(
554                         (idInTable == null)||
555                         (!bServerID || idInTable.IsRemoteDisconnected()),
556                         "Expect to find WeakRef only for remotely disconnected ids");
557                     // We could find a weakRef to a client ID that does not 
558                     // match the idObj .. but that is a handled ---- case 
559                     // during Unmarshaling .. SetIdentity() will return the ID
560                     // from the table to the caller.
561                 }
562                 else
563                 {
564                     // We found a non-weak (strong) Identity for the URI
565                     idInTable = (Identity) o;
566                     bServerID = idInTable is ServerIdentity;
567
568                     //We dont put strong refs to client "Identity"s in the table                    
569                     Contract.Assert(
570                         bServerID, 
571                         "Found client side strong ID in the table");
572                 }
573
574                 if ((idInTable != null) && (idInTable != idObj))
575                 {
576                     // We are trying to add another identity for the same URI
577                     switch (duplicateOption)
578                     {
579                     
580                     case DuplicateIdentityOption.Unique:
581                     {
582                         
583                         String tempURI = idObj.URI;  
584
585                         // Throw an exception to indicate the error since this could
586                         // be caused by a user trying to marshal two objects with the same
587                         // URI
588                         throw new RemotingException(
589                             Environment.GetResourceString("Remoting_URIClash",
590                                 tempURI));
591                     } // case DuplicateIdentityOption.Unique
592                     
593                     case DuplicateIdentityOption.UseExisting:
594                     {
595                         // This would be a case where our thread lost the ----
596                         // we will return the one found in the table
597                         idObj = idInTable;
598                         break;
599                     } // case DuplicateIdentityOption.UseExisting:
600                     
601                     default:
602                     {
603                         Contract.Assert(false, "Invalid DuplicateIdentityOption");
604                         break;
605                     }
606                     
607                     } // switch (duplicateOption)
608                     
609                 }
610                 else
611                 if (wr!=null)
612                 {                   
613                     // We come here if we found a weakRef in the table but
614                     // the target object had been cleaned up 
615                     // OR
616                     // If there was a weakRef in the table and the target
617                     // object matches the idObj just passed in
618                     
619                     // Strengthen the entry if it a ServerIdentity.
620                     if (bServerID)
621                     {                       
622                         URITable[uriKey] = idObj;
623                     }
624                     else
625                     {
626                         // For client IDs associate the table entry
627                         // with the one passed in.
628                         // (If target was null we would set it ... 
629                         // if was non-null then it matches idObj anyway)
630                         wr.Target = idObj;  
631                     }
632                 }
633             }
634             else
635             {
636                 // We did not find an identity entry for the URI
637                 Object addMe = null;
638                 if (bServerIDSet)
639                 {
640                     addMe = idObj;
641                     ((ServerIdentity)idObj).SetHandle();
642                 }
643                 else
644                 {
645                     addMe = new WeakReference(idObj);
646                 }                    
647                 
648                 // Add the entry into the table
649                 URITable.Add(uriKey, addMe);
650                 idObj.SetInIDTable();
651                 
652                 // After every fixed number of set-id calls we run through
653                 // the table and cleanup if needed.             
654                 SetIDCount++;
655                 if (SetIDCount % CleanUpCountInterval == 0)
656                 {
657                     // This should be called with the write lock held!
658                     //   (which is why we assert that at the beginning of this
659                     //    method)
660                     CleanupIdentities(null);
661                 }
662
663             }
664             
665             Message.DebugOut("SetIdentity:: Identity::URI: " + idObj.URI + "\n");       
666             return idObj;
667         }
668
669 #if false
670          //  Convert table entry to a weak reference
671          //
672         internal static void WeakenIdentity(String URI)
673         {
674             Contract.Assert(URI!=null, "Null URI");
675             BCLDebug.Trace("REMOTE", 
676                 "IdentityHolder.WeakenIdentity ",URI, " for context ", Thread.CurrentContext);         
677             
678             String uriKey = MakeURIKey(URI);
679             // We need to guarantee that finally is not interrupted so that the lock is released.
680             // TableLock has a long path without reliability contract.  To avoid adding contract on
681             // the path, we will use ReaderWriterLock directly.
682             ReaderWriterLock rwlock = TableLock;
683             bool takeAndRelease = !rwlock.IsWriterLockHeld;
684
685             RuntimeHelpers.PrepareConstrainedRegions();
686             try
687             {
688                 if (takeAndRelease)
689                     rwlock.AcquireWriterLock(INFINITE);
690
691                 Object oRef = URITable[uriKey];
692                 WeakReference wr = oRef as WeakReference;
693                 if (null == wr)
694                 {                    
695                     // Make the id a weakRef if it isn't already.
696                     Contract.Assert(
697                        oRef != null &&  (oRef is ServerIdentity), 
698                        "Invaild URI given to WeakenIdentity");
699                        
700                     URITable[uriKey] = new WeakReference(oRef);
701                 }
702             }
703             finally
704             {
705                 if (takeAndRelase && rwlock.IsWriterLockHeld){
706                     rwlock.ReleaseWriterLock();
707                 }
708             }
709         }
710 #endif
711
712         [System.Security.SecurityCritical]  // auto-generated
713         internal static void RemoveIdentity(String uri)
714         {
715             RemoveIdentity(uri, true);
716         }
717         
718         [System.Security.SecurityCritical]  // auto-generated
719         internal static void RemoveIdentity(String uri, bool bResetURI)
720         {
721             Contract.Assert(uri!=null, "Null URI");
722             BCLDebug.Trace("REMOTE",
723                 "IdentityHolder.WeakenIdentity ",uri, " for context ", Thread.CurrentContext);
724
725             Identity id;
726             String uriKey = MakeURIKey(uri);
727             // We need to guarantee that finally is not interrupted so that the lock is released.
728             // TableLock has a long path without reliability contract.  To avoid adding contract on
729             // the path, we will use ReaderWriterLock directly.
730             ReaderWriterLock rwlock = TableLock;
731             bool takeAndRelease = !rwlock.IsWriterLockHeld;
732
733             RuntimeHelpers.PrepareConstrainedRegions();
734             try
735             {
736                 if (takeAndRelease)
737                     rwlock.AcquireWriterLock(INFINITE);
738
739                 Object oRef = URITable[uriKey];
740                 WeakReference wr = oRef as WeakReference;
741                 if (null != wr)
742                 {
743                     id = (Identity) wr.Target;
744                     wr.Target = null;
745                 }
746                 else
747                 {
748                     id = (Identity) oRef;
749                     if (id != null)
750                         ((ServerIdentity)id).ResetHandle();
751                 }
752
753                 if(id != null)
754                 {
755                     URITable.Remove(uriKey);
756                     // Mark the ID as not present in the ID Table
757                     // This will clear its URI & objRef fields
758                     id.ResetInIDTable(bResetURI);
759                 }
760             }
761             finally
762             {
763                 if (takeAndRelease && rwlock.IsWriterLockHeld){
764                     rwlock.ReleaseWriterLock();
765                 }
766             }
767         } // RemoveIdentity
768
769
770         // Support for dynamically registered property sinks
771         [System.Security.SecurityCritical]  // auto-generated
772         internal static bool AddDynamicProperty(MarshalByRefObject obj, IDynamicProperty prop)
773         {
774             if (RemotingServices.IsObjectOutOfContext(obj))
775             {
776                 // We have to add a proxy side property, get the identity
777                 RealProxy rp = RemotingServices.GetRealProxy(obj);
778                 return rp.IdentityObject.AddProxySideDynamicProperty(prop);            
779             }
780             else
781             {
782                 MarshalByRefObject realObj = 
783                     (MarshalByRefObject)
784                         RemotingServices.AlwaysUnwrap((ContextBoundObject)obj);
785                 // This is a real object. See if we have an identity for it
786                 ServerIdentity srvID = (ServerIdentity)MarshalByRefObject.GetIdentity(realObj);
787                 if (srvID != null)
788                 {
789                     return srvID.AddServerSideDynamicProperty(prop);
790                 }
791                 else
792                 {
793                     // identity not found, we can't set a sink for this object.
794                     throw new RemotingException(
795                        Environment.GetResourceString("Remoting_NoIdentityEntry"));
796
797                 }                        
798             }
799         }
800
801         [System.Security.SecurityCritical]  // auto-generated
802         internal static bool RemoveDynamicProperty(MarshalByRefObject obj, String name)
803         {
804             if (RemotingServices.IsObjectOutOfContext(obj))
805             {
806                 // We have to add a proxy side property, get the identity
807                 RealProxy rp = RemotingServices.GetRealProxy(obj);
808                 return rp.IdentityObject.RemoveProxySideDynamicProperty(name);            
809             }
810             else
811             {
812
813                 MarshalByRefObject realObj = 
814                     (MarshalByRefObject)
815                         RemotingServices.AlwaysUnwrap((ContextBoundObject)obj);
816                         
817                 // This is a real object. See if we have an identity for it
818                 ServerIdentity srvID = (ServerIdentity)MarshalByRefObject.GetIdentity(realObj);
819                 if (srvID != null)
820                 {
821                     return srvID.RemoveServerSideDynamicProperty(name);
822                 }
823                 else
824                 {
825                     // identity not found, we can't set a sink for this object.
826                     throw new RemotingException(
827                        Environment.GetResourceString("Remoting_NoIdentityEntry"));
828                 }
829             }
830         }
831     } // class IdentityHolder
832
833 }