Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 54 additions & 46 deletions src/Build/Evaluation/ConditionEvaluator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -139,20 +139,20 @@ static bool ContainsInvalidCharacter(ReadOnlySpan<char> span)
private struct ExpressionTreeForCurrentOptionsWithSize
{
// condition string -> pool of expression trees
private readonly ConcurrentDictionary<string, ConcurrentStack<GenericExpressionNode>> _conditionPools;
private readonly ConcurrentDictionary<string, Stack<GenericExpressionNode>> _conditionPools;
private int _mOptimisticSize;

public readonly int OptimisticSize => _mOptimisticSize;

public ExpressionTreeForCurrentOptionsWithSize(ConcurrentDictionary<string, ConcurrentStack<GenericExpressionNode>> conditionPools)
public ExpressionTreeForCurrentOptionsWithSize(ConcurrentDictionary<string, Stack<GenericExpressionNode>> conditionPools)
{
_conditionPools = conditionPools;
_mOptimisticSize = conditionPools.Count;
}

public ConcurrentStack<GenericExpressionNode> GetOrAdd(string condition, Func<string, ConcurrentStack<GenericExpressionNode>> addFunc)
public Stack<GenericExpressionNode> GetOrAdd(string condition, Func<string, Stack<GenericExpressionNode>> addFunc)
{
if (!_conditionPools.TryGetValue(condition, out var stack))
if (!_conditionPools.TryGetValue(condition, out Stack<GenericExpressionNode>? stack))
{
// Count how many conditions there are in the cache.
// The condition evaluator will flush the cache when some threshold is exceeded.
Expand Down Expand Up @@ -241,61 +241,69 @@ internal static bool EvaluateConditionCollectingConditionedProperties<P, I>(
// Get the expression tree cache for the current parsing options.
var cachedExpressionTreesForCurrentOptions = s_cachedExpressionTrees.GetOrAdd(
(int)options,
_ => new ExpressionTreeForCurrentOptionsWithSize(new ConcurrentDictionary<string, ConcurrentStack<GenericExpressionNode>>(StringComparer.Ordinal)));
_ => new ExpressionTreeForCurrentOptionsWithSize(new ConcurrentDictionary<string, Stack<GenericExpressionNode>>(StringComparer.Ordinal)));

cachedExpressionTreesForCurrentOptions = FlushCacheIfLargerThanThreshold(options, cachedExpressionTreesForCurrentOptions);

// Get the pool of expressions for this condition.
var expressionPool = cachedExpressionTreesForCurrentOptions.GetOrAdd(condition, _ => new ConcurrentStack<GenericExpressionNode>());
Stack<GenericExpressionNode> expressionPool = cachedExpressionTreesForCurrentOptions.GetOrAdd(condition, _ => new Stack<GenericExpressionNode>());

// Try and see if there's an available expression tree in the pool.
// If not, parse a new expression tree and add it back to the pool.
if (!expressionPool.TryPop(out var parsedExpression))
lock (expressionPool)
{
var conditionParser = new Parser();

#region REMOVE_COMPAT_WARNING
conditionParser.LoggingServices = loggingContext?.LoggingService;
conditionParser.LogBuildEventContext = loggingContext?.BuildEventContext ?? BuildEventContext.Invalid;
#endregion

parsedExpression = conditionParser.Parse(condition, options, elementLocation);
}
// Try and see if there's an available expression tree in the pool.
// If not, parse a new expression tree and add it back to the pool.
GenericExpressionNode parsedExpression;
if (expressionPool.Count == 0)
{
var conditionParser = new Parser();

bool result;
#region REMOVE_COMPAT_WARNING
conditionParser.LoggingServices = loggingContext?.LoggingService;
conditionParser.LogBuildEventContext = loggingContext?.BuildEventContext ?? BuildEventContext.Invalid;
#endregion

var state = new ConditionEvaluationState<P, I>(
condition,
expander,
expanderOptions,
conditionedPropertiesTable,
evaluationDirectory,
elementLocation,
fileSystem,
projectRootElementCache);

expander.PropertiesUseTracker.PropertyReadContext = PropertyReadContext.ConditionEvaluation;
// We are evaluating this expression now and it can cache some state for the duration,
// so we don't want multiple threads working on the same expression
lock (parsedExpression)
{
try
parsedExpression = conditionParser.Parse(condition, options, elementLocation);
}
else
{
result = parsedExpression.Evaluate(state);
parsedExpression = expressionPool.Pop();
}
finally

bool result;

var state = new ConditionEvaluationState<P, I>(
condition,
expander,
expanderOptions,
conditionedPropertiesTable,
evaluationDirectory,
elementLocation,
fileSystem,
projectRootElementCache);

expander.PropertiesUseTracker.PropertyReadContext = PropertyReadContext.ConditionEvaluation;
// We are evaluating this expression now and it can cache some state for the duration,
// so we don't want multiple threads working on the same expression
lock (parsedExpression)
{
parsedExpression.ResetState();
if (!s_disableExpressionCaching)
try
{
result = parsedExpression.Evaluate(state);
}
finally
{
// Finished using the expression tree. Add it back to the pool so other threads can use it.
expressionPool.Push(parsedExpression);
parsedExpression.ResetState();
if (!s_disableExpressionCaching)
{
// Finished using the expression tree. Add it back to the pool so other threads can use it.
expressionPool.Push(parsedExpression);
}
expander.PropertiesUseTracker.ResetPropertyReadContext();
}
expander.PropertiesUseTracker.ResetPropertyReadContext();
}
}

return result;
return result;
}
}

private static ExpressionTreeForCurrentOptionsWithSize FlushCacheIfLargerThanThreshold(
Expand All @@ -317,14 +325,14 @@ private static ExpressionTreeForCurrentOptionsWithSize FlushCacheIfLargerThanThr
(int)options,
_ =>
new ExpressionTreeForCurrentOptionsWithSize(
new ConcurrentDictionary<string, ConcurrentStack<GenericExpressionNode>>(StringComparer.Ordinal)),
new ConcurrentDictionary<string, Stack<GenericExpressionNode>>(StringComparer.Ordinal)),
(key, existing) =>
{
if (existing.OptimisticSize > 3000)
{
return
new ExpressionTreeForCurrentOptionsWithSize(
new ConcurrentDictionary<string, ConcurrentStack<GenericExpressionNode>>(StringComparer.Ordinal));
new ConcurrentDictionary<string, Stack<GenericExpressionNode>>(StringComparer.Ordinal));
}
else
{
Expand Down