You've already forked linux-packaging-mono
							
							
		
			
	
	
		
			254 lines
		
	
	
		
			8.9 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
		
		
			
		
	
	
			254 lines
		
	
	
		
			8.9 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
|   | 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; | ||
|  | 	} | ||
|  | 
 |