Exemple #1
0
        /// <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));
        }