Exemplo n.º 1
0
        private static ExpressionTreeForCurrentOptionsWithSize FlushCacheIfLargerThanThreshold(
            ParserOptions options,
            ExpressionTreeForCurrentOptionsWithSize cachedExpressionTreesForCurrentOptions)
        {
            if (cachedExpressionTreesForCurrentOptions.OptimisticSize > 3000)
            {
                // VS stress tests could fill up this cache without end, for example if they use
                // random configuration names - those appear in conditional expressions.
                // So if we hit a limit that we should never hit in normal circumstances in VS,
                // and rarely, periodically hit in normal circumstances in large tree builds,
                // just clear out the cache. It can start repopulating again. Some kind of prioritized
                // aging isn't worth it: although the hit rate of these caches is excellent (nearly 100%)
                // the cost of reparsing expressions should the cache be cleared is not particularly large.
                // Loading Australian Government in VS, there are 3 of these tables, two with about 50
                // entries and one with about 650 entries. So 3000 seems large enough.
                cachedExpressionTreesForCurrentOptions = s_cachedExpressionTrees.AddOrUpdate(
                    (int)options,
                    _ =>
                    new ExpressionTreeForCurrentOptionsWithSize(
                        new ConcurrentDictionary <string, ConcurrentStack <GenericExpressionNode> >(StringComparer.Ordinal)),
                    (key, existing) =>
                {
                    if (existing.OptimisticSize > 3000)
                    {
                        return
                        (new ExpressionTreeForCurrentOptionsWithSize(
                             new ConcurrentDictionary <string, ConcurrentStack <GenericExpressionNode> >(StringComparer.Ordinal)));
                    }
                    else
                    {
                        return(existing);
                    }
                });
            }

            return(cachedExpressionTreesForCurrentOptions);
        }
Exemplo n.º 2
0
        /// <summary>
        /// Evaluates a string representing a condition from a "condition" attribute.
        /// If the condition is a malformed string, it throws an InvalidProjectFileException.
        /// This method uses cached expression trees to avoid generating them from scratch every time it's called.
        /// This method is thread safe and is called from engine and task execution module threads
        /// Logging service may be null.
        /// </summary>
        internal static bool EvaluateConditionCollectingConditionedProperties <P, I>
        (
            string condition,
            ParserOptions options,
            Expander <P, I> expander,
            ExpanderOptions expanderOptions,
            Dictionary <string, List <string> > conditionedPropertiesTable,
            string evaluationDirectory,
            ElementLocation elementLocation,
            ILoggingService loggingServices,
            BuildEventContext buildEventContext,
            ProjectRootElementCache projectRootElementCache = null
        )
            where P : class, IProperty
            where I : class, IItem
        {
            ErrorUtilities.VerifyThrowArgumentNull(condition, "condition");
            ErrorUtilities.VerifyThrowArgumentNull(expander, "expander");
            ErrorUtilities.VerifyThrowArgumentLength(evaluationDirectory, "evaluationDirectory");
            ErrorUtilities.VerifyThrowArgumentNull(buildEventContext, "buildEventContext");

            // An empty condition is equivalent to a "true" condition.
            if (condition.Length == 0)
            {
                return(true);
            }

            // If the condition wasn't empty, there must be a location for it
            ErrorUtilities.VerifyThrowArgumentNull(elementLocation, "elementLocation");

            // Get the expression tree cache for the current parsing options.
            ExpressionTreeForCurrentOptionsWithSize cachedExpressionTreesForCurrentOptions = s_cachedExpressionTrees.GetOrAdd(
                (int)options,
                _ => new ExpressionTreeForCurrentOptionsWithSize(new ConcurrentDictionary <string, ConcurrentStack <GenericExpressionNode> >(StringComparer.Ordinal)));

            cachedExpressionTreesForCurrentOptions = FlushCacheIfLargerThanThreshold(options, cachedExpressionTreesForCurrentOptions);

            // Get the pool of expressions for this condition.
            var expressionPool = cachedExpressionTreesForCurrentOptions.GetOrAdd(condition, _ => new ConcurrentStack <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.
            GenericExpressionNode parsedExpression;

            if (!expressionPool.TryPop(out parsedExpression))
            {
                Parser conditionParser = new Parser();

                #region REMOVE_COMPAT_WARNING
                conditionParser.LoggingServices      = loggingServices;
                conditionParser.LogBuildEventContext = buildEventContext;
                #endregion

                parsedExpression = conditionParser.Parse(condition, options, elementLocation);
            }

            bool result;

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

            // 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
                {
                    result = parsedExpression.Evaluate(state);
                }
                finally
                {
                    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);
                    }
                }
            }

            return(result);
        }