Jo Shields a575963da9 Imported Upstream version 3.6.0
Former-commit-id: da6be194a6b1221998fc28233f2503bd61dd9d14
2014-08-13 10:39:27 +01:00

254 lines
8.9 KiB
Plaintext

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