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