/// <summary> /// /// </summary> /// <param name="keepRunningLaunchedOptionals">if false, all plugins that are "optional" but are already launched will be stopped</param> /// <returns></returns> public bool Apply(bool stopLaunchedOptionals) { if (_planCalculator != null) { throw new InvalidOperationException(Runner.R.ReentrantApplyCall); } if (_contextObject == null) { throw new InvalidOperationException(Runner.R.InitializeRequired); } bool errorWhileApplying = false; if (_runningConfig.IsDirty) { // Allocates a new PlanCalculator and reuses it as long as reapplying is needed. _planCalculator = new PlanCalculator(_discoverer, _host.IsPluginRunning); try { do { RunnerRequirementsSnapshot requirements = new RunnerRequirementsSnapshot(_requirements); SolvedPluginConfigurationSnapshot configSnapshot = new SolvedPluginConfigurationSnapshot(_config.SolvedPluginConfiguration); // During call to ObtainBestPlan, no reentrancy can occur (ObtainBestPlan does not call // any external functions or objects nor does it raise any event). // Once obtained, the best plan is also available through _planCalculator.LastBestPlan property. ExecutionPlan bestPlan = _planCalculator.ObtainBestPlan(requirements.FinalConfigSnapshot, stopLaunchedOptionals); if (bestPlan.Impossible) { errorWhileApplying = true; } else { // Here is where rentrancy may occur. // Starting/stopping any plugin may start/stop others or enable/disable this runner. var result = _host.Execute(bestPlan.PluginsToDisable, bestPlan.PluginsToStop, bestPlan.PluginsToStart); if (result.Status != ExecutionPlanResultStatus.Success) { Debug.Assert(result.Culprit != null, "An error is necessarily associated to a plugin."); _requirements.SetRunningError(result); errorWhileApplying = true; } else { _planCalculator.ReapplyNeeded = false; _requirements.UpdateRunningStatus(requirements.FinalConfigSnapshot); _runningConfig.Apply(configSnapshot, requirements); } } }while(_planCalculator.ReapplyNeeded && !errorWhileApplying); if (ApplyDone != null) { ApplyDone(this, new ApplyDoneEventArgs(!errorWhileApplying)); } } finally { _planCalculator = null; } } return(!errorWhileApplying); }
/// <summary> /// Launches ComputeCombination for each "plugin status combination". /// Gets the lower cost among all the combinations generated. /// </summary> internal ExecutionPlan ObtainBestPlan( Dictionary<object, SolvedConfigStatus> finalConfig, bool stopLaunchedOptionals ) { _finalConfig = finalConfig; if( _parseMap == null ) _parseMap = new BitArray( _discoverer.Plugins.Count ); else _parseMap.Length = _discoverer.Plugins.Count; _mappingArray.Clear(); _mappingDic.Clear(); int disabledCount = 0; // Locking plugins : // - If a plugin is disabled, it should not be launched, we do not add it to the map // - If a plugin needs to be started (MustExistAndRun), we lock its value to true // - If a plugin is the only implemention of a service and that this service has to be started, we lock this plugin's value to true // - If a plugin has no service references and does not implement any services as well, and that it is not asked to be // started and it is NOT running or it is running but stopLaunchedOptionals is true, we lock its value to false; int index = 0; foreach( IPluginInfo pI in _discoverer.Plugins ) { // SolvedConfigStatus of the actual plugin. SolvedConfigStatus pluginStatus = _finalConfig.GetValueWithDefault( pI, SolvedConfigStatus.Optional ); if( pluginStatus == SolvedConfigStatus.Disabled ) { // If a plugin is disabled, it should not be launched, we do not add it to the map. disabledCount++; continue; } // SolvedConfigStatus of the implemented service if any. SolvedConfigStatus serviceStatus = pI.Service != null ? _finalConfig.GetValueWithDefault( pI.Service, SolvedConfigStatus.Optional ) : SolvedConfigStatus.Optional; if( serviceStatus == SolvedConfigStatus.Disabled ) { // If a plugin is disabled, it should not be launched, we do not add it to the map disabledCount++; continue; } // Here, we have no more disabled plugins. // Initializes a PluginData for this particular plugin and allocates // a new index in the bit array. Debug.Assert( index == _mappingArray.Count ); PluginData pluginData = new PluginData( pI, index, IsPluginRunning( pI ) ); _mappingArray.Add( pluginData ); _mappingDic.Add( pI, pluginData ); if( pluginStatus == SolvedConfigStatus.MustExistAndRun || (serviceStatus == SolvedConfigStatus.MustExistAndRun && pI.Service.Implementations.Count == 1) ) { // If a plugin needs to be started (MustExistAndRun), we lock its value to true. // If a plugin is the only implemention of a service and that this service has to be started, we lock this plugin's value to true. _parseMap.Set( index, true ); pluginData.Locked = true; } else if( pI.Service == null && pI.ServiceReferences.Count == 0 ) // This plugin is independent. { // This is only an optimization. // The cost function gives a cost to the stop or the start of a plugin. When a plugin is independant like in this case, we lock its // status by taking into account the requirement (should it run? MustExistTryStart/OptionalTryStart) and its current status (IsPluginRunning) // and the stopLaunchedOptionals boolean. if( (pluginStatus != SolvedConfigStatus.MustExistTryStart || pluginStatus != SolvedConfigStatus.OptionalTryStart) && ( !pluginData.IsRunning || stopLaunchedOptionals )) { // If a plugin has no service references and does not implement any services as well, // and that it is not asked to be started AND it is not running, we lock its value to false; _parseMap.Set( index, false ); // We do not set thereWillBeNoChange to false: there will ACTUALLY be no changes // since the plugin is NOT running. } else { // If a plugin has no service references and does not implement any services as well, // and that it is asked to be started OR is currently running, we lock its value to true; _parseMap.Set( index, true ); } pluginData.Locked = true; } index++; } // Trim the parseMap, to remove indexes that should have been filled by disabled plugins. Debug.Assert( _parseMap.Length >= disabledCount ); _parseMap.Length -= disabledCount; // If the parseMap has a length of 0, it means either that there are no plugins or that all plugins are disabled. // In either of these cases, we don't calculate any execution plan. But we still have a valid execution plan, it just doesn't have any plugins to launch. BitArray bestCombination = _parseMap; if( _parseMap.Length > 0 ) { int bestCost = Int32.MaxValue; double combinationsCount = Math.Pow( 2, _parseMap.Length - _mappingDic.Values.Count( ( e ) => { return e.Locked == true; } ) ); for( int i = 0; i < combinationsCount; i++ ) { int cost = ComputeCombination(stopLaunchedOptionals); Debug.Assert( cost >= 0 ); // Return if the cost is equal to 0 (no better solution). if( cost == 0 ) { return _lastBestPlan = GenerateExecutionPlan( _parseMap ); } if( cost < bestCost ) { bestCost = cost; bestCombination = (BitArray)_parseMap.Clone(); } GenerateNextCombination(); } // If there is no valid combination, we return an impossible plan and // we do not keep it as the LastBestPlan. if( bestCost == Int32.MaxValue ) return GenerateExecutionPlan( null ); } return _lastBestPlan = GenerateExecutionPlan( bestCombination ); }
/// <summary> /// Launches ComputeCombination for each "plugin status combination". /// Gets the lower cost among all the combinations generated. /// </summary> internal ExecutionPlan ObtainBestPlan(Dictionary <object, SolvedConfigStatus> finalConfig, bool stopLaunchedOptionals) { _finalConfig = finalConfig; if (_parseMap == null) { _parseMap = new BitArray(_discoverer.Plugins.Count); } else { _parseMap.Length = _discoverer.Plugins.Count; } _mappingArray.Clear(); _mappingDic.Clear(); int disabledCount = 0; // Locking plugins : // - If a plugin is disabled, it should not be launched, we do not add it to the map // - If a plugin needs to be started (MustExistAndRun), we lock its value to true // - If a plugin is the only implemention of a service and that this service has to be started, we lock this plugin's value to true // - If a plugin has no service references and does not implement any services as well, and that it is not asked to be // started and it is NOT running or it is running but stopLaunchedOptionals is true, we lock its value to false; int index = 0; foreach (IPluginInfo pI in _discoverer.Plugins) { // SolvedConfigStatus of the actual plugin. SolvedConfigStatus pluginStatus = _finalConfig.GetValueWithDefault(pI, SolvedConfigStatus.Optional); if (pluginStatus == SolvedConfigStatus.Disabled) { // If a plugin is disabled, it should not be launched, we do not add it to the map. disabledCount++; continue; } // SolvedConfigStatus of the implemented service if any. SolvedConfigStatus serviceStatus = pI.Service != null ? _finalConfig.GetValueWithDefault(pI.Service, SolvedConfigStatus.Optional) : SolvedConfigStatus.Optional; if (serviceStatus == SolvedConfigStatus.Disabled) { // If a plugin is disabled, it should not be launched, we do not add it to the map disabledCount++; continue; } // Here, we have no more disabled plugins. // Initializes a PluginData for this particular plugin and allocates // a new index in the bit array. Debug.Assert(index == _mappingArray.Count); PluginData pluginData = new PluginData(pI, index, IsPluginRunning(pI)); _mappingArray.Add(pluginData); _mappingDic.Add(pI, pluginData); if (pluginStatus == SolvedConfigStatus.MustExistAndRun || (serviceStatus == SolvedConfigStatus.MustExistAndRun && pI.Service.Implementations.Count == 1)) { // If a plugin needs to be started (MustExistAndRun), we lock its value to true. // If a plugin is the only implemention of a service and that this service has to be started, we lock this plugin's value to true. _parseMap.Set(index, true); pluginData.Locked = true; } else if (pI.Service == null && pI.ServiceReferences.Count == 0) // This plugin is independent. { // This is only an optimization. // The cost function gives a cost to the stop or the start of a plugin. When a plugin is independant like in this case, we lock its // status by taking into account the requirement (should it run? MustExistTryStart/OptionalTryStart) and its current status (IsPluginRunning) // and the stopLaunchedOptionals boolean. if ((pluginStatus != SolvedConfigStatus.MustExistTryStart || pluginStatus != SolvedConfigStatus.OptionalTryStart) && (!pluginData.IsRunning || stopLaunchedOptionals)) { // If a plugin has no service references and does not implement any services as well, // and that it is not asked to be started AND it is not running, we lock its value to false; _parseMap.Set(index, false); // We do not set thereWillBeNoChange to false: there will ACTUALLY be no changes // since the plugin is NOT running. } else { // If a plugin has no service references and does not implement any services as well, // and that it is asked to be started OR is currently running, we lock its value to true; _parseMap.Set(index, true); } pluginData.Locked = true; } index++; } // Trim the parseMap, to remove indexes that should have been filled by disabled plugins. Debug.Assert(_parseMap.Length >= disabledCount); _parseMap.Length -= disabledCount; // If the parseMap has a length of 0, it means either that there are no plugins or that all plugins are disabled. // In either of these cases, we don't calculate any execution plan. But we still have a valid execution plan, it just doesn't have any plugins to launch. BitArray bestCombination = _parseMap; if (_parseMap.Length > 0) { int bestCost = Int32.MaxValue; double combinationsCount = Math.Pow(2, _parseMap.Length - _mappingDic.Values.Count((e) => { return(e.Locked == true); })); for (int i = 0; i < combinationsCount; i++) { int cost = ComputeCombination(stopLaunchedOptionals); Debug.Assert(cost >= 0); // Return if the cost is equal to 0 (no better solution). if (cost == 0) { return(_lastBestPlan = GenerateExecutionPlan(_parseMap)); } if (cost < bestCost) { bestCost = cost; bestCombination = (BitArray)_parseMap.Clone(); } GenerateNextCombination(); } // If there is no valid combination, we return an impossible plan and // we do not keep it as the LastBestPlan. if (bestCost == Int32.MaxValue) { return(GenerateExecutionPlan(null)); } } return(_lastBestPlan = GenerateExecutionPlan(bestCombination)); }