private void ExecuteCommonParseActivities(IReadOnlyCollection <QualifiedModuleName> toParse, IReadOnlyCollection <QualifiedModuleName> toReresolveReferencesInput, CancellationToken token) { token.ThrowIfCancellationRequested(); var toReresolveReferences = new HashSet <QualifiedModuleName>(); toReresolveReferences.UnionWith(toReresolveReferencesInput); token.ThrowIfCancellationRequested(); _parserStateManager.SetModuleStates(toParse, ParserState.Pending, token); token.ThrowIfCancellationRequested(); _parserStateManager.SetStatusAndFireStateChanged(this, ParserState.LoadingReference, token); token.ThrowIfCancellationRequested(); _parsingStageService.SyncComReferences(State.Projects, token); if (_parsingStageService.LastSyncOfCOMReferencesLoadedReferences || _parsingStageService.COMReferencesUnloadedUnloadedInLastSync.Any()) { var unloadedReferences = _parsingStageService.COMReferencesUnloadedUnloadedInLastSync; var additionalModulesToBeReresolved = OtherModulesReferencingAnyNotToBeParsed(unloadedReferences.ToHashSet().AsReadOnly(), toParse); toReresolveReferences.UnionWith(additionalModulesToBeReresolved); _parserStateManager.SetModuleStates(additionalModulesToBeReresolved, ParserState.ResolvingReferences, token); ClearModuleToModuleReferences(unloadedReferences); RefreshDeclarationFinder(); } token.ThrowIfCancellationRequested(); _parsingStageService.LoadBuitInDeclarations(); if (_parsingStageService.LastLoadOfBuiltInDeclarationsLoadedDeclarations) { RefreshDeclarationFinder(); } token.ThrowIfCancellationRequested(); IReadOnlyCollection <QualifiedModuleName> toResolveReferences; if (!toParse.Any()) { toResolveReferences = toReresolveReferences.AsReadOnly(); } else { toResolveReferences = ModulesForWhichToResolveReferences(toParse, toReresolveReferences); token.ThrowIfCancellationRequested(); PerformPreParseCleanup(toResolveReferences, token); token.ThrowIfCancellationRequested(); _parserStateManager.SetModuleStates(toParse, ParserState.Parsing, token); token.ThrowIfCancellationRequested(); _parsingStageService.ParseModules(toParse, token); if (token.IsCancellationRequested || State.Status >= ParserState.Error) { throw new OperationCanceledException(token); } _parserStateManager.EvaluateOverallParserState(token); if (token.IsCancellationRequested || State.Status >= ParserState.Error) { throw new OperationCanceledException(token); } _parserStateManager.SetModuleStates(toParse, ParserState.ResolvingDeclarations, token); token.ThrowIfCancellationRequested(); _parsingStageService.ResolveDeclarations(toParse, token); } if (token.IsCancellationRequested || State.Status >= ParserState.Error) { throw new OperationCanceledException(token); } //We need to refresh the DeclarationFinder before the handlers for ResolvedDeclarations run no matter //whether we parsed or resolved something because modules not referenced by any remeining module might //have been removed. E.g. the CodeExplorer needs this update. RefreshDeclarationFinder(); token.ThrowIfCancellationRequested(); //Explicitly setting the overall state here guarantees that the handlers attached //to the state change to ResolvedDeclarations always run, provided there is no error. State.SetStatusAndFireStateChanged(this, ParserState.ResolvedDeclarations); if (token.IsCancellationRequested || State.Status >= ParserState.Error) { throw new OperationCanceledException(token); } _parserStateManager.SetModuleStates(toResolveReferences, ParserState.ResolvingReferences, token); token.ThrowIfCancellationRequested(); _parsingStageService.ResolveReferences(toResolveReferences, token); if (token.IsCancellationRequested || State.Status >= ParserState.Error) { throw new OperationCanceledException(token); } RefreshDeclarationFinder(); token.ThrowIfCancellationRequested(); //At this point all modules should either be in the Ready state or in an error state. //This is the point where the change of the overall state to Ready is triggered on the success path. _parserStateManager.EvaluateOverallParserState(token); token.ThrowIfCancellationRequested(); }
public void SuspendRequested(object sender, RubberduckStatusSuspendParserEventArgs e) { if (ParsingSuspendLock.IsReadLockHeld) { e.Result = SuspensionResult.UnexpectedError; const string errorMessage = "A suspension action was attempted while a read lock was held. This indicates a bug in the code logic as suspension should not be requested from same thread that has a read lock."; Logger.Error(errorMessage); #if DEBUG Debug.Assert(false, errorMessage); #endif return; } object parseRequestor = null; try { if (!ParsingSuspendLock.TryEnterWriteLock(e.MillisecondsTimeout)) { e.Result = SuspensionResult.TimedOut; return; } lock (SuspendStackSyncObject) { _isSuspended = true; } var originalStatus = State.Status; if (!e.AllowedRunStates.Contains(originalStatus)) { e.Result = SuspensionResult.IncompatibleState; return; } _parserStateManager.SetStatusAndFireStateChanged(e.Requestor, ParserState.Busy, CancellationToken.None); e.BusyAction.Invoke(); } catch { e.Result = SuspensionResult.UnexpectedError; throw; } finally { lock (SuspendStackSyncObject) { _isSuspended = false; if (_requestorStack.TryPop(out var lastRequestor)) { _requestorStack.Clear(); parseRequestor = lastRequestor; } // Though there were no reparse requests, we need to reset the state before we release the // write lock to avoid introducing discrepancy in the parser state due to readers being // blocked. Any reparse requests must be done outside the write lock; see further below. if (parseRequestor == null) { // We cannot make any assumptions about the original state, nor do we know // anything about resuming the previous state, so we must delegate the state // evaluation to the state manager. _parserStateManager.EvaluateOverallParserState(CancellationToken.None); } } if (ParsingSuspendLock.IsWriteLockHeld) { ParsingSuspendLock.ExitWriteLock(); } if (e.Result == SuspensionResult.Pending) { e.Result = SuspensionResult.Completed; } } // Any reparse requests must be done outside the write lock to avoid deadlocks if (parseRequestor != null) { BeginParse(parseRequestor); } }