1Runtime support for Remoting
2============================
3
4The runtime supports a special objects called "TransparentProxy". You can
5create objects of this type by calling GetTransparentProxy() on a "RealProxy"
6object.
7
8LDFLD/STFLD for transparent proxies
9===================================
10
11Access to fields must be redirected to the remote object. System.Object has
12some special methods for that:
13
14void FieldGetter (string typeName, string fieldName, ref object val);
15
16void FieldSetter (string typeName, string fieldName, object val);
17
18This methods are never called on actual object. The are only used to pack
19LDFLD/STFLD operations into method call messages, which are then passed to the
20RealProxy::Invoke() method.
21
22There are two helper methods which can be used by the JIT and the interpreter
23to convert LDFLD/STFLD operations into messages and then call
24RealProxy::Invoke(): mono_store_remote_field() and mono_load_remote_field().
25
26Cross app domain optimizations
27==============================
28
29The new implementation of the cross app domain channel makes a minimal use of
30the remoting infrastructure. The idea is to create remoting wrappers specific
31for cross app domain calls, which take the input paramers, switch the domain
32and dispatch the call in the new domain.
33
34When an vtable for a proxy needs to be created, the runtime checks if the proxy
35is referencing an object that belongs to another domain in the same process.
36In such case, the fast xdomain wrapper is returned instead of the regular one.
37
38The xdomain wrapper will have a different structure depending on the signature
39of the method it wraps, since different types have different marshalling needs.
40There are four types of marshalling, the first one is the fastest, the last one
41is the slowest:
42
431) No marshalling at all: this is for primitive types.
44
452) 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
513) 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
584) Serialization. The value is serialized in one domain and deserialized in the
59   other one.
60
61The xdomain wrapper will be generated according to the marshalling needs of
62each parameter.
63
64The cross domain wrapper is divided in two methods. The first method (the
65wrapper itself) takes the input parameters and serializes those that need to
66be serialized. After that, sets the new domain and calls to a second method
67in the new domain, which deserializes the parameters, makes a local copy of
68those that don't need serialization, and dispatches the call to the real
69object. Then, the inverse sequence is followed: return values are serialized,
70flow returns to the first method, which changes the domain again and
71deserializes the values.
72
73Sample wrapper
74--------------
75
76This are examples of cross domain wrappers in pseudo-C# code.
77The first example is for a method with the following signature:
78
79	ArrayList Test (int a, string b, ArrayList c, ref ArrayList d, ref string e, ref int f)
80
81Of course, the wrapper has the same signature:
82
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	}
174
175
176Another example
177---------------
178
179This is another example of a method with more simple parameters:
180
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
223
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	}
253
254