Skip to content

Commit f1586a9

Browse files
author
Gokhan Abatay
committed
Avoid lambda compilation as much as possible, concurrent Compile() call causes "Garbage Collector" suspend all threads too often
1 parent bb1bf45 commit f1586a9

File tree

3 files changed

+74
-21
lines changed

3 files changed

+74
-21
lines changed

src/NHibernate/Impl/ExpressionProcessor.cs

Lines changed: 57 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -248,32 +248,73 @@ private static ICriterion Le(ProjectionInfo property, object value)
248248
}
249249

250250
/// <summary>
251-
/// Invoke the expression to extract its runtime value
251+
/// Walk or Invoke expression to extract its runtime value
252252
/// </summary>
253253
public static object FindValue(Expression expression)
254254
{
255-
if (expression.NodeType == ExpressionType.Constant)
256-
return ((ConstantExpression) expression).Value;
257-
258-
if (expression.NodeType == ExpressionType.MemberAccess)
255+
object value;
256+
switch (expression.NodeType)
259257
{
260-
var memberAccess = (MemberExpression) expression;
261-
if (memberAccess.Expression == null || memberAccess.Expression.NodeType == ExpressionType.Constant)
262-
{
263-
var constantValue = ((ConstantExpression) memberAccess.Expression)?.Value;
264-
var member = memberAccess.Member;
265-
switch (member.MemberType)
258+
case ExpressionType.Constant:
259+
var constantExpression = (ConstantExpression) expression;
260+
return constantExpression.Value;
261+
case ExpressionType.MemberAccess:
262+
var memberExpression = (MemberExpression) expression;
263+
value = memberExpression.Expression != null ? FindValue(memberExpression.Expression) : null;
264+
265+
switch (memberExpression.Member.MemberType)
266266
{
267267
case MemberTypes.Field:
268-
return ((FieldInfo) member).GetValue(constantValue);
268+
return ((FieldInfo) memberExpression.Member).GetValue(value);
269269
case MemberTypes.Property:
270-
return ((PropertyInfo) member).GetValue(constantValue);
270+
return ((PropertyInfo) memberExpression.Member).GetValue(value);
271271
}
272-
}
272+
break;
273+
case ExpressionType.Call:
274+
var methodCallExpression = (MethodCallExpression) expression;
275+
var args = new object[methodCallExpression.Arguments.Count];
276+
for (int i = 0; i < args.Length; i++)
277+
args[i] = FindValue(methodCallExpression.Arguments[i]);
278+
279+
if (methodCallExpression.Object == null) //extension or static method
280+
{
281+
if (args.Length > 0 && args[0] != null)
282+
{
283+
return methodCallExpression.Method.Invoke(args[0].GetType(), args);
284+
}
285+
else
286+
{
287+
return methodCallExpression.Method.Invoke(methodCallExpression.Method.DeclaringType, args);
288+
}
289+
}
290+
else
291+
{
292+
var callingObject = FindValue(methodCallExpression.Object);
293+
294+
return methodCallExpression.Method.Invoke(callingObject, args);
295+
}
296+
case ExpressionType.Convert:
297+
var unaryExpression = (UnaryExpression) expression;
298+
value = FindValue(unaryExpression.Operand);
299+
var type = Nullable.GetUnderlyingType(unaryExpression.Type) ?? unaryExpression.Type;
300+
if (type == typeof(object) || value == null)
301+
{
302+
return value;
303+
}
304+
else if (value is IConvertible || unaryExpression.Method != null)
305+
{
306+
if (type != unaryExpression.Operand.Type)
307+
{
308+
value = unaryExpression.Method != null ? unaryExpression.Method.Invoke(null, new[] { value }) : Convert.ChangeType(value, type);
309+
}
310+
311+
return value;
312+
}
313+
break;
273314
}
274315

275-
var valueExpression = Expression.Lambda(expression).Compile();
276-
object value = valueExpression.DynamicInvoke();
316+
var valueExpression = Expression.Lambda(expression).Compile(true);
317+
value = valueExpression.DynamicInvoke();
277318
return value;
278319
}
279320

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
using System;
2+
using System.Linq.Expressions;
3+
4+
namespace NHibernate.Impl
5+
{
6+
#if NET461
7+
internal static class LambdaExpressionExtensions
8+
{
9+
public static Delegate Compile(this LambdaExpression expression, bool preferInterpretation) =>
10+
expression.Compile(); //Concurrent Compile() call causes "Garbage Collector" suspend all threads too often
11+
}
12+
#endif
13+
}

src/NHibernate/Linq/Visitors/ExpressionParameterVisitor.cs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.Linq.Expressions;
66
using System.Reflection;
77
using NHibernate.Engine;
8+
using NHibernate.Impl;
89
using NHibernate.Linq.Functions;
910
using NHibernate.Param;
1011
using NHibernate.Type;
@@ -150,12 +151,10 @@ protected override Expression VisitUnary(UnaryExpression node)
150151
node.Method != null && // The implicit/explicit operator method
151152
node.Operand is ConstantExpression constantExpression)
152153
{
153-
// Instead of getting constantExpression.Value, we override the value by compiling and executing this subtree,
154-
// performing the cast.
155-
var lambda = Expression.Lambda<Func<object>>(Expression.Convert(node, typeof(object)));
156-
var compiledLambda = lambda.Compile();
154+
// Instead of getting constantExpression.Value, we override the value with ExpressionProcessor
155+
var value = ExpressionProcessor.FindValue(node);
157156

158-
AddConstantExpressionParameter(constantExpression, compiledLambda());
157+
AddConstantExpressionParameter(constantExpression, value);
159158
}
160159

161160
return base.VisitUnary(node);

0 commit comments

Comments
 (0)