Runtime support for Remoting ============================ The runtime supports a special objects called "TransparentProxy". You can create objects of this type by calling GetTransparentProxy() on a "RealProxy" object. LDFLD/STFLD for transparent proxies =================================== Access to fields must be redirected to the remote object. System.Object has some special methods for that: void FieldGetter (string typeName, string fieldName, ref object val); void FieldSetter (string typeName, string fieldName, object val); This methods are never called on actual object. The are only used to pack LDFLD/STFLD operations into method call messages, which are then passed to the RealProxy::Invoke() method. There are two helper methods which can be used by the JIT and the interpreter to convert LDFLD/STFLD operations into messages and then call RealProxy::Invoke(): mono_store_remote_field() and mono_load_remote_field(). Cross app domain optimizations ============================== The new implementation of the cross app domain channel makes a minimal use of the remoting infrastructure. The idea is to create remoting wrappers specific for cross app domain calls, which take the input paramers, switch the domain and dispatch the call in the new domain. When an vtable for a proxy needs to be created, the runtime checks if the proxy is referencing an object that belongs to another domain in the same process. In such case, the fast xdomain wrapper is returned instead of the regular one. The xdomain wrapper will have a different structure depending on the signature of the method it wraps, since different types have different marshalling needs. There are four types of marshalling, the first one is the fastest, the last one is the slowest: 1) No marshalling at all: this is for primitive types. 2) Internal copy of the object in the new domain: some system types can be copied from one domain to the other by the runtime. This currently applies to arrays of primitive types (or arrays of values that can be internally copied), String and StringBuilder. We can add more types in the future. 3) Internal copy for Out parameters. It is a specific case of the previous type, when an input parameter has the [Out] attribute, which means that the content of the object that is marshalled into the new domain, needs to be copied over the instance of the original object. This applies to arrays of primitive types and StringBuilder. This is used, for example, to be able to call methods such as Stream.Read ([Out]buffer, pos, lengh) across domains. 4) Serialization. The value is serialized in one domain and deserialized in the other one. The xdomain wrapper will be generated according to the marshalling needs of each parameter. The cross domain wrapper is divided in two methods. The first method (the wrapper itself) takes the input parameters and serializes those that need to be serialized. After that, sets the new domain and calls to a second method in the new domain, which deserializes the parameters, makes a local copy of those that don't need serialization, and dispatches the call to the real object. Then, the inverse sequence is followed: return values are serialized, flow returns to the first method, which changes the domain again and deserializes the values. Sample wrapper -------------- This are examples of cross domain wrappers in pseudo-C# code. The first example is for a method with the following signature: ArrayList Test (int a, string b, ArrayList c, ref ArrayList d, ref string e, ref int f) Of course, the wrapper has the same signature: ArrayList Test_xdomain_invoke (int a, string b, ArrayList c, ref ArrayList d, ref string e, ref int f) { int loc_new_domainid, loc_old_domainid; ArrayList loc_return; byte[] loc_serialized_array; // Save thread domain data Context loc_context = Thread.CurrentContext; if (loc_context.IsDefaultContext) { return Test_remoting_invoke (a, b, c, ref d, ref e, ref f); } object loc_datastore = Thread.ResetDataStoreStatus (); // Create the array that will hold the parameters to be serialized object[] loc_array = new object [3]; // +1 to store the return value loc_array [0] = c; loc_array [1] = d; // Serialize parameters loc_serialized_array = RemotingServices.SerializeCallData (loc_Array); // Get the target domain id and change the domain RealProxy loc_real_proxy = ((TransparentProxy)this).rp; loc_new_domainid = loc_real_proxy->target_domain_id; loc_old_domainid = mono_remoting_set_domain_by_id (loc_new_domainid); string e_copy = e; /* The following is an indirect call made into the target domain */ Test_xdomain_dispatch (rp, ref loc_serialized_array, out loc_serialized_exc, a, b, ref e_copy, ref f); // Switch context mono_remoting_set_domain_by_id (loc_old_domainid); // Restore thread domain data mono_context_set (loc_context); Thread.RestoreDataStoreStatus (loc_datastore); if (loc_serialized_exc != null) { Exception ex = (Exception) RemotingServices.DeserializeCallData (loc_serialized_exc); ex.FixRemotingException (); throw ex; } // copy back non-serialized output parametars e = mono_marshal_xdomain_copy_value (e_copy); // Deserialize out parameters loc_serialized_array = mono_marshal_xdomain_copy_value (loc_serialized_array); loc_array = RemotingServices.DeserializeObject (loc_serialized_array); d = loc_array [1]; mono_thread_force_interruption_checkpoint (); return loc_array [2]; } void Test_xdomain_dispatch (RealProxy rp, ref byte[] loc_call_data, out byte[] loc_exc_data, int a, string b, ref string e, ref int f) { // Deserialize parameters try { // Clean the call context CallContext.SetCurrentCallContext (null); // Deserialize call data if (loc_call_data != null) { loc_call_data = mono_marshal_xdomain_copy_value (loc_call_data); loc_array = RemotingServices.DeserializeCallData (loc_call_data); } // Get the target object object target = rp.GetAppDomainTarget (); // Load the arguments b = mono_marshal_xdomain_copy_value (b); // Make the call to the real object mono_thread_force_interruption_checkpoint (); loc_return = target.Test (a, b, loc_array[0], ref loc_array[1], ref e, ref f); // Serialize the return values // Reset parameters in the array that don't need to be serialized back loc_array [0] = null; // Add the return value to the array loc_array [2] = loc_return; // Serialize loc_call_data = RemotingServices.SerializeCallData (loc_array); loc_exc_data = null; } catch (Exception ex) { loc_exc_data = RemotingServices.SerializeExceptionData (ex); } } Another example --------------- This is another example of a method with more simple parameters: int SimpleTest_xdomain_invoke (int a) { int loc_new_domainid, loc_old_domainid; int loc_return; byte[] loc_serialized_array; // Save thread domain data Context loc_context = Thread.CurrentContext; if (loc_context.IsDefaultContext) { return SimpleTest_remoting_invoke (a, b, c, ref d, ref e, ref f); } object loc_datastore = Thread.ResetDataStoreStatus (); // Serialize parameters. This will only serialize LogicalContext data if needed. loc_serialized_array = RemotingServices.SerializeCallData (null); // Get the target domain id and change the domain RealProxy loc_real_proxy = ((TransparentProxy)this).rp; loc_new_domainid = loc_real_proxy->target_domain_id; loc_old_domainid = mono_remoting_set_domain_by_id (loc_new_domainid); /* The following is an indirect call made into the target domain */ loc_return = SimpleTest_xdomain_dispatch (rp, ref loc_serialized_array, out loc_serialized_exc, a); // Switch domain mono_remoting_set_domain_by_id (loc_old_domainid); // Restore thread domain data mono_context_set (loc_context); Thread.RestoreDataStoreStatus (loc_datastore); if (loc_serialized_exc != null) { Exception ex = (Exception) RemotingServices.DeserializeCallData (loc_serialized_exc); ex.FixRemotingException (); throw ex; } RemotingServices.DeserializeCallData (loc_serialized_array); return loc_return [2]; } int SimpleTest_xdomain_dispatch (RealProxy rp, ref byte[] loc_call_data, out byte[] loc_exc_data, int a) { int loc_return; // Deserialize parameters try { // Clean the call context CallContext.SetCurrentCallContext (null); // Deserialize call data if (loc_call_data != null) { loc_call_data = mono_marshal_xdomain_copy_value (loc_call_data); RemotingServices.DeserializeCallData (loc_call_data); } // Get the target object object target = rp.GetAppDomainTarget (); // Make the call to the real object loc_return = target.Test (a); loc_call_data = RemotingServices.SerializeCallData (loc_Array); loc_exc_data = null; } catch (Exception ex) { loc_exc_data = RemotingServices.SerializeExceptionData (ex); } return loc_return; }