/// <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>
 /// Causes this WorkbookRuntimeAdapter to close all open project runtime slots.
 /// </summary>
 /// <param name="manager">The IRuntimeManager to be used to perform the unload.</param>
 public void UnloadAll(IRuntimeManager manager)
 {
     ProjectExecutionDetail[] items;
     lock (m_ruleState) {
         items = m_ruleState.Values.ToArray();
         m_ruleState.Clear();
     }
     foreach (var key in items.Select(i => i.Key)) {
         if (key.HasValue) {
             manager.Unload(key.Value);
         }
     }
 }