예제 #1
0
        /// <summary>
        /// If any expression nodes cache any state for the duration of evaluation,
        /// now's the time to clean it up
        /// </summary>
        internal override void ResetState()
        {
            if (_leftChild != null)
            {
                _leftChild.ResetState();
            }

            if (_rightChild != null)
            {
                _rightChild.ResetState();
            }
        }
예제 #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");

            Hashtable cachedExpressionTreesForCurrentOptions = s_cachedExpressionTrees[(int)options];

            // We only need to lock on writes to the table
            if (cachedExpressionTreesForCurrentOptions == null)
            {
                // Given property functions, casing in conditional expressions isn't necessarily ignored.
                cachedExpressionTreesForCurrentOptions = new Hashtable(50, StringComparer.Ordinal);

                lock (s_cachedExpressionTrees)
                {
                    s_cachedExpressionTrees[(int)options] = cachedExpressionTreesForCurrentOptions;
                }
            }

            // 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.
            if (cachedExpressionTreesForCurrentOptions.Count > 3000) // threadsafe
            {
                lock (cachedExpressionTreesForCurrentOptions)
                {
                    cachedExpressionTreesForCurrentOptions.Clear();
                }
            }

            // Try and see if we have an expression tree for this condition already
            GenericExpressionNode parsedExpression = (GenericExpressionNode)cachedExpressionTreesForCurrentOptions[condition];

            if (parsedExpression == null)
            {
                Parser conditionParser = new Parser();

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

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

                // It's possible two threads will add a different tree to the same entry in the hashtable,
                // but it should be rare and it's not a problem - the previous entry will be thrown away.
                // We could ensure no dupes with double check locking but it's not really necessary here.
                // Also, we don't want to lock on every read.
                lock (cachedExpressionTreesForCurrentOptions)
                {
                    if (!s_disableExpressionCaching)
                    {
                        cachedExpressionTreesForCurrentOptions[condition] = parsedExpression;
                    }
                }
            }

            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();
                }
            }

            return(result);
        }