Exemplo n.º 1
0
        /// <summary>
        /// Discover external module dependencies and enqueue them if necessary
        /// </summary>
        /// <remarks>
        /// The parsed file gets updated with the external module references as they are discovered.
        /// </remarks>
        private async Task <Possible <bool> > EnqueueSpecDependenciesIfAny(ModuleUnderConstruction owningModule, ISourceFile parsedFile)
        {
            var allSpecifiers = m_moduleReferenceResolver.GetExternalModuleReferences(parsedFile);

            foreach (var moduleName in allSpecifiers)
            {
                // Get the module definition from the resolver and enqueue it. Since we don't deal with versions yet at the
                // import level, there should be exactly one module definition with that name
                var maybeModuleDefinition = await m_workspaceProvider.FindUniqueModuleDefinitionWithName(moduleName);

                if (!maybeModuleDefinition.Succeeded)
                {
                    return(maybeModuleDefinition.Failure);
                }

                var moduleDefinition = maybeModuleDefinition.Result;

                // Since the referenced module has been found, we update the parsed file with the reference. This
                // information is later used by the checker.
                if (!m_moduleReferenceResolver.TryUpdateExternalModuleReference(parsedFile, moduleDefinition, out var failure))
                {
                    return(failure);
                }

                // Update the owning module advertising that the found module was referenced
                owningModule.AddReferencedModule(moduleDefinition.Descriptor, moduleName.ReferencedFrom);

                EnqueueModuleForParsing(moduleDefinition);
            }

            return(true);
        }
Exemplo n.º 2
0
        private void CheckForCompletionAndSignalQueueIfDone(ModuleUnderConstruction owningModule)
        {
            // Now that all dependencies of the owning module have been scheduled,
            // we check if the module for which we just added a spec is complete
            if (owningModule.IsModuleFirstTimeComplete())
            {
                var moduleId = owningModule.Definition.Descriptor;

                // We create a parsed module out of a module under construction.
                // Observe many threads can try to create the same completed module. But that's ok, the method is thread safe
                // and always return the same instance.
                // It is important to notice that we should add this module to m_modulesAlreadyParsed before removing it from
                // m_modulesToBeParsed. This is so no producer will think the module is not there at any point in time (which
                // would cause the producer to try to schedule that module again)
                var success = owningModule.TryCreateCompletedModule(m_moduleReferenceResolver, m_workspaceConfiguration, m_queueOptions.CancelOnFirstFailure, out ParsedModule module, out Failure[] failures);
                if (success)
                {
                    m_modulesAlreadyParsed[moduleId] = module;

                    // Now the module is completed and we can bind the changed file as well.
                    var modifiedSourceFile = owningModule.GetSourceFileForInjectingQualifiers();
                    if (modifiedSourceFile != null)
                    {
                        ScheduleSourceFileBinding(new ParsedSpecWithOwningModule(parsedFile: modifiedSourceFile, owningModule: owningModule.Definition));
                    }
                }
                else
                {
                    // This is a bogus module we put here when failures occurr. This guarantees that modules
                    // are not scheduled more than once (detailed reasons above). When there are failures
                    // we want to report them only once, so leave that to whoever successfully removes
                    // the module from m_modulesToBeParsed to report them.
                    // Observe that this (empty) module will be left in case of failures, but that's not
                    // likely to be a problem
                    m_modulesAlreadyParsed[moduleId] = new ParsedModule(owningModule.Definition, hasFailures: true);
                }

                // If removing fails, that means that somebody else successfully removed the module, so we do nothing
                // If removing succeeds, we are responsible for signaling if the queue is done
                if (m_modulesToBeParsed.TryRemove(moduleId, out ModuleUnderConstruction dummy))
                {
                    if (!success)
                    {
                        ReportFailureAndCancelParsingIfNeeded(failures);
                    }

                    // Now we decide if we are done with parsing the transitive closure.
                    // This happens when the set of modules to be parsed is empty
                    // and EnqueuingCompleted has been called.
                    // Observe that it is important that we first add all dependencies to be parsed
                    // before removing a completed module. This ensures
                    // that if anybody sees an empty collection, we are done for sure
                    if (m_modulesToBeParsed.IsEmpty && m_enqueingComplete)
                    {
                        cancellationTokenChain.Dispose();
                        m_parseQueue.Complete();
                    }
                }
            }
        }
Exemplo n.º 3
0
        /// <summary>
        /// Creates a workspace from all known modules in an incremental way
        /// </summary>
        /// <remarks>
        /// It reuses already parsed modules so they don't get recomputed again
        /// </remarks>
        public async Task <Workspace> CreateIncrementalWorkspaceForAllKnownModulesAsync(
            IEnumerable <ParsedModule> parsedModules,
            ModuleUnderConstruction moduleUnderConstruction,
            IEnumerable <Failure> failures,
            [CanBeNull] ParsedModule preludeModule)
        {
            var maybeModuleDefinitions = await GetModuleDefinitionsForAllResolversAsync();

            if (!maybeModuleDefinitions.Succeeded)
            {
                return(Failure(maybeModuleDefinitions.Failure));
            }

            var moduleDefinitions = maybeModuleDefinitions.Result;

            ModuleDefinition preludeDefinition;

            if (preludeModule != null)
            {
                // Need to add prelude to a list of parsed modules if awailable
                var parsedModuleList = parsedModules.ToList();
                parsedModuleList.Add(preludeModule);
                parsedModules     = parsedModuleList;
                preludeDefinition = preludeModule.Definition;
            }
            else
            {
                var possiblePreludeDefinition = await TryGetPreludeModuleDefinitionAsync();

                if (!possiblePreludeDefinition.Succeeded)
                {
                    return(Failure(possiblePreludeDefinition.Failure));
                }

                preludeDefinition = possiblePreludeDefinition.Result;
                moduleDefinitions.Add(preludeDefinition);
            }

            if (!WorkspaceValidator.ValidateModuleDefinitions(moduleDefinitions, PathTable, out var validationFailures))
            {
                return(Failure(validationFailures));
            }

            var queue = ModuleParsingQueue.CreateIncrementalQueue(
                this,
                Configuration,
                m_moduleReferenceResolver,
                preludeDefinition,
                GetConfigurationModule(),
                parsedModules,
                failures);

            return(await queue.ProcessIncrementalAsync(moduleUnderConstruction));
        }
Exemplo n.º 4
0
 /// <inheritdoc />
 public async Task <Workspace> CreateIncrementalWorkspaceForAllKnownModulesAsync(
     IEnumerable <ParsedModule> parsedModules,
     ModuleUnderConstruction moduleUnderConstruction,
     IEnumerable <Failure> failures,
     ParsedModule preludeModule)
 {
     using (m_statistics.EndToEndParsing.Start())
     {
         return(await m_decoratee.CreateIncrementalWorkspaceForAllKnownModulesAsync(parsedModules, moduleUnderConstruction, failures, preludeModule));
     }
 }
Exemplo n.º 5
0
        /// <summary>
        /// Process a partially constructed module
        /// </summary>
        public Task <Workspace> ProcessIncrementalAsync(ModuleUnderConstruction moduleUnderConstruction)
        {
            Contract.Requires(moduleUnderConstruction != null);
            Contract.Requires(!moduleUnderConstruction.IsModuleComplete());

            // Add the module to be parsed as is
            var definition = moduleUnderConstruction.Definition;

            Contract.Assert(!m_modulesToBeParsed.ContainsKey(definition.Descriptor));
            m_modulesToBeParsed[definition.Descriptor] = moduleUnderConstruction;

            // Now schedule all the pending specs
            // It is extremely important to get specs in order to preserve determinism.
            var pendingSpecs = moduleUnderConstruction.GetPendingSpecPathsOrderedByPath(m_workspaceProvider.PathTable);

            foreach (var spec in pendingSpecs)
            {
                m_parseQueue.Post(new SpecWithOwningModule(spec.path, definition));
            }

            return(EnqueuingFinishedAndWaitForCompletion());
        }
Exemplo n.º 6
0
        /// <summary>
        /// Enqueues all the specs of <param name="moduleDefinition"/> for parsing. Actual enqueuing only happens if the module has not been
        /// completed yet, nor already scheduled.
        /// </summary>
        /// <remarks>
        /// This method can only be called before calling <see cref="EnqueuingCompleted"/>
        /// </remarks>
        private void EnqueueModuleForParsing(ModuleDefinition moduleDefinition)
        {
            if (m_modulesAlreadyParsed.ContainsKey(moduleDefinition.Descriptor) ||
                m_modulesToBeParsed.ContainsKey(moduleDefinition.Descriptor))
            {
                return;
            }

            // If the module has no specs, it is already parsed
            if (moduleDefinition.Specs.Count == 0)
            {
                m_modulesAlreadyParsed[moduleDefinition.Descriptor] = new ParsedModule(moduleDefinition);
                return;
            }

            // Create a module with no specs and add it to the modules-to-be-parsed collection
            var module = new ModuleUnderConstruction(moduleDefinition);

            if (!m_modulesToBeParsed.TryAdd(moduleDefinition.Descriptor, module))
            {
                // If the module has already been added by someone else, we just declare the enqueue successful
                return;
            }

            // Now schedule all the specs
            foreach (var spec in moduleDefinition.Specs)
            {
                // Constructor of this class ensures that queue is unbounded.
                // In cases of bounded queue, posting to a queue can lead to a deadlock
                // because ActionBlock infrastructure removes item from the queue
                // only when call back that processes that element finishes its execution.
                // But in this case, callback can add item to the queue which will lead to a deadlock
                // when queue is full.
                m_parseQueue.Post(new SpecWithOwningModule(spec, moduleDefinition));
            }
        }