21
21
using System . Diagnostics . CodeAnalysis ;
22
22
using System . IO ;
23
23
using System . Linq ;
24
+ using System . Runtime . CompilerServices ;
24
25
using System . Text ;
25
26
using System . Threading ;
26
27
using System . Threading . Tasks ;
@@ -45,11 +46,10 @@ internal sealed class ProjectEntry : IPythonProjectEntry, IAggregateableProjectE
45
46
private readonly ConcurrentQueue < WeakReference < ReferenceDict > > _backReferences = new ConcurrentQueue < WeakReference < ReferenceDict > > ( ) ;
46
47
private readonly HashSet < AggregateProjectEntry > _aggregates = new HashSet < AggregateProjectEntry > ( ) ;
47
48
48
- private TaskCompletionSource < IModuleAnalysis > _analysisTcs = new TaskCompletionSource < IModuleAnalysis > ( ) ;
49
+ private AnalysisCompletionToken _analysisCompletionToken ;
49
50
private AnalysisUnit _unit ;
50
51
private readonly ManualResetEventSlim _pendingParse = new ManualResetEventSlim ( true ) ;
51
52
private long _expectedParseVersion ;
52
- private long _expectedAnalysisVersion ;
53
53
54
54
internal ProjectEntry (
55
55
PythonAnalyzer state ,
@@ -66,6 +66,7 @@ IAnalysisCookie cookie
66
66
67
67
MyScope = new ModuleInfo ( ModuleName , this , state . Interpreter . CreateModuleContext ( ) ) ;
68
68
_unit = new AnalysisUnit ( null , MyScope . Scope ) ;
69
+ _analysisCompletionToken = AnalysisCompletionToken . Default ;
69
70
70
71
_buffers = new SortedDictionary < int , DocumentBuffer > { [ 0 ] = new DocumentBuffer ( ) } ;
71
72
if ( Cookie is InitialContentCookie c ) {
@@ -137,10 +138,10 @@ public IPythonParse GetCurrentParse() {
137
138
}
138
139
}
139
140
140
- internal Task < IModuleAnalysis > GetAnalysisAsync ( int waitingTimeout = - 1 , CancellationToken cancellationToken = default ( CancellationToken ) ) {
141
+ internal Task < IModuleAnalysis > GetAnalysisAsync ( int waitingTimeout = - 1 , CancellationToken cancellationToken = default ) {
141
142
Task < IModuleAnalysis > task ;
142
143
lock ( this ) {
143
- task = _analysisTcs . Task ;
144
+ task = _analysisCompletionToken . Task ;
144
145
}
145
146
146
147
if ( task . IsCompleted || waitingTimeout == - 1 && ! cancellationToken . CanBeCanceled ) {
@@ -158,22 +159,15 @@ public IPythonParse GetCurrentParse() {
158
159
159
160
internal void SetCompleteAnalysis ( ) {
160
161
lock ( this ) {
161
- if ( _expectedAnalysisVersion != Analysis . Version ) {
162
- return ;
163
- }
164
- _analysisTcs . TrySetResult ( Analysis ) ;
162
+ _analysisCompletionToken . TrySetAnalysis ( Analysis ) ;
165
163
}
166
164
RaiseNewAnalysis ( ) ;
167
165
}
168
166
169
- internal void ResetCompleteAnalysis ( ) {
170
- TaskCompletionSource < IModuleAnalysis > analysisTcs = null ;
167
+ internal void NewAnalysisAwaitableOnParse ( ) {
171
168
lock ( this ) {
172
- _expectedAnalysisVersion = AnalysisVersion + 1 ;
173
- analysisTcs = _analysisTcs ;
174
- _analysisTcs = new TaskCompletionSource < IModuleAnalysis > ( TaskCreationOptions . RunContinuationsAsynchronously ) ;
169
+ _analysisCompletionToken = _analysisCompletionToken . NewParse ( ) ;
175
170
}
176
- analysisTcs ? . TrySetCanceled ( ) ;
177
171
}
178
172
179
173
public void SetCurrentParse ( PythonAst tree , IAnalysisCookie cookie , bool notify = true ) {
@@ -195,38 +189,58 @@ public void SetCurrentParse(PythonAst tree, IAnalysisCookie cookie, bool notify
195
189
196
190
internal bool IsVisible ( ProjectEntry assigningScope ) => true ;
197
191
198
- public void Analyze ( CancellationToken cancel ) => Analyze ( cancel , false ) ;
199
-
200
- public void Analyze ( CancellationToken cancel , bool enqueueOnly ) {
192
+ public void Analyze ( CancellationToken cancel ) {
201
193
if ( cancel . IsCancellationRequested ) {
202
194
return ;
203
195
}
204
196
205
197
lock ( this ) {
206
- AnalysisVersion ++ ;
198
+ PrepareForAnalysis ( ) ;
207
199
208
- foreach ( var aggregate in _aggregates ) {
209
- aggregate ? . BumpVersion ( ) ;
210
- }
200
+ ProjectState . AnalyzeQueuedEntries ( cancel ) ;
211
201
212
- Parse ( enqueueOnly , cancel ) ;
202
+ // publish the analysis now that it's complete/running
203
+ Analysis = new ModuleAnalysis (
204
+ _unit ,
205
+ ( ( ModuleScope ) _unit . Scope ) . CloneForPublish ( ) ,
206
+ DocumentUri ,
207
+ AnalysisVersion
208
+ ) ;
213
209
}
214
210
215
- if ( ! enqueueOnly ) {
216
- RaiseNewAnalysis ( ) ;
211
+ RaiseNewAnalysis ( ) ;
212
+ }
213
+
214
+ public void PreAnalyze ( ) {
215
+ lock ( this ) {
216
+ PrepareForAnalysis ( ) ;
217
+
218
+ // publish the analysis now that it's complete/running
219
+ Analysis = new ModuleAnalysis (
220
+ _unit ,
221
+ ( ( ModuleScope ) _unit . Scope ) . CloneForPublish ( ) ,
222
+ DocumentUri ,
223
+ AnalysisVersion
224
+ ) ;
217
225
}
218
226
}
219
227
220
228
private void RaiseNewAnalysis ( ) => NewAnalysis ? . Invoke ( this , EventArgs . Empty ) ;
221
229
222
- public int AnalysisVersion { get ; private set ; }
230
+ public int AnalysisVersion => _analysisCompletionToken . Version ;
223
231
224
232
public bool IsAnalyzed => Analysis != null ;
225
233
226
- private void Parse ( bool enqueueOnly , CancellationToken cancel ) {
234
+ private void PrepareForAnalysis ( ) {
227
235
#if DEBUG
228
236
Debug . Assert ( Monitor . IsEntered ( this ) ) ;
229
237
#endif
238
+ _analysisCompletionToken = _analysisCompletionToken . NewAnalysis ( ) ;
239
+
240
+ foreach ( var aggregate in _aggregates ) {
241
+ aggregate ? . BumpVersion ( ) ;
242
+ }
243
+
230
244
var parse = GetCurrentParse ( ) ;
231
245
var tree = parse ? . Tree ;
232
246
var cookie = parse ? . Cookie ;
@@ -306,18 +320,6 @@ where lastDot > 0
306
320
}
307
321
308
322
_unit . Enqueue ( ) ;
309
-
310
- if ( ! enqueueOnly ) {
311
- ProjectState . AnalyzeQueuedEntries ( cancel ) ;
312
- }
313
-
314
- // publish the analysis now that it's complete/running
315
- Analysis = new ModuleAnalysis (
316
- _unit ,
317
- ( ( ModuleScope ) _unit . Scope ) . CloneForPublish ( ) ,
318
- DocumentUri ,
319
- AnalysisVersion
320
- ) ;
321
323
}
322
324
323
325
public IGroupableAnalysisProject AnalysisGroup => ProjectState ;
@@ -348,7 +350,7 @@ where lastDot > 0
348
350
349
351
public void Dispose ( ) {
350
352
lock ( this ) {
351
- AnalysisVersion = - 1 ;
353
+ _analysisCompletionToken = AnalysisCompletionToken . Disposed ;
352
354
353
355
var state = ProjectState ;
354
356
foreach ( var aggregatedInto in _aggregates ) {
@@ -494,6 +496,55 @@ public void ResetDocument(int version, string content) {
494
496
public void AddBackReference ( ReferenceDict referenceDict ) {
495
497
_backReferences . Enqueue ( new WeakReference < ReferenceDict > ( referenceDict ) ) ;
496
498
}
499
+
500
+ private struct AnalysisCompletionToken {
501
+ public static readonly AnalysisCompletionToken Default ;
502
+ public static readonly AnalysisCompletionToken Disposed ;
503
+
504
+ static AnalysisCompletionToken ( ) {
505
+ var cancelledTcs = CreateTcs ( ) ;
506
+ cancelledTcs . SetCanceled ( ) ;
507
+
508
+ Default = new AnalysisCompletionToken ( CreateTcs ( ) , - 1 , false ) ;
509
+ Disposed = new AnalysisCompletionToken ( cancelledTcs , - 1 , true ) ;
510
+ }
511
+
512
+ private readonly bool _isParse ;
513
+ private readonly TaskCompletionSource < IModuleAnalysis > _tcs ;
514
+
515
+ public int Version { get ; }
516
+ public Task < IModuleAnalysis > Task => _tcs . Task ;
517
+
518
+ public AnalysisCompletionToken NewParse ( ) {
519
+ if ( _isParse ) {
520
+ return this ;
521
+ }
522
+
523
+ var tcs = CreateTcs ( ) ;
524
+ _tcs . TrySetCanceled ( ) ;
525
+ return new AnalysisCompletionToken ( tcs , Version + 1 , true ) ;
526
+ }
527
+
528
+ public AnalysisCompletionToken NewAnalysis ( ) {
529
+ var tcs = _tcs . Task . IsCompleted ? CreateTcs ( ) : _tcs ;
530
+ return new AnalysisCompletionToken ( tcs , _isParse ? Version : Version + 1 , false ) ;
531
+ }
532
+
533
+ private AnalysisCompletionToken ( TaskCompletionSource < IModuleAnalysis > tcs , int version , bool isParse ) {
534
+ _tcs = tcs ;
535
+ Version = version ;
536
+ _isParse = isParse ;
537
+ }
538
+
539
+ public void TrySetAnalysis ( IModuleAnalysis analysis ) {
540
+ if ( Version == analysis . Version ) {
541
+ _tcs . TrySetResult ( analysis ) ;
542
+ }
543
+ }
544
+
545
+ private static TaskCompletionSource < IModuleAnalysis > CreateTcs ( )
546
+ => new TaskCompletionSource < IModuleAnalysis > ( TaskCreationOptions . RunContinuationsAsynchronously ) ;
547
+ }
497
548
}
498
549
499
550
class InitialContentCookie : IAnalysisCookie {
0 commit comments