14
14
// permissions and limitations under the License.
15
15
16
16
using System ;
17
- using System . ComponentModel ;
18
17
using System . Diagnostics ;
19
18
using System . Linq ;
20
19
using System . Threading ;
25
24
using Microsoft . Python . Analysis . Modules ;
26
25
using Microsoft . Python . Analysis . Types ;
27
26
using Microsoft . Python . Core ;
28
- using Microsoft . Python . Core . Diagnostics ;
29
27
using Microsoft . Python . Core . Logging ;
30
28
using Microsoft . Python . Core . Services ;
31
29
using Microsoft . Python . Parsing . Ast ;
@@ -34,21 +32,21 @@ namespace Microsoft.Python.Analysis.Analyzer {
34
32
internal sealed class PythonAnalyzerSession {
35
33
private readonly int _maxTaskRunning = Environment . ProcessorCount ;
36
34
private readonly object _syncObj = new object ( ) ;
37
- private readonly Action < Task > _startNextSession ;
38
35
36
+ private readonly IDependencyChainWalker < AnalysisModuleKey , PythonAnalyzerEntry > _walker ;
37
+ private readonly PythonAnalyzerEntry _entry ;
39
38
private readonly CancellationTokenSource _cts ;
39
+ private readonly Action < Task > _startNextSession ;
40
40
private readonly IServiceManager _services ;
41
41
private readonly AsyncManualResetEvent _analysisCompleteEvent ;
42
42
private readonly IDiagnosticsService _diagnosticsService ;
43
43
private readonly IProgressReporter _progress ;
44
44
private readonly IPythonAnalyzer _analyzer ;
45
45
private readonly ILogger _log ;
46
46
47
- private State _state = State . NotStarted ;
48
- private IDependencyChainWalker < AnalysisModuleKey , PythonAnalyzerEntry > _walker ;
47
+ private State _state ;
48
+ private bool _isCanceled ;
49
49
private int _runningTasks ;
50
- private int _version ;
51
- private PythonAnalyzerSession _nextSession ;
52
50
53
51
public bool IsCompleted {
54
52
get {
@@ -58,21 +56,25 @@ public bool IsCompleted {
58
56
}
59
57
}
60
58
61
- public int Version => _version ;
59
+ public int Version { get ; }
62
60
63
61
public PythonAnalyzerSession ( IServiceManager services ,
64
62
IProgressReporter progress ,
65
63
AsyncManualResetEvent analysisCompleteEvent ,
64
+ Action < Task > startNextSession ,
66
65
CancellationToken analyzerToken ,
67
66
CancellationToken sessionToken ,
68
67
IDependencyChainWalker < AnalysisModuleKey , PythonAnalyzerEntry > walker ,
69
- int version ) {
68
+ int version ,
69
+ PythonAnalyzerEntry entry ) {
70
70
71
71
_services = services ;
72
72
_analysisCompleteEvent = analysisCompleteEvent ;
73
- _startNextSession = StartNextSession ;
74
- _version = version ;
73
+ _startNextSession = startNextSession ;
74
+ Version = version ;
75
75
_walker = walker ;
76
+ _entry = entry ;
77
+ _state = State . NotStarted ;
76
78
_cts = CancellationTokenSource . CreateLinkedTokenSource ( analyzerToken , sessionToken ) ;
77
79
78
80
_diagnosticsService = _services . GetService < IDiagnosticsService > ( ) ;
@@ -81,119 +83,64 @@ public PythonAnalyzerSession(IServiceManager services,
81
83
_log = _services . GetService < ILogger > ( ) ;
82
84
}
83
85
84
- public void Start ( PythonAnalyzerSession previousSession , PythonAnalyzerEntry entry ) {
85
- IDependencyChainWalker < AnalysisModuleKey , PythonAnalyzerEntry > walker ;
86
- State previousSessionState ;
87
-
86
+ public void Start ( bool analyzeEntry ) {
88
87
lock ( _syncObj ) {
89
- walker = _walker ;
90
88
if ( _state != State . NotStarted ) {
91
- _cts . Dispose ( ) ;
89
+ analyzeEntry = false ;
90
+ } else if ( _state == State . Completed ) {
92
91
return ;
93
- }
94
-
95
- previousSessionState = previousSession ? . CancelOrSchedule ( this , _version ) ?? State . Completed ;
96
- if ( previousSessionState == State . Completed ) {
92
+ } else {
97
93
_state = State . Started ;
98
- _walker = null ;
99
94
}
100
95
}
101
96
102
- switch ( previousSessionState ) {
103
- case State . Started when entry . IsUserModule :
104
- StartAnalysis ( entry , walker . Version ) ;
105
- break ;
106
- case State . Completed :
107
- Start ( walker ) ;
108
- break ;
97
+ if ( analyzeEntry && _entry != null ) {
98
+ Task . Run ( ( ) => Analyze ( _entry , _walker . Version , _cts . Token ) , _cts . Token ) . DoNotWait ( ) ;
99
+ } else {
100
+ StartAsync ( _walker ) . ContinueWith ( _startNextSession ) . DoNotWait ( ) ;
109
101
}
110
102
}
111
103
112
- private void StartNextSession ( Task task ) {
113
- PythonAnalyzerSession nextSession ;
104
+ public void Cancel ( ) {
114
105
lock ( _syncObj ) {
115
- _walker = null ;
116
- nextSession = _nextSession ;
117
- _nextSession = null ;
106
+ _isCanceled = true ;
118
107
}
119
-
120
- nextSession ? . TryStart ( ) ;
121
108
}
122
109
123
- private State CancelOrSchedule ( PythonAnalyzerSession nextSession , int version ) {
124
- lock ( _syncObj ) {
125
- Check . InvalidOperation ( _nextSession == null ) ;
126
- Check . InvalidOperation ( nextSession != null ) ;
127
- switch ( _state ) {
128
- case State . NotStarted :
129
- _state = State . Completed ;
130
- break ;
131
- case State . Started :
132
- _nextSession = nextSession ;
133
- Interlocked . Exchange ( ref _version , version ) ;
134
- break ;
135
- case State . Completed :
136
- break ;
137
- default :
138
- throw new ArgumentOutOfRangeException ( ) ;
139
- }
140
-
141
- return _state ;
142
- }
143
- }
144
-
145
- private void TryStart ( ) {
146
- IDependencyChainWalker < AnalysisModuleKey , PythonAnalyzerEntry > walker = default ;
147
- lock ( _syncObj ) {
148
- if ( _state == State . NotStarted ) {
149
- _state = State . Started ;
150
- walker = _walker ;
151
- }
152
-
153
- _walker = null ;
154
- }
155
-
156
- if ( walker != default ) {
157
- Start ( walker ) ;
158
- }
159
- }
160
-
161
- private void Start ( IDependencyChainWalker < AnalysisModuleKey , PythonAnalyzerEntry > walker ) => StartAsync ( walker ) . ContinueWith ( _startNextSession ) . DoNotWait ( ) ;
162
-
163
110
private async Task StartAsync ( IDependencyChainWalker < AnalysisModuleKey , PythonAnalyzerEntry > walker ) {
164
- int version ;
165
111
lock ( _syncObj ) {
166
- version = _version ;
167
112
var notAnalyzed = walker . AffectedValues . Count ( e => e . NotAnalyzed ) ;
168
113
169
- if ( version > walker . Version && notAnalyzed < _maxTaskRunning ) {
114
+ if ( _isCanceled && notAnalyzed < _maxTaskRunning ) {
170
115
_state = State . Completed ;
171
116
_cts . Dispose ( ) ;
172
117
return ;
173
118
}
174
119
}
175
-
120
+
121
+ var cancellationToken = _cts . Token ;
176
122
var stopWatch = Stopwatch . StartNew ( ) ;
177
123
foreach ( var affectedEntry in walker . AffectedValues ) {
178
- affectedEntry . Invalidate ( version ) ;
124
+ affectedEntry . Invalidate ( Version ) ;
179
125
}
180
126
181
127
var originalRemaining = walker . Remaining ;
182
128
var remaining = originalRemaining ;
183
129
try {
184
130
_log ? . Log ( TraceEventType . Verbose , $ "Analysis version { walker . Version } of { originalRemaining } entries has started.") ;
185
- remaining = await AnalyzeAffectedEntriesAsync ( walker , stopWatch , _cts . Token ) ;
131
+ remaining = await AnalyzeAffectedEntriesAsync ( walker , stopWatch , cancellationToken ) ;
186
132
} finally {
187
133
_cts . Dispose ( ) ;
188
134
stopWatch . Stop ( ) ;
189
135
136
+ bool isCanceled ;
190
137
lock ( _syncObj ) {
191
- if ( _version == walker . Version ) {
192
- _progress . ReportRemaining ( walker . Remaining ) ;
193
- }
194
-
138
+ isCanceled = _isCanceled ;
195
139
_state = State . Completed ;
196
- _cts . Dispose ( ) ;
140
+ }
141
+
142
+ if ( ! isCanceled ) {
143
+ _progress . ReportRemaining ( walker . Remaining ) ;
197
144
}
198
145
199
146
if ( _log != null ) {
@@ -212,7 +159,12 @@ private async Task<int> AnalyzeAffectedEntriesAsync(IDependencyChainWalker<Analy
212
159
IDependencyChainNode < PythonAnalyzerEntry > node ;
213
160
var remaining = 0 ;
214
161
while ( ( node = await walker . GetNextAsync ( cancellationToken ) ) != null ) {
215
- if ( _version > walker . Version && ! node . Value . NotAnalyzed ) {
162
+ bool isCanceled ;
163
+ lock ( _syncObj ) {
164
+ isCanceled = _isCanceled ;
165
+ }
166
+
167
+ if ( isCanceled && ! node . Value . NotAnalyzed ) {
216
168
remaining ++ ;
217
169
node . Skip ( ) ;
218
170
continue ;
@@ -227,7 +179,12 @@ private async Task<int> AnalyzeAffectedEntriesAsync(IDependencyChainWalker<Analy
227
179
228
180
if ( walker . MissingKeys . All ( k => k . IsTypeshed ) ) {
229
181
Interlocked . Exchange ( ref _runningTasks , 0 ) ;
230
- if ( _version == walker . Version ) {
182
+ bool isCanceled ;
183
+ lock ( _syncObj ) {
184
+ isCanceled = _isCanceled ;
185
+ }
186
+
187
+ if ( ! isCanceled ) {
231
188
_analysisCompleteEvent . Set ( ) ;
232
189
}
233
190
}
@@ -269,16 +226,18 @@ private void Analyze(IDependencyChainWalker<AnalysisModuleKey, PythonAnalyzerEnt
269
226
node . Commit ( ) ;
270
227
_log ? . Log ( TraceEventType . Verbose , $ "Analysis of { module . Name } ({ module . ModuleType } ) failed.") ;
271
228
} finally {
272
- if ( _version == walker . Version ) {
229
+ bool isCanceled ;
230
+ lock ( _syncObj ) {
231
+ isCanceled = _isCanceled ;
232
+ }
233
+
234
+ if ( ! isCanceled ) {
273
235
_progress . ReportRemaining ( walker . Remaining ) ;
274
236
}
275
237
Interlocked . Decrement ( ref _runningTasks ) ;
276
238
}
277
239
}
278
240
279
- private void StartAnalysis ( PythonAnalyzerEntry entry , int version )
280
- => Task . Run ( ( ) => Analyze ( entry , version , _cts . Token ) , _cts . Token ) . DoNotWait ( ) ;
281
-
282
241
private void Analyze ( PythonAnalyzerEntry entry , int version , CancellationToken cancellationToken ) {
283
242
var stopWatch = Stopwatch . StartNew ( ) ;
284
243
try {
0 commit comments