AppDomain and MarshalByRefObject Lifetime: How to Avoid RemotingException?

When a MarshalByRef object is passed from AppDomain (1) to another (2), if you wait 6 minutes before calling the method on it in the second AppDomain (2), you will get a RemotingException:

System.Runtime.Remoting.RemotingException: Object [...] is disabled or does not exist on the server.

Some documentation about this isse:

Correct me if I am wrong: if InitializeLifetimeService returns null, the object can only be assembled in AppDomain 1, when will AppDomain 2 be unloaded, even if a proxy server was received?

Is there a way to disable the lifetime and save the proxy (in AppDomain 2) and the object (in AppDomain1) until the proxy is complete? Maybe with ISponsor ...?

+55
c # remoting appdomain object-lifetime
Mar 09
source share
9 answers

see here:

http://social.msdn.microsoft.com/Forums/en-US/netfxremoting/thread/3ab17b40-546f-4373-8c08-f0f072d818c9/

which basically says:

[SecurityPermissionAttribute(SecurityAction.Demand, Flags = SecurityPermissionFlag.Infrastructure)] public override object InitializeLifetimeService() { return null; } 
+41
May 24 '11 at 13:21
source share

I finally found a way to activate client instances, but it includes managed code in Finalizer :( I specialized my class for communicating with CrossAppDomain, but you can change it and try in other remote operations. Let me know if you find an error .

The following two classes must be in the assembly, loaded in all involved areas of the application.

  /// <summary> /// Stores all relevant information required to generate a proxy in order to communicate with a remote object. /// Disconnects the remote object (server) when finalized on local host (client). /// </summary> [Serializable] [EditorBrowsable(EditorBrowsableState.Never)] public sealed class CrossAppDomainObjRef : ObjRef { /// <summary> /// Initializes a new instance of the CrossAppDomainObjRef class to /// reference a specified CrossAppDomainObject of a specified System.Type. /// </summary> /// <param name="instance">The object that the new System.Runtime.Remoting.ObjRef instance will reference.</param> /// <param name="requestedType"></param> public CrossAppDomainObjRef(CrossAppDomainObject instance, Type requestedType) : base(instance, requestedType) { //Proxy created locally (not remoted), the finalizer is meaningless. GC.SuppressFinalize(this); } /// <summary> /// Initializes a new instance of the System.Runtime.Remoting.ObjRef class from /// serialized data. /// </summary> /// <param name="info">The object that holds the serialized object data.</param> /// <param name="context">The contextual information about the source or destination of the exception.</param> private CrossAppDomainObjRef(SerializationInfo info, StreamingContext context) : base(info, context) { Debug.Assert(context.State == StreamingContextStates.CrossAppDomain); Debug.Assert(IsFromThisProcess()); Debug.Assert(IsFromThisAppDomain() == false); //Increment ref counter CrossAppDomainObject remoteObject = (CrossAppDomainObject)GetRealObject(new StreamingContext(StreamingContextStates.CrossAppDomain)); remoteObject.AppDomainConnect(); } /// <summary> /// Disconnects the remote object. /// </summary> ~CrossAppDomainObjRef() { Debug.Assert(IsFromThisProcess()); Debug.Assert(IsFromThisAppDomain() == false); //Decrement ref counter CrossAppDomainObject remoteObject = (CrossAppDomainObject)GetRealObject(new StreamingContext(StreamingContextStates.CrossAppDomain)); remoteObject.AppDomainDisconnect(); } /// <summary> /// Populates a specified System.Runtime.Serialization.SerializationInfo with /// the data needed to serialize the current System.Runtime.Remoting.ObjRef instance. /// </summary> /// <param name="info">The System.Runtime.Serialization.SerializationInfo to populate with data.</param> /// <param name="context">The contextual information about the source or destination of the serialization.</param> public override void GetObjectData(SerializationInfo info, StreamingContext context) { Debug.Assert(context.State == StreamingContextStates.CrossAppDomain); base.GetObjectData(info, context); info.SetType(typeof(CrossAppDomainObjRef)); } } 

And now with CrossAppDomainObject, your remote object should inherit from this class instead of MarshalByRefObject.

  /// <summary> /// Enables access to objects across application domain boundaries. /// Contrary to MarshalByRefObject, the lifetime is managed by the client. /// </summary> public abstract class CrossAppDomainObject : MarshalByRefObject { /// <summary> /// Count of remote references to this object. /// </summary> [NonSerialized] private int refCount; /// <summary> /// Creates an object that contains all the relevant information required to /// generate a proxy used to communicate with a remote object. /// </summary> /// <param name="requestedType">The System.Type of the object that the new System.Runtime.Remoting.ObjRef will reference.</param> /// <returns>Information required to generate a proxy.</returns> [EditorBrowsable(EditorBrowsableState.Never)] public sealed override ObjRef CreateObjRef(Type requestedType) { CrossAppDomainObjRef objRef = new CrossAppDomainObjRef(this, requestedType); return objRef; } /// <summary> /// Disables LifeTime service : object has an infinite life time until it Disconnected. /// </summary> /// <returns>null.</returns> [EditorBrowsable(EditorBrowsableState.Never)] public sealed override object InitializeLifetimeService() { return null; } /// <summary> /// Connect a proxy to the object. /// </summary> [EditorBrowsable(EditorBrowsableState.Never)] public void AppDomainConnect() { int value = Interlocked.Increment(ref refCount); Debug.Assert(value > 0); } /// <summary> /// Disconnects a proxy from the object. /// When all proxy are disconnected, the object is disconnected from RemotingServices. /// </summary> [EditorBrowsable(EditorBrowsableState.Never)] public void AppDomainDisconnect() { Debug.Assert(refCount > 0); if (Interlocked.Decrement(ref refCount) == 0) RemotingServices.Disconnect(this); } } 
+13
Mar 11 '10 at 8:19
source share

Unfortunately, this solution is incorrect when AppDomains are used for plugin purposes (the plugin assembly should not be uploaded to your main application domain).

Calling GetRealObject () in your constructor and destructor results in a real type of the remote object, which leads to an attempt to load the assembly of the remote object into the current AppDomain. This can cause either an exception (if the assembly cannot be loaded) or an undesirable effect that you loaded with someone else's assembly, which you cannot unload later.

A better solution would be if you register your remote objects in your main AppDomain application using the ClientSponsor.Register () method (not static to create a client sponsor instance). By default, it will update your remote proxies every 2 minutes, which is enough if your objects have a default lifetime of 5 minutes.

+6
Apr 16 '10 at 16:17
source share

I created a class that disconnects when destroyed.

 public class MarshalByRefObjectPermanent : MarshalByRefObject { public override object InitializeLifetimeService() { return null; } ~MarshalByRefObjectPermanent() { RemotingServices.Disconnect(this); } } 
+1
Feb 24 '16 at 3:28
source share

You can try a serializable single ISponsor object that implements IObjectReference. The implementation of GetRealObject (from IObjectReference should return MySponsor.Instance when context.State is CrossAppDomain, otherwise returns itself. MySponsor.Instance is a self-initialized synchronized (MethodImplOptions.Synchronized) singleton. Update implementation (from ISponsor) should check for staticIsnload and returns TimeSpan.Zero when marked for unloading /AppDomain.Current.IsFinalizingForUnload () or returns LifetimeServices.RenewOnCallTime otherwise.

To attach it, just get ILease and Register (MySponsor.Instance), which will be converted to MySponsor.Instance installed in AppDomain due to the implementation of GetRealObject.

To stop sponsoring, re-receive ILease and Unregister (MySponsor.Instance), then install MySponsor.IsFlaggedForUnload via the cross-AppDomain callback (myPluginAppDomain.DoCallback (MySponsor.FlagForUnload)).

This should support your object in another AppDomain until either an unregistered call, or a FlagForUnload call, or AppDomain is canceled.

0
Jun 09 '14 at 20:56 on
source share
 [SecurityPermissionAttribute(SecurityAction.Demand, Flags = SecurityPermissionFlag.Infrastructure)] public override object InitializeLifetimeService() { return null; } 

I tested this and worked great, of course, you need to know that the proxy server lives forever until you do GC-ing for yourself. But in my case, using the -w770 plugin connected to my main application, there is no memory leak or anything like that. I just made sure that I implement IDisposable and works fine (I can say because my loaded dll (in the factory) can be overwritten as soon as the factory is located correctly)

Edit: if your events with bubbles through domains add this line of code to the class creating the proxy server, otherwise your bubble will also throw;)

0
Oct 13 '15 at 9:59
source share

If you want to recreate the remote object after garbage collection without creating the ISponsor class and not give it an infinite lifetime, you can call the dummy function of the remote object while catching a RemotingException .

 public static class MyClientClass { private static MarshalByRefObject remoteClass; static MyClientClass() { CreateRemoteInstance(); } // ... public static void DoStuff() { // Before doing stuff, check if the remote object is still reachable try { remoteClass.GetLifetimeService(); } catch(RemotingException) { CreateRemoteInstance(); // Re-create remote instance } // Now we are sure the remote class is reachable // Do actual stuff ... } private static void CreateRemoteInstance() { remoteClass = (MarshalByRefObject)AppDomain.CurrentAppDomain.CreateInstanceFromAndUnwrap(remoteClassPath, typeof(MarshalByRefObject).FullName); } } 
0
08 Oct '16 at 10:50
source share

There are two possible solutions.

Singleton Approach: Override InitializeLifetimeService

As Sasha Goldstein points out in a blog post referenced in the original poster, if your Marshaled object has Singleton semantics, you can override InitializeLifetimeService :

 class MyMarshaledObject : MarshalByRefObject { public bool DoSomethingRemote() { // ... execute some code remotely ... return true; } [SecurityPermissionAttribute(SecurityAction.Demand, Flags = SecurityPermissionFlag.Infrastructure)] public override object InitializeLifetimeService() { return null; } } 

However, as user 266648 points in another answer

this solution would not work if such an object was created every time the client connects itself, because it will never be GCed, and your memory consumption will increase until you stop your server or it crashes, because he has no more memory

Class Approach: Using ClientSponsor

A more general solution is to use ClientSponsor to extend the life of the remote object activated by the class. The related MSDN article has a useful starter example that you can follow:

 using System; using System.Runtime.Remoting; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Tcp; using System.Runtime.Remoting.Lifetime; namespace RemotingSamples { class HelloClient { static void Main() { // Register a channel. TcpChannel myChannel = new TcpChannel (); ChannelServices.RegisterChannel(myChannel); RemotingConfiguration.RegisterActivatedClientType( typeof(HelloService),"tcp://localhost:8085/"); // Get the remote object. HelloService myService = new HelloService(); // Get a sponsor for renewal of time. ClientSponsor mySponsor = new ClientSponsor(); // Register the service with sponsor. mySponsor.Register(myService); // Set renewaltime. mySponsor.RenewalTime = TimeSpan.FromMinutes(2); // Renew the lease. ILease myLease = (ILease)mySponsor.InitializeLifetimeService(); TimeSpan myTime = mySponsor.Renewal(myLease); Console.WriteLine("Renewed time in minutes is " + myTime.Minutes.ToString()); // Call the remote method. Console.WriteLine(myService.HelloMethod("World")); // Unregister the channel. mySponsor.Unregister(myService); mySponsor.Close(); } } } 

It costs nothing about how life-time management works in the Remoting API, which is pretty well described here on MSDN . I quoted the part that seemed most useful to me:

The Remote Connectivity service associates a lease with each service and deletes the service when its lease expires. Lifetime service can take on the function of a traditional distributed garbage collector, and is also well-tuned with an increase in the number of clients per server.

Each application domain contains a rental manager who is responsible for managing the rental in his domain. All leases are periodically reviewed for expired leases. If the lease expires, one or more lease sponsors are called and an opportunity to extend the lease is provided. If none of the sponsors decides to extend the lease, the rental manager rents the lease, and the object can be collected by the garbage collector. The rental manager maintains a rental list with the rental sorted by the remaining rental time. Leases with the least remaining time are saved at the top of the list. The Remote Connectivity service associates a lease with each service and deletes the service when its lease expires.

0
Jan 27 '19 at 17:29
source share

I recently came across this exception. Right now, my solution just unloads AppDomain and then reloads AppDomain after a long interval. Fortunately, this workaround works for my case. I'm sorry there is a more elegant way to handle this.

-2
Jan 24 '11 at 18:32
source share



All Articles