254 lines
8.9 KiB
Plaintext
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;
|
||
|
}
|
||
|
|