Пример #1
0
        /// <summary>
        ///     Assigns the data cooker to the appropriate pass(es) and block(s).
        /// </summary>
        /// <param name="scheduler">
        ///     The scheduler in which this node participates.
        /// </param>
        /// <param name="dependentPass">
        ///     The pass in which the dependent cooker that resulted in this call to <see cref="Schedule"/> runs.
        ///     This parameter may be null.
        /// </param>
        internal override void Schedule(ISourceDataCookerScheduler scheduler, SchedulingPass dependentPass)
        {
            if (dependentPass == null)
            {
                // An AsRequired cooker should never be scheduled by itself - but only when required by
                // another cooker.
                //
                return;
            }

            if (this.Passes.Contains(dependentPass))
            {
                // Already been scheduled for this pass
                return;
            }

            // change the starting block to match the block of the dependent cooker
            this.Pass = dependentPass;
            this.SetStatus(SchedulingStatus.NotScheduled);
            base.Schedule(scheduler, null);

            Debug.Assert(this.Status == SchedulingStatus.Scheduled);
            Debug.Assert(this.Pass != null);
            Debug.Assert(this.Block != null);

            this.Passes.Add(dependentPass);
        }
Пример #2
0
        /// <summary>
        ///     Assigns <see cref="PassIndex"/> and <see cref="PassBlockIndex"/>.
        /// </summary>
        /// <param name="scheduler">
        ///     The scheduler in which this node participates.
        /// </param>
        /// <param name="dependentPass">
        ///     The pass in which the dependent cooker that resulted in this call to <see cref="Schedule"/> runs.
        ///     This parameter may be null.
        /// </param>
        internal virtual void Schedule(ISourceDataCookerScheduler scheduler, SchedulingPass dependentPass)
        {
            Debug.Assert(!(scheduler is null));

            if (this.Status == SchedulingStatus.Scheduled)
            {
                return;
            }

            if (this.Status == SchedulingStatus.Scheduling)
            {
                throw new InvalidOperationException(
                          $"Source data cooker {nameof(this.DataCookerPath)} is involved in a circular dependency.");
            }

            this.Status = SchedulingStatus.Scheduling;

            // An AsRequired node will establish this before scheduling
            if (this.Pass == null)
            {
                this.Pass = scheduler.Pass0;
            }

            this.Block = this.Pass.Block0;

            var requiredCookerPaths = this.CookerDependencies.RequiredDataCookers ?? new List <DataCookerPath>();

            List <DataCookerSchedulingNode> dependencies;

            {
                // Sort dependencies so that AsRequired are done last. These need to know the final PassIndex
                // of this cooker before they can be scheduled.
                //

                var normalDependencies     = new List <DataCookerSchedulingNode>();
                var asRequiredDependencies = new List <DataCookerSchedulingNode>();

                foreach (var requiredDataCookerPath in requiredCookerPaths)
                {
                    var sourceParserId = requiredDataCookerPath.SourceParserId;
                    if (!StringComparer.Ordinal.Equals(sourceParserId, this.DataCooker.Path.SourceParserId))
                    {
                        throw new InvalidOperationException(
                                  $"A source data parser may not require data cookers from other source parsers: {this.DataCookerPath}");
                    }

                    var requiredCookerNode = scheduler.GetSchedulingNode(requiredDataCookerPath);
                    Debug.Assert(!(requiredCookerNode is null));

                    var productionStrategy = requiredCookerNode.SourceDataCookerDescriptor.DataProductionStrategy;
                    if (productionStrategy == DataProductionStrategy.AsRequired)
                    {
                        asRequiredDependencies.Add(requiredCookerNode);
                    }
                    else
                    {
                        normalDependencies.Add(requiredCookerNode);
                    }
                }

                if (this.SourceDataCookerDescriptor.DataProductionStrategy == DataProductionStrategy.AsRequired)
                {
                    if (normalDependencies.Any())
                    {
                        throw new InvalidOperationException(
                                  $"A source data cooker with a {nameof(DataProductionStrategy)} " +
                                  $"of {nameof(DataProductionStrategy.AsRequired)} cannot depend on source data " +
                                  $"cookers not using this same {nameof(DataProductionStrategy)}: {this.DataCooker.Path} " +
                                  $"depends on {normalDependencies[0].DataCookerPath}");
                    }
                }

                dependencies = normalDependencies.Concat(asRequiredDependencies).ToList();
            }

            // Make sure all of the dependencies are scheduled.
            foreach (var requiredCookerNode in dependencies)
            {
                requiredCookerNode.Schedule(scheduler, this.Pass);

                if (this.PassIndex > requiredCookerNode.PassIndex)
                {
                    continue;
                }

                if (this.CookerDependencies.DependencyTypes is null ||
                    !this.CookerDependencies.DependencyTypes.TryGetValue(
                        requiredCookerNode.DataCookerPath, out var dependencyType))
                {
                    dependencyType = DataCookerDependencyType.AlignedWithProductionStrategy;
                }

                var productionStrategy = requiredCookerNode.SourceDataCookerDescriptor.DataProductionStrategy;

                if (productionStrategy == DataProductionStrategy.AsRequired)
                {
                    if (dependencyType != DataCookerDependencyType.AlignedWithProductionStrategy)
                    {
                        // These source cookers will automatically run in any/all stages where a dependent source
                        // cooker is scheduled. Given that, only DataCookerDependencyType.AlignedWithProductionStrategy
                        // make sense.
                        //

                        throw new InvalidOperationException(
                                  $"A SourceCooker whose {nameof(DataProductionStrategy)} is " +
                                  $"{nameof(DataProductionStrategy.AsRequired)} can only be consumed using " +
                                  $"{nameof(DataCookerDependencyType.AlignedWithProductionStrategy)}");
                    }
                }

                var oldPassIndex = this.Pass.Index;
                if (requiredCookerNode.PassIndex > this.PassIndex)
                {
                    this.Pass = requiredCookerNode.Pass;
                }

                if (productionStrategy == DataProductionStrategy.PostSourceParsing)
                {
                    if (dependencyType == DataCookerDependencyType.AlignedWithProductionStrategy)
                    {
                        if (requiredCookerNode.PassIndex == int.MaxValue)
                        {
                            throw new InvalidOperationException(
                                      "The number of required passes through the data source exceeds maximum allowed value.");
                        }

                        if (requiredCookerNode.Pass.Next == null)
                        {
                            requiredCookerNode.Pass.CreateNext();
                        }
                        Debug.Assert(requiredCookerNode.Pass.Next != null);

                        // this cannot take place in the same pass as the required data cooker
                        if (requiredCookerNode.Pass.Next.Index > this.PassIndex)
                        {
                            this.Pass = requiredCookerNode.Pass.Next;
                        }
                    }
                }

                if (this.PassIndex > oldPassIndex)
                {
                    // If we've moved into a higher source pass, then the block
                    // should reset to zero, or we could end up in an invalid state.
                    //
                    this.Block = this.Pass.Block0;
                }

                if (this.PassIndex == requiredCookerNode.PassIndex)
                {
                    // This should be set at least to the requiredCooker, in case that cooker has a
                    // required cooker.
                    //
                    if (this.PassBlockIndex < requiredCookerNode.PassBlockIndex)
                    {
                        this.Block = requiredCookerNode.Block;
                    }

                    if (dependencyType == DataCookerDependencyType.AsConsumed ||
                        productionStrategy == DataProductionStrategy.AsRequired ||
                        (dependencyType == DataCookerDependencyType.AlignedWithProductionStrategy &&
                         productionStrategy == DataProductionStrategy.AsConsumed))
                    {
                        // if this takes place in the same pass as the required cooker, just make it come after
                        // the required cooker in the pass
                        //
                        if (requiredCookerNode.Block.Next == null)
                        {
                            this.Block = requiredCookerNode.Block.CreateNext();
                        }
                        else if (this.PassBlockIndex < requiredCookerNode.Block.Next.Index)
                        {
                            this.Block = requiredCookerNode.Block.Next;
                        }
                    }
                }
            }

            this.Block.Nodes.Add(this);
            this.Status = SchedulingStatus.Scheduled;
        }