Fix Xamarin bug 7594.
[mono-project.git] / docs / remoting
blob65f92323bfb191ebf5c53894c8a57aa100c4002b
1 Runtime support for Remoting
2 ============================
4 The runtime supports a special objects called "TransparentProxy". You can
5 create objects of this type by calling GetTransparentProxy() on a "RealProxy"
6 object. 
8 LDFLD/STFLD for transparent proxies
9 ===================================
11 Access to fields must be redirected to the remote object. System.Object has
12 some special methods for that:
14 void FieldGetter (string typeName, string fieldName, ref object val);
15                 
16 void FieldSetter (string typeName, string fieldName, object val);
18 This methods are never called on actual object. The are only used to pack
19 LDFLD/STFLD operations into method call messages, which are then passed to the
20 RealProxy::Invoke() method. 
22 There are two helper methods which can be used by the JIT and the interpreter
23 to convert LDFLD/STFLD operations into messages and then call
24 RealProxy::Invoke(): mono_store_remote_field() and mono_load_remote_field().
26 Cross app domain optimizations
27 ==============================
29 The new implementation of the cross app domain channel makes a minimal use of
30 the remoting infrastructure. The idea is to create remoting wrappers specific
31 for cross app domain calls, which take the input paramers, switch the domain
32 and dispatch the call in the new domain.
34 When an vtable for a proxy needs to be created, the runtime checks if the proxy
35 is referencing an object that belongs to another domain in the same process.
36 In such case, the fast xdomain wrapper is returned instead of the regular one.
38 The xdomain wrapper will have a different structure depending on the signature
39 of the method it wraps, since different types have different marshalling needs.
40 There are four types of marshalling, the first one is the fastest, the last one
41 is the slowest:
43 1) No marshalling at all: this is for primitive types.
45 2) Internal copy of the object in the new domain: some system types can
46    be copied from one domain to the other by the runtime. This currently
47    applies to arrays of primitive types (or arrays of values that can be
48    internally copied), String and StringBuilder. We can add more types in
49    the future.
50    
51 3) Internal copy for Out parameters. It is a specific case of the previous
52    type, when an input parameter has the [Out] attribute, which means that the
53    content of the object that is marshalled into the new domain, needs to be
54    copied over the instance of the original object. This applies to arrays
55    of primitive types and StringBuilder. This is used, for example, to be able
56    to call methods such as Stream.Read ([Out]buffer, pos, lengh) across domains.
57    
58 4) Serialization. The value is serialized in one domain and deserialized in the
59    other one.
60    
61 The xdomain wrapper will be generated according to the marshalling needs of
62 each parameter.
64 The cross domain wrapper is divided in two methods. The first method (the
65 wrapper itself) takes the input parameters and serializes those that need to
66 be serialized. After that, sets the new domain and calls to a second method
67 in the new domain, which deserializes the parameters, makes a local copy of
68 those that don't need serialization, and dispatches the call to the real
69 object. Then, the inverse sequence is followed: return values are serialized,
70 flow returns to the first method, which changes the domain again and
71 deserializes the values.
73 Sample wrapper
74 --------------
76 This are examples of cross domain wrappers in pseudo-C# code.
77 The first example is for a method with the following signature:
79         ArrayList Test (int a, string b, ArrayList c, ref ArrayList d, ref string e, ref int f)
81 Of course, the wrapper has the same signature:
83         ArrayList Test_xdomain_invoke (int a, string b, ArrayList c, ref ArrayList d, ref string e, ref int f)
84         {
85                 int loc_new_domainid, loc_old_domainid;
86                 ArrayList loc_return;
87                 byte[] loc_serialized_array;
88                 
89                 // Save thread domain data
90                 Context loc_context = Thread.CurrentContext;
91                 if (loc_context.IsDefaultContext) {
92                         return Test_remoting_invoke (a, b, c, ref d, ref e, ref f);
93                 }
94                 object loc_datastore = Thread.ResetDataStoreStatus ();
95                 
96                 // Create the array that will hold the parameters to be serialized
97                 object[] loc_array = new object [3];    // +1 to store the return value
98                 loc_array [0] = c;
99                 loc_array [1] = d;
100         
101                 // Serialize parameters
102                 loc_serialized_array = RemotingServices.SerializeCallData (loc_Array);
103         
104                 // Get the target domain id and change the domain
105                 RealProxy loc_real_proxy = ((TransparentProxy)this).rp;
106                 loc_new_domainid = loc_real_proxy->target_domain_id;
107                 
108                 loc_old_domainid = mono_remoting_set_domain_by_id (loc_new_domainid);
109         
110                 string e_copy = e;
111                 /* The following is an indirect call made into the target domain */
112                 Test_xdomain_dispatch (rp, ref loc_serialized_array, out loc_serialized_exc, a, b, ref e_copy, ref f);
113                 
114                 // Switch context
115                 mono_remoting_set_domain_by_id (loc_old_domainid);
116                 
117                 // Restore thread domain data
118                 mono_context_set (loc_context);
119                 Thread.RestoreDataStoreStatus (loc_datastore);
120                 
121                 if (loc_serialized_exc != null) {
122                         Exception ex = (Exception) RemotingServices.DeserializeCallData (loc_serialized_exc);
123                         ex.FixRemotingException ();
124                         throw ex;
125                 }
126                 
127                 // copy back non-serialized output parametars
128                 e = mono_marshal_xdomain_copy_value (e_copy);
129                 
130                 // Deserialize out parameters
131                 loc_serialized_array = mono_marshal_xdomain_copy_value (loc_serialized_array);
132                 loc_array = RemotingServices.DeserializeObject (loc_serialized_array);
133                 d = loc_array [1];
134                 mono_thread_force_interruption_checkpoint ();
135                 return loc_array [2];
136         }
137         
138         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)
139         {
140                 // Deserialize parameters
141                 try {
142                         // Clean the call context
143                         CallContext.SetCurrentCallContext (null);
144                         
145                         // Deserialize call data
146                         if (loc_call_data != null) {
147                                 loc_call_data = mono_marshal_xdomain_copy_value (loc_call_data);
148                                 loc_array = RemotingServices.DeserializeCallData (loc_call_data);
149                         }
150                         
151                         // Get the target object
152                         object target = rp.GetAppDomainTarget ();
153                 
154                         // Load the arguments
155                         b = mono_marshal_xdomain_copy_value (b);
156                 
157                         // Make the call to the real object
158                         mono_thread_force_interruption_checkpoint ();
159                         loc_return = target.Test (a, b, loc_array[0], ref loc_array[1], ref e, ref f);
160                         
161                         // Serialize the return values
162                         // Reset parameters in the array that don't need to be serialized back
163                         loc_array [0] = null;
164                         // Add the return value to the array
165                         loc_array [2] = loc_return;
166                         // Serialize
167                         loc_call_data = RemotingServices.SerializeCallData (loc_array);
168                         loc_exc_data = null;
169                 }
170                 catch (Exception ex) {
171                         loc_exc_data = RemotingServices.SerializeExceptionData (ex);
172                 }
173         }
176 Another example
177 ---------------
179 This is another example of a method with more simple parameters:
181         int SimpleTest_xdomain_invoke (int a)
182         {
183                 int loc_new_domainid, loc_old_domainid;
184                 int loc_return;
185                 byte[] loc_serialized_array;
186                 
187                 // Save thread domain data
188                 Context loc_context = Thread.CurrentContext;
189                 if (loc_context.IsDefaultContext) {
190                         return SimpleTest_remoting_invoke (a, b, c, ref d, ref e, ref f);
191                 }
192                 object loc_datastore = Thread.ResetDataStoreStatus ();
193                 
194                 // Serialize parameters. This will only serialize LogicalContext data if needed.
195                 loc_serialized_array = RemotingServices.SerializeCallData (null);
196         
197                 // Get the target domain id and change the domain
198                 RealProxy loc_real_proxy = ((TransparentProxy)this).rp;
199                 loc_new_domainid = loc_real_proxy->target_domain_id;
200                 
201                 loc_old_domainid = mono_remoting_set_domain_by_id (loc_new_domainid);
202         
203                 /* The following is an indirect call made into the target domain */
204                 loc_return = SimpleTest_xdomain_dispatch (rp, ref loc_serialized_array, out loc_serialized_exc, a);
205                 
206                 // Switch domain
207                 mono_remoting_set_domain_by_id (loc_old_domainid);
208                 
209                 // Restore thread domain data
210                 mono_context_set (loc_context);
211                 Thread.RestoreDataStoreStatus (loc_datastore);
212                 
213                 if (loc_serialized_exc != null) {
214                         Exception ex = (Exception) RemotingServices.DeserializeCallData (loc_serialized_exc);
215                         ex.FixRemotingException ();
216                         throw ex;
217                 }
218                 
219                 RemotingServices.DeserializeCallData (loc_serialized_array);
220                 return loc_return [2];
221         }
222         
224         int SimpleTest_xdomain_dispatch (RealProxy rp, ref byte[] loc_call_data, out byte[] loc_exc_data, int a)
225         {
226                 int loc_return;
227                 
228                 // Deserialize parameters
229                 try {
230                         // Clean the call context
231                         CallContext.SetCurrentCallContext (null);
232                         
233                         // Deserialize call data
234                         if (loc_call_data != null) {
235                                 loc_call_data = mono_marshal_xdomain_copy_value (loc_call_data);
236                                 RemotingServices.DeserializeCallData (loc_call_data);
237                         }
238                         
239                         // Get the target object
240                         object target = rp.GetAppDomainTarget ();
241                 
242                         // Make the call to the real object
243                         loc_return = target.Test (a);
244                         
245                         loc_call_data = RemotingServices.SerializeCallData (loc_Array);
246                         loc_exc_data = null;
247                 }
248                 catch (Exception ex) {
249                         loc_exc_data = RemotingServices.SerializeExceptionData (ex);
250                 }
251                 return loc_return;
252         }