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;
	}

