/// <summary>
 /// Creates a new ProjectExecutionDetail instance from the details encapsulated in a 
 /// ProjectInvocationRule.
 /// </summary>
 public static ProjectExecutionDetail Create(IProjectInvocationRule rule)
 {
     return new ProjectExecutionDetail() {
         Key = null,
         InputDataRequirement = DataRequirement.None,
         Path = rule.ProjectPath,
         TimeLimit = rule.TimeLimit,
         Unit = rule.Unit,
         Log = null
     };
 }
 /// <summary>
 /// Identifies whether material information differs between a ProjectExecutionDetail and an
 /// IProjectInvocationRule.
 /// </summary>
 private bool DetailsHaveChanged(ProjectExecutionDetail detail, IProjectInvocationRule rule)
 {
     int ms1 = Helper.GetMilliseconds(detail.TimeLimit, detail.Unit);
     int ms2 = Helper.GetMilliseconds(rule.TimeLimit, rule.Unit);
     return ms1 != ms2 || detail.Path != rule.ProjectPath;
 }
 /// <summary>
 /// Instructs the Absyntax runtime host to close certain project slots.
 /// </summary>
 /// <remarks>
 /// Project slots to be closed are:
 /// (a) those that are not referred to in the incoming invocation rules;
 /// (b) those that are associated with rules whose material details have changed;
 /// (c) those that are associated with rules for which the property ReloadProjectBeforeExecuting 
 /// returns true.
 /// </remarks>
 private void UnloadProjectsWhereNecessary(IProjectInvocationRule[] rules, IRuntimeManager manager)
 {
     KeyValuePair<int, ProjectExecutionDetail>[] items;
     lock (m_ruleState) {
         items = m_ruleState.ToArray();
     }
     var list = new List<int>();
     foreach (var kvp in items) {
         int ruleId = kvp.Key;
         ProjectExecutionDetail ped = kvp.Value;
         IProjectInvocationRule matchingRule = rules.FirstOrDefault(r => r.Id == ruleId);
         if (matchingRule == null || DetailsHaveChanged(ped, matchingRule) || matchingRule.ReloadProjectBeforeExecuting) {
             int? key = ped.Key;
             if (key.HasValue) {
                 manager.Unload(key.Value);
             }
             list.Add(ruleId);
         }
     }
     lock (m_ruleState) {
         list.ForEach(i => m_ruleState.Remove(i));
     }
 }
 /// <summary>
 /// Creates a new ExecutionItem instance for each supplied IProjectInvocationRule.
 /// </summary>
 private ExecutionItem[] CreateExecutionItems(IProjectInvocationRule[] rules)
 {
     var list = new List<ExecutionItem>();
     lock (m_ruleState) {
         foreach (IProjectInvocationRule rule in rules) {
             ProjectExecutionDetail detail;
             if (!m_ruleState.TryGetValue(rule.Id, out detail)) {
                 detail = ProjectExecutionDetail.Create(rule);
             }
             var item = new ExecutionItem(rule, detail);
             list.Add(item);
         }
     }
     return list.ToArray();
 }