You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by pt...@apache.org on 2016/06/17 15:33:43 UTC

ignite git commit: IGNITE-2877 .NET: Compile and cache delegates for service method invocation

Repository: ignite
Updated Branches:
  refs/heads/master 00a60b951 -> fbba10219


IGNITE-2877 .NET: Compile and cache delegates for service method invocation

This closes #690


Project: http://git-wip-us.apache.org/repos/asf/ignite/repo
Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/fbba1021
Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/fbba1021
Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/fbba1021

Branch: refs/heads/master
Commit: fbba10219281dbf6b3a7cf369dbc1c08640a4cba
Parents: 00a60b9
Author: Pavel Tupitsyn <pt...@apache.org>
Authored: Fri Jun 17 18:33:32 2016 +0300
Committer: Pavel Tupitsyn <pt...@apache.org>
Committed: Fri Jun 17 18:33:32 2016 +0300

----------------------------------------------------------------------
 .../Services/ServicesTest.cs                    |  5 +--
 .../Impl/Common/DelegateConverter.cs            | 44 ++++++++++++++++++++
 .../Impl/Services/ServiceProxyInvoker.cs        | 14 +++----
 3 files changed, 52 insertions(+), 11 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/fbba1021/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/ServicesTest.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/ServicesTest.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/ServicesTest.cs
index e114f5a..2bacf50 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/ServicesTest.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/ServicesTest.cs
@@ -321,8 +321,7 @@ namespace Apache.Ignite.Core.Tests.Services
 
             // .. but setter does not
             var ex = Assert.Throws<ServiceInvocationException>(() => { prx.TestProperty = new object(); });
-            Assert.AreEqual("Object of type 'System.Object' cannot be converted to type 'System.Int32'.",
-                ex.InnerException.Message);
+            Assert.AreEqual("Specified cast is not valid.", ex.InnerException.Message);
         }
 
         /// <summary>
@@ -528,7 +527,7 @@ namespace Apache.Ignite.Core.Tests.Services
 
             // Basics
             Assert.IsTrue(svc.isInitialized());
-            Assert.IsTrue(svc.isExecuted());
+            Assert.IsTrue(TestUtils.WaitForCondition(() => svc.isExecuted(), 500));
             Assert.IsFalse(svc.isCancelled());
 
             // Primitives

http://git-wip-us.apache.org/repos/asf/ignite/blob/fbba1021/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Common/DelegateConverter.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Common/DelegateConverter.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Common/DelegateConverter.cs
index 9a0f789..ff61e28 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Common/DelegateConverter.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Common/DelegateConverter.cs
@@ -152,6 +152,50 @@ namespace Apache.Ignite.Core.Impl.Common
 
             return Expression.Lambda<T>(callExpr, argParams).Compile();
         }
+        /// <summary>
+        /// Compiles a function with a single object[] argument which maps array items to actual arguments.
+        /// </summary>
+        /// <param name="method">Method.</param>
+        /// <returns>
+        /// Compiled function that calls specified method.
+        /// </returns>
+        [SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods")]
+        public static Func<object, object[], object> CompileFuncFromArray(MethodInfo method)
+        {
+            Debug.Assert(method != null);
+            Debug.Assert(method.DeclaringType != null);
+
+            var targetParam = Expression.Parameter(typeof(object));
+            var targetParamConverted = Expression.Convert(targetParam, method.DeclaringType);
+
+            var arrParam = Expression.Parameter(typeof(object[]));
+
+            var methodParams = method.GetParameters();
+            var argParams = new Expression[methodParams.Length];
+
+            for (var i = 0; i < methodParams.Length; i++)
+            {
+                var arrElem = Expression.ArrayIndex(arrParam, Expression.Constant(i));
+                argParams[i] = Expression.Convert(arrElem, methodParams[i].ParameterType);
+            }
+            
+            Expression callExpr = Expression.Call(targetParamConverted, method, argParams);
+
+            if (callExpr.Type == typeof(void))
+            {
+                // Convert action to function
+                var action = Expression.Lambda<Action<object, object[]>>(callExpr, targetParam, arrParam).Compile();
+                return (obj, args) =>
+                {
+                    action(obj, args);
+                    return null;
+                };
+            }
+
+            callExpr = Expression.Convert(callExpr, typeof(object));
+
+            return Expression.Lambda<Func<object, object[], object>>(callExpr, targetParam, arrParam).Compile();
+        }
 
         /// <summary>
         /// Compiles a generic ctor with arbitrary number of arguments.

http://git-wip-us.apache.org/repos/asf/ignite/blob/fbba1021/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Services/ServiceProxyInvoker.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Services/ServiceProxyInvoker.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Services/ServiceProxyInvoker.cs
index 3c34886..bf6cd16 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Services/ServiceProxyInvoker.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Services/ServiceProxyInvoker.cs
@@ -32,8 +32,8 @@ namespace Apache.Ignite.Core.Impl.Services
     internal static class ServiceProxyInvoker
     {
         /** Cached method info. */
-        private static readonly CopyOnWriteConcurrentDictionary<Tuple<Type, string, int>, MethodInfo> Methods =
-            new CopyOnWriteConcurrentDictionary<Tuple<Type, string, int>, MethodInfo>();
+        private static readonly CopyOnWriteConcurrentDictionary<Tuple<Type, string, int>, Func<object, object[], object>> Methods =
+            new CopyOnWriteConcurrentDictionary<Tuple<Type, string, int>, Func<object, object[], object>>();
 
         /// <summary>
         /// Invokes the service method according to data from a stream,
@@ -69,14 +69,14 @@ namespace Apache.Ignite.Core.Impl.Services
         /// <summary>
         /// Finds suitable method in the specified type, or throws an exception.
         /// </summary>
-        private static MethodBase GetMethodOrThrow(Type svcType, string methodName, object[] arguments)
+        private static Func<object, object[], object> GetMethodOrThrow(Type svcType, string methodName, object[] arguments)
         {
             Debug.Assert(svcType != null);
             Debug.Assert(!string.IsNullOrWhiteSpace(methodName));
 
             // 0) Check cached methods
             var cacheKey = Tuple.Create(svcType, methodName, arguments.Length);
-            MethodInfo res;
+            Func<object, object[], object> res;
 
             if (Methods.TryGetValue(cacheKey, out res))
                 return res;
@@ -89,9 +89,7 @@ namespace Apache.Ignite.Core.Impl.Services
             if (methods.Length == 1)
             {
                 // Update cache only when there is a single method with a given name and arg count.
-                Methods.GetOrAdd(cacheKey, x => methods[0]);
-
-                return methods[0];
+                return Methods.GetOrAdd(cacheKey, x => DelegateConverter.CompileFuncFromArray(methods[0]));
             }
 
             if (methods.Length == 0)
@@ -104,7 +102,7 @@ namespace Apache.Ignite.Core.Impl.Services
             methods = methods.Where(m => AreMethodArgsCompatible(arguments, m.GetParameters())).ToArray();
 
             if (methods.Length == 1)
-                return methods[0];
+                return (obj, args) => methods[0].Invoke(obj, args);
 
             // 3) 0 or more than 1 matching method - throw.
             var argsString = arguments.Length == 0