3 // Copyright (c) Microsoft Corporation. All rights reserved.
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;
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.
21 using System.Collections;
22 using System.Diagnostics.Contracts;
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);)
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
35 internal static bool bStrongIdentity(int flags)
37 return (flags&StrongIdentity)!=0;
40 internal static bool bIsInitializing(int flags)
42 return (flags & IsInitializing) != 0;
46 // Internal enum to specify options for SetIdentity
48 internal enum DuplicateIdentityOption
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
56 internal sealed class IdentityHolder
58 // private static Timer CleanupTimer = null;
59 // private const int CleanupInterval = 60000; // 1 minute.
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;
66 private static Hashtable _URITable = new Hashtable();
67 private static volatile Context _cachedDefaultContext = null;
70 internal static Hashtable URITable
72 get { return _URITable; }
75 internal static Context DefaultContext
77 [System.Security.SecurityCritical] // auto-generated
80 if (_cachedDefaultContext == null)
82 _cachedDefaultContext = Thread.GetDomain().GetDefaultContext();
84 return _cachedDefaultContext;
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)
92 return Identity.RemoveAppNameOrAppGuidIfNecessary(
93 uri.ToLower(CultureInfo.InvariantCulture));
96 private static String MakeURIKeyNoLower(String uri)
98 return Identity.RemoveAppNameOrAppGuidIfNecessary(uri);
101 internal static ReaderWriterLock TableLock
103 get { return Thread.GetDomain().RemotingData.IDTableLock;}
107 // Cycles through the table periodically and cleans up expired entries.
109 private static void CleanupIdentities(Object state)
113 Thread.GetDomain().RemotingData.IDTableLock.IsWriterLockHeld,
114 "ID Table being cleaned up without taking a lock!");
116 IDictionaryEnumerator e = URITable.GetEnumerator();
117 ArrayList removeList = new ArrayList();
121 WeakReference wr = o as WeakReference;
122 if ((null != wr) && (null == wr.Target))
124 removeList.Add(e.Key);
128 foreach (String key in removeList)
130 URITable.Remove(key);
134 [System.Security.SecurityCritical] // auto-generated
135 internal static void FlushIdentityTable()
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;
143 RuntimeHelpers.PrepareConstrainedRegions();
146 rwlock.AcquireWriterLock(INFINITE);
147 CleanupIdentities(null);
150 if(takeAndRelease && rwlock.IsWriterLockHeld){
151 rwlock.ReleaseWriterLock();
156 private IdentityHolder() { // this is a singleton object. Can't construct it.
160 // Looks up the identity corresponding to a URI.
162 [System.Security.SecurityCritical] // auto-generated
163 internal static Identity ResolveIdentity(String URI)
166 throw new ArgumentNullException("URI");
167 Contract.EndContractBlock();
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;
176 RuntimeHelpers.PrepareConstrainedRegions();
180 rwlock.AcquireReaderLock(INFINITE);
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)]);
190 if (takeAndRelease && rwlock.IsReaderLockHeld){
191 rwlock.ReleaseReaderLock();
198 // If the identity isn't found, this version will just return
199 // null instead of asserting (this version doesn't need to
201 [System.Security.SecurityCritical] // auto-generated
202 internal static Identity CasualResolveIdentity(String uri)
207 Identity id = CasualResolveReference(URITable[MakeURIKeyNoLower(uri)]);
209 id = CasualResolveReference(URITable[MakeURIKey(uri)]);
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)
221 // Check if this a well-known object which needs to be faulted in
222 id = RemotingConfigHandler.CreateWellKnownObject(uri);
227 } // CasualResolveIdentity
230 private static Identity ResolveReference(Object o)
233 TableLock.IsReaderLockHeld || TableLock.IsWriterLockHeld ,
234 "Should have locked the ID Table!");
235 WeakReference wr = o as WeakReference;
238 return((Identity) wr.Target);
242 return((Identity) o);
244 } // ResolveReference
246 private static Identity CasualResolveReference(Object o)
248 WeakReference wr = o as WeakReference;
251 return((Identity) wr.Target);
255 return((Identity) o);
257 } // CasualResolveReference
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)
267 Message.DebugOut("Entered FindOrCreateServerIdentity \n");
269 ServerIdentity srvID = null;
272 srvID = (ServerIdentity) MarshalByRefObject.GetIdentity(obj, out fServer);
276 // Create a new server identity and add it to the
277 // table. IdentityHolder will take care of ----s
278 Context serverCtx = null;
280 if (obj is ContextBoundObject)
282 serverCtx = Thread.CurrentContext;
286 serverCtx = DefaultContext;
288 Contract.Assert(null != serverCtx, "null != serverCtx");
290 ServerIdentity serverID = new ServerIdentity(obj, serverCtx);
292 // Set the identity depending on whether we have the server or proxy
295 srvID = obj.__RaceSetServerIdentity(serverID);
296 Contract.Assert(srvID == MarshalByRefObject.GetIdentity(obj), "Bad ID state!" );
301 rp = RemotingServices.GetRealProxy(obj);
302 Contract.Assert(null != rp, "null != rp");
304 rp.IdentityObject = serverID;
305 srvID = (ServerIdentity) rp.IdentityObject;
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))
315 srvID.IsInitializing = true;
318 Message.DebugOut("Created ServerIdentity \n");
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
326 // GopalK: Try eliminating the test because it is also done by GetOrCreateIdentity
327 if ((null != objURI) && (null != srvID.ObjURI))
329 if (string.Compare(objURI, srvID.ObjURI, StringComparison.OrdinalIgnoreCase) == 0) // case-insensitive compare
331 Message.DebugOut("Trying to associate a URI with identity again .. throwing execption \n");
332 throw new RemotingException(
334 Environment.GetResourceString(
335 "Remoting_ResetURI"),
336 srvID.ObjURI, objURI));
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) )
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;
351 RuntimeHelpers.PrepareConstrainedRegions();
355 rwlock.AcquireWriterLock(INFINITE);
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
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
365 // (4) if id was created through SetObjectUriForMarshal, it would be
367 if ((srvID.ObjURI == null) ||
368 (srvID.IsInIDTable() == false))
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);
375 // If the object is marked as disconnect, mark it as connected
376 if(srvID.IsDisconnected())
377 srvID.SetFullyConnected();
381 if (takeAndRelease && rwlock.IsWriterLockHeld)
383 rwlock.ReleaseWriterLock();
388 Message.DebugOut("Leaving FindOrCreateServerIdentity \n");
389 Contract.Assert(null != srvID,"null != srvID");
395 // This is typically called when we are unmarshaling an objectref
396 // in order to create a client side identity for a remote server
398 [System.Security.SecurityCritical] // auto-generated
399 internal static Identity FindOrCreateIdentity(
400 String objURI, String URL, ObjRef objectRef)
402 Identity idObj = null;
404 Contract.Assert(null != objURI,"null != objURI");
406 bool bWellKnown = (URL != null);
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);
414 (idObj is ServerIdentity))
416 // We are trying to do a connect to a server wellknown object.
417 throw new RemotingException(
419 CultureInfo.CurrentCulture, Environment.GetResourceString(
420 "Remoting_WellKnown_CantDirectlyConnect"),
426 // There is no entry for this uri in the IdTable.
427 Message.DebugOut("RemotingService::FindOrCreateIdentity: Creating Identity\n");
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
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);
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;
445 RuntimeHelpers.PrepareConstrainedRegions();
448 // Add it to the identity table
450 rwlock.AcquireWriterLock(INFINITE);
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);
459 idObj.RaceSetObjRef(objectRef);
463 if (takeAndRelease && rwlock.IsWriterLockHeld)
465 rwlock.ReleaseWriterLock();
471 Message.DebugOut("RemotingService::FindOrCreateIdentity: Found Identity!\n");
473 Contract.Assert(null != idObj,"null != idObj");
478 // Creates an identity entry.
479 // This is used by Unmarshal and Marshal to generate the URI to identity
483 [System.Security.SecurityCritical] // auto-generated
484 private static Identity SetIdentity(
485 Identity idObj, String URI, DuplicateIdentityOption duplicateOption)
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");
493 // WriterLock must already be taken when SetIdentity is called!
495 TableLock.IsWriterLockHeld,
496 "Should have write-locked the ID Table!");
498 // flag to denote that the id being set is a ServerIdentity
499 bool bServerIDSet = idObj is ServerIdentity;
501 if (null == idObj.URI)
503 // No URI has been associated with this identity. It must be a
504 // server identity getting marshaled out of the app domain for
506 Contract.Assert(bServerIDSet,"idObj should be ServerIdentity");
508 // Set the URI on the idObj (generating one if needed)
509 idObj.SetOrCreateURI(URI);
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)
516 idObj.ObjectRef.URI = idObj.URI;
518 Message.DebugOut("SetIdentity: Generated URI " + URI + " for identity");
521 // If we have come this far then there is no URI to identity
522 // mapping present. Go ahead and create one.
524 // ID should have a URI by now.
525 Contract.Assert(null != idObj.URI,"null != idObj.URI");
527 // See if this identity is already present in the Uri table
528 String uriKey = MakeURIKey(idObj.URI);
529 Object o = URITable[uriKey];
531 // flag to denote that the id found in the table is a ServerIdentity
535 // We found an identity (or a WeakRef to one) for the URI provided
536 WeakReference wr = o as WeakReference;
537 Identity idInTable = null;
540 // The object we found is a weak referece to an identity
542 // This could be an identity for a client side
545 // a server identity which has been weakened since its life
547 idInTable = (Identity) wr.Target;
549 bServerID = idInTable is ServerIdentity;
551 // If we find a weakRef for a ServerId we will be converting
552 // it to a strong one before releasing the IdTable lock.
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.
564 // We found a non-weak (strong) Identity for the URI
565 idInTable = (Identity) o;
566 bServerID = idInTable is ServerIdentity;
568 //We dont put strong refs to client "Identity"s in the table
571 "Found client side strong ID in the table");
574 if ((idInTable != null) && (idInTable != idObj))
576 // We are trying to add another identity for the same URI
577 switch (duplicateOption)
580 case DuplicateIdentityOption.Unique:
583 String tempURI = idObj.URI;
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
588 throw new RemotingException(
589 Environment.GetResourceString("Remoting_URIClash",
591 } // case DuplicateIdentityOption.Unique
593 case DuplicateIdentityOption.UseExisting:
595 // This would be a case where our thread lost the ----
596 // we will return the one found in the table
599 } // case DuplicateIdentityOption.UseExisting:
603 Contract.Assert(false, "Invalid DuplicateIdentityOption");
607 } // switch (duplicateOption)
613 // We come here if we found a weakRef in the table but
614 // the target object had been cleaned up
616 // If there was a weakRef in the table and the target
617 // object matches the idObj just passed in
619 // Strengthen the entry if it a ServerIdentity.
622 URITable[uriKey] = idObj;
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)
636 // We did not find an identity entry for the URI
641 ((ServerIdentity)idObj).SetHandle();
645 addMe = new WeakReference(idObj);
648 // Add the entry into the table
649 URITable.Add(uriKey, addMe);
650 idObj.SetInIDTable();
652 // After every fixed number of set-id calls we run through
653 // the table and cleanup if needed.
655 if (SetIDCount % CleanUpCountInterval == 0)
657 // This should be called with the write lock held!
658 // (which is why we assert that at the beginning of this
660 CleanupIdentities(null);
665 Message.DebugOut("SetIdentity:: Identity::URI: " + idObj.URI + "\n");
670 // Convert table entry to a weak reference
672 internal static void WeakenIdentity(String URI)
674 Contract.Assert(URI!=null, "Null URI");
675 BCLDebug.Trace("REMOTE",
676 "IdentityHolder.WeakenIdentity ",URI, " for context ", Thread.CurrentContext);
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;
685 RuntimeHelpers.PrepareConstrainedRegions();
689 rwlock.AcquireWriterLock(INFINITE);
691 Object oRef = URITable[uriKey];
692 WeakReference wr = oRef as WeakReference;
695 // Make the id a weakRef if it isn't already.
697 oRef != null && (oRef is ServerIdentity),
698 "Invaild URI given to WeakenIdentity");
700 URITable[uriKey] = new WeakReference(oRef);
705 if (takeAndRelase && rwlock.IsWriterLockHeld){
706 rwlock.ReleaseWriterLock();
712 [System.Security.SecurityCritical] // auto-generated
713 internal static void RemoveIdentity(String uri)
715 RemoveIdentity(uri, true);
718 [System.Security.SecurityCritical] // auto-generated
719 internal static void RemoveIdentity(String uri, bool bResetURI)
721 Contract.Assert(uri!=null, "Null URI");
722 BCLDebug.Trace("REMOTE",
723 "IdentityHolder.WeakenIdentity ",uri, " for context ", Thread.CurrentContext);
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;
733 RuntimeHelpers.PrepareConstrainedRegions();
737 rwlock.AcquireWriterLock(INFINITE);
739 Object oRef = URITable[uriKey];
740 WeakReference wr = oRef as WeakReference;
743 id = (Identity) wr.Target;
748 id = (Identity) oRef;
750 ((ServerIdentity)id).ResetHandle();
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);
763 if (takeAndRelease && rwlock.IsWriterLockHeld){
764 rwlock.ReleaseWriterLock();
770 // Support for dynamically registered property sinks
771 [System.Security.SecurityCritical] // auto-generated
772 internal static bool AddDynamicProperty(MarshalByRefObject obj, IDynamicProperty prop)
774 if (RemotingServices.IsObjectOutOfContext(obj))
776 // We have to add a proxy side property, get the identity
777 RealProxy rp = RemotingServices.GetRealProxy(obj);
778 return rp.IdentityObject.AddProxySideDynamicProperty(prop);
782 MarshalByRefObject realObj =
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);
789 return srvID.AddServerSideDynamicProperty(prop);
793 // identity not found, we can't set a sink for this object.
794 throw new RemotingException(
795 Environment.GetResourceString("Remoting_NoIdentityEntry"));
801 [System.Security.SecurityCritical] // auto-generated
802 internal static bool RemoveDynamicProperty(MarshalByRefObject obj, String name)
804 if (RemotingServices.IsObjectOutOfContext(obj))
806 // We have to add a proxy side property, get the identity
807 RealProxy rp = RemotingServices.GetRealProxy(obj);
808 return rp.IdentityObject.RemoveProxySideDynamicProperty(name);
813 MarshalByRefObject realObj =
815 RemotingServices.AlwaysUnwrap((ContextBoundObject)obj);
817 // This is a real object. See if we have an identity for it
818 ServerIdentity srvID = (ServerIdentity)MarshalByRefObject.GetIdentity(realObj);
821 return srvID.RemoveServerSideDynamicProperty(name);
825 // identity not found, we can't set a sink for this object.
826 throw new RemotingException(
827 Environment.GetResourceString("Remoting_NoIdentityEntry"));
831 } // class IdentityHolder