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