Skip to content
This repository was archived by the owner on Apr 14, 2022. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions src/Analysis/Ast/Impl/Analyzer/Evaluation/ExpressionEval.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,6 @@
// See the Apache Version 2.0 License for specific language governing
// permissions and limitations under the License.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using Microsoft.Python.Analysis.Analyzer.Symbols;
using Microsoft.Python.Analysis.Diagnostics;
using Microsoft.Python.Analysis.Modules;
Expand All @@ -27,6 +23,9 @@
using Microsoft.Python.Core.Logging;
using Microsoft.Python.Core.Text;
using Microsoft.Python.Parsing.Ast;
using System;
using System.Collections.Generic;
using System.Diagnostics;

namespace Microsoft.Python.Analysis.Analyzer.Evaluation {
/// <summary>
Expand Down
87 changes: 72 additions & 15 deletions src/Analysis/Ast/Impl/Analyzer/PythonAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -197,43 +197,73 @@ private void AnalyzeDocument(AnalysisModuleKey key, PythonAnalyzerEntry entry, I
_analysisCompleteEvent.Reset();
_log?.Log(TraceEventType.Verbose, $"Analysis of {entry.Module.Name}({entry.Module.ModuleType}) queued");

var snapshot = _dependencyResolver.NotifyChanges(key, entry, dependencies);
var graphVersion = _dependencyResolver.ChangeValue(key, entry, dependencies);

lock (_syncObj) {
if (_version > snapshot.Version) {
if (_version > graphVersion) {
return;
}

_version = snapshot.Version;
_version = graphVersion;
_currentSession?.Cancel();
}

if (snapshot.MissingKeys.Count > 0) {
LoadMissingDocuments(entry.Module.Interpreter, snapshot.MissingKeys);
}
UpdateDependentEntriesDepth(entry, dependencies, graphVersion);

if (TryCreateSession(snapshot, entry, cancellationToken, out var session)) {
if (TryCreateSession(graphVersion, entry, cancellationToken, out var session)) {
session.Start(true);
}
}

private bool TryCreateSession(DependencyGraphSnapshot<AnalysisModuleKey, PythonAnalyzerEntry> snapshot, PythonAnalyzerEntry entry, CancellationToken cancellationToken, out PythonAnalyzerSession session) {
private void UpdateDependentEntriesDepth(PythonAnalyzerEntry entry, ImmutableArray<AnalysisModuleKey> dependentKeys, int graphVersion) {
if (dependentKeys.Count == 0) {
return;
}

var dependentEntries = new List<PythonAnalyzerEntry>();
lock (_syncObj) {
if (_version > graphVersion) {
return;
}

foreach (var key in dependentKeys) {
if (_analysisEntries.TryGetValue(key, out var value)) {
dependentEntries.Add(value);
}
}
}

if (dependentEntries.Count == 0) {
return;
}

var depth = entry.Depth;
foreach (var dependentEntry in dependentEntries) {
dependentEntry.SetDepth(graphVersion, depth);
}
}

private bool TryCreateSession(int graphVersion, PythonAnalyzerEntry entry, CancellationToken cancellationToken, out PythonAnalyzerSession session) {
var analyzeUserModuleOutOfOrder = false;
lock (_syncObj) {
if (_currentSession != null) {
if (_currentSession.Version > snapshot.Version || _nextSession != null && _nextSession.Version > snapshot.Version) {
if (_currentSession.Version > graphVersion || _nextSession != null && _nextSession.Version > graphVersion) {
session = null;
return false;
}

analyzeUserModuleOutOfOrder = !_currentSession.IsCompleted && entry.IsUserModule && _currentSession.AffectedEntriesCount >= _maxTaskRunning;
if (_version > snapshot.Version && analyzeUserModuleOutOfOrder) {
if (_version > graphVersion && analyzeUserModuleOutOfOrder) {
session = CreateSession(null, entry, cancellationToken);
return true;
}
}
}

if (!_dependencyResolver.TryCreateWalker(snapshot.Version, out var walker)) {
var snapshot = _dependencyResolver.CurrentGraphSnapshot;
LoadMissingDocuments(entry.Module.Interpreter, snapshot.MissingKeys);

if (!_dependencyResolver.TryCreateWalker(snapshot, out var walker)) {
session = null;
return false;
}
Expand All @@ -254,7 +284,6 @@ private bool TryCreateSession(DependencyGraphSnapshot<AnalysisModuleKey, PythonA
return true;
}

_currentSession.Cancel();
_nextSession = session = CreateSession(walker, analyzeUserModuleOutOfOrder ? entry : null, cancellationToken);
return analyzeUserModuleOutOfOrder;
}
Expand All @@ -278,9 +307,37 @@ private PythonAnalyzerSession CreateSession(IDependencyChainWalker<AnalysisModul
=> new PythonAnalyzerSession(_services, _progress, _analysisCompleteEvent, _startNextSession, _disposeToken.CancellationToken, cancellationToken, walker, _version, entry);

private void LoadMissingDocuments(IPythonInterpreter interpreter, ImmutableArray<AnalysisModuleKey> missingKeys) {
foreach (var (moduleName, _, isTypeshed) in missingKeys) {
if (missingKeys.Count == 0) {
return;
}

var foundKeys = ImmutableArray<AnalysisModuleKey>.Empty;
foreach (var missingKey in missingKeys) {
lock (_syncObj) {
if (_analysisEntries.TryGetValue(missingKey, out _)) {
continue;
}
}

var (moduleName, _, isTypeshed) = missingKey;
var moduleResolution = isTypeshed ? interpreter.TypeshedResolution : interpreter.ModuleResolution;
moduleResolution.GetOrLoadModule(moduleName);
var module = moduleResolution.GetOrLoadModule(moduleName);
if (module != null && module.ModuleType != ModuleType.Unresolved) {
foundKeys = foundKeys.Add(missingKey);
}
}

if (foundKeys.Count > 0) {
foreach (var foundKey in foundKeys) {
PythonAnalyzerEntry entry;
lock (_syncObj) {
if (!_analysisEntries.TryGetValue(foundKey, out entry)) {
continue;
}
}

_dependencyResolver.TryAddValue(foundKey, entry, ImmutableArray<AnalysisModuleKey>.Empty);
}
}
}
}
Expand Down
29 changes: 27 additions & 2 deletions src/Analysis/Ast/Impl/Analyzer/PythonAnalyzerEntry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ internal sealed class PythonAnalyzerEntry {
private HashSet<AnalysisModuleKey> _analysisDependencies;
private int _bufferVersion;
private int _analysisVersion;
private int _depth;

public IPythonModule Module {
get {
Expand Down Expand Up @@ -74,11 +75,21 @@ public int AnalysisVersion {
}
}

public int Depth {
get {
lock (_syncObj) {
return _depth;
}
}
}

public bool NotAnalyzed => PreviousAnalysis is EmptyAnalysis;

public PythonAnalyzerEntry(EmptyAnalysis emptyAnalysis) {
_previousAnalysis = emptyAnalysis;
_module = emptyAnalysis.Document;
_isUserModule = emptyAnalysis.Document.ModuleType == ModuleType.User;
_depth = _isUserModule ? 0 : -1;

_bufferVersion = -1;
_analysisVersion = 0;
Expand All @@ -92,10 +103,24 @@ public bool IsValidVersion(int version, out IPythonModule module, out PythonAst
lock (_syncObj) {
module = _module;
ast = _ast;
if (ast == null || module == null) {
return false;
}

return _previousAnalysis is EmptyAnalysis || _isUserModule || _analysisVersion <= version;
}
}

public void SetDepth(int version, int depth) {
lock (_syncObj) {
if (_analysisVersion > version) {
return;
}

_depth = _depth == -1 ? depth : Math.Min(_depth, depth);
}
}

public void TrySetAnalysis(IDocumentAnalysis analysis, int version) {
lock (_syncObj) {
if (_previousAnalysis is EmptyAnalysis) {
Expand Down Expand Up @@ -139,7 +164,7 @@ public void TryCancel(OperationCanceledException oce, int version) {

public void Invalidate(int analysisVersion) {
lock (_syncObj) {
if (_analysisVersion >= analysisVersion) {
if (_analysisVersion >= analysisVersion || !_analysisTcs.Task.IsCompleted) {
return;
}

Expand Down Expand Up @@ -176,7 +201,6 @@ public bool Invalidate(ImmutableArray<IPythonModule> analysisDependencies, int a
return false;
}

UpdateAnalysisTcs(analysisVersion);
if (_analysisDependencies == null) {
_analysisDependencies = dependenciesHashSet;
} else {
Expand All @@ -187,6 +211,7 @@ public bool Invalidate(ImmutableArray<IPythonModule> analysisDependencies, int a
}
}

UpdateAnalysisTcs(analysisVersion);
dependencies = _parserDependencies != null
? ImmutableArray<AnalysisModuleKey>.Create(_parserDependencies.Union(_analysisDependencies).ToArray())
: ImmutableArray<AnalysisModuleKey>.Create(_analysisDependencies);
Expand Down
18 changes: 10 additions & 8 deletions src/Analysis/Ast/Impl/Analyzer/PythonAnalyzerSession.cs
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ private void LogResults(double elapsed, int originalRemaining, int remaining, in
if (_log == null) {
return;
}

if (remaining == 0) {
_log.Log(TraceEventType.Verbose, $"Analysis version {version} of {originalRemaining} entries has been completed in {elapsed} ms.");
} else if (remaining < originalRemaining) {
Expand All @@ -208,8 +208,8 @@ private async Task<int> AnalyzeAffectedEntriesAsync(Stopwatch stopWatch, Cancell
var remaining = 0;
var ace = new AsyncCountdownEvent(0);

bool isCanceled;
while ((node = await _walker.GetNextAsync(cancellationToken)) != null) {
bool isCanceled;
lock (_syncObj) {
isCanceled = _isCanceled;
}
Expand All @@ -229,16 +229,18 @@ private async Task<int> AnalyzeAffectedEntriesAsync(Stopwatch stopWatch, Cancell

await ace.WaitAsync(cancellationToken);

if (_walker.MissingKeys.All(k => k.IsTypeshed)) {
Interlocked.Exchange(ref _runningTasks, 0);
bool isCanceled;
lock (_syncObj) {
isCanceled = _isCanceled;
}
lock (_syncObj) {
isCanceled = _isCanceled;
}

if (_walker.MissingKeys.Count == 0 || _walker.MissingKeys.All(k => k.IsTypeshed)) {
Interlocked.Exchange(ref _runningTasks, 0);

if (!isCanceled) {
_analysisCompleteEvent.Set();
}
} else if (!isCanceled && _log != null && _log.LogLevel >= TraceEventType.Verbose) {
_log?.Log(TraceEventType.Verbose, $"Missing keys: {string.Join(", ", _walker.MissingKeys)}");
}

return remaining;
Expand Down
Loading