Пример #1
0
        /// <summary>
        /// This function is a little complex:
        ///
        /// 1.	It needs to be thread-safe and atomic.  This means the iteration over the list
        ///     needs to happen w/o releasing a lock, and the tests also get run under lock
        ///     (they’re simple operations and therefore are safe to do this).  But the target
        ///     is not safe to run under a lock because it may block and cause a deadlock.  Therefore
        ///     we need to release the lock before running the target.
        /// 2.	The code for doing the evaluation is a little complex, and the threading issues makes
        ///     it more complex.  Therefore factoring it into a version which just looks for a rule,
        ///     and a version which looks and executes a rule is either going to obscure the locking issues
        ///     or result in a reasonable duplication of code which may be error prone to maintain.
        /// 3.	The updates of the sites needs to happen before the execution of the rule.  This
        ///     is due to both stack overflow and performance issues.  If a function goes recursive and we
        ///     haven’t updated the recursive-call site before then we’ll repeatedly generate and evaluate
        ///     rules until we stack overflow, and if we don’t stack overflow our perf will
        ///     suck until the method unwinds once.
        ///
        /// For those reasons we get the one big method  which takes 7 parameters to handle both updating
        /// and executing.  One of those parameters is the bool flag to indicate that we should execute
        /// the rule (that’s how that decision is made).  3 more are the result of the execution and the
        /// target/rule list to update for the caller.  One more is the site object which we need to lock
        /// on to make the update to the site atomic.  And finally we get the CodeContext which now flows
        /// in instead of having RuleTree hold onto a LanguageContext.  This is because we need
        /// the full CodeContext to execute the test/target and it needs to be the real CodeContext so
        /// we get the proper set of locals/globals flowing through.
        /// </summary>
        private StandardRule <T> GetRuleMaybeExecute(CodeContext callerContext, object[] args, bool execute, object site, ref T target, ref RuleSet <T> rules, out object result)
        {
            // TODO: We can do better granularity than just types.
            LinkedList <StandardRule <T> > ruleList = GetRuleList(args);

            if (DynamicSiteHelpers.IsBigTarget(typeof(T)))
            {
                args = new object[] { Tuple.MakeTuple(typeof(T).GetGenericArguments()[0], args) };
            }

            bool lockReleased = false;
            int  index        = 0;

            Monitor.Enter(ruleList);
            try {
                LinkedListNode <StandardRule <T> > node = ruleList.First;
                while (node != null)
                {
                    StandardRule <T> rule = node.Value;
                    if (!rule.IsValid)
                    {
                        LinkedListNode <StandardRule <T> > nodeToRemove = node;
                        node = node.Next;
                        ruleList.Remove(nodeToRemove);
                        continue;
                    }
                    PerfTrack.NoteEvent(PerfTrack.Categories.RuleEvaluation, "Evaluating " + index++ + " rule in tree");

                    CodeContext tmpCtx = callerContext.Scope.GetTemporaryVariableContext(callerContext, rule.ParamVariables, args);
                    try {
                        if ((bool)rule.Test.Evaluate(tmpCtx))
                        {
                            // Tentative optimization of moving rule to front of list when found
                            ruleList.Remove(node);
                            ruleList.AddFirst(node);

                            if (site != null)
                            {
                                DynamicSiteHelpers.UpdateSite <T>(callerContext, site, ref target, ref rules, rule);
                            }

                            // release the lock for calling the target which may block, we assume
                            // the test performs no synchronization
                            Monitor.Exit(ruleList);
                            lockReleased = true;

                            if (execute)
                            {
                                result = rule.Target.Execute(tmpCtx);
                            }
                            else
                            {
                                result = null;
                            }

                            return(rule);
                        }
                    } finally {
                        tmpCtx.Scope.TemporaryStorage.Clear();
                    }

                    node = node.Next;
                }
            } finally {
                if (!lockReleased)
                {
                    Monitor.Exit(ruleList);
                }
            }


            PerfTrack.NoteEvent(PerfTrack.Categories.Rules, "NoMatch" + index);

            result = null;
            return(null);
        }
Пример #2
0
 public StandardRule <T> ExecuteRuleAndUpdateSite(CodeContext callerContext, object[] args, object site, ref T target, ref RuleSet <T> rules, out object result)
 {
     return(GetRuleMaybeExecute(callerContext, args, true, site, ref target, ref rules, out result));
 }