forked from mono-soc-2012/mono
-
Notifications
You must be signed in to change notification settings - Fork 0
/
remoting
253 lines (193 loc) · 8.88 KB
/
remoting
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
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;
}