@@ -135,8 +135,15 @@ protected void ShutdownScheduler ()
135135 }
136136 }
137137
138+ CancellationTokenSource disposeTokenSource = new CancellationTokenSource ( ) ;
139+
138140 public void Dispose ( )
139141 {
142+ if ( disposeTokenSource != null ) {
143+ disposeTokenSource . Cancel ( ) ;
144+ disposeTokenSource . Dispose ( ) ;
145+ disposeTokenSource = null ;
146+ }
140147 Dispose ( true ) ;
141148 GC . SuppressFinalize ( this ) ;
142149 }
@@ -292,49 +299,50 @@ public bool TryGetVersionInfo (FilePath localPath, VersionInfoQueryFlags queryFl
292299 /// </param>
293300 public async Task < IReadOnlyList < VersionInfo > > GetVersionInfoAsync ( IEnumerable < FilePath > paths , VersionInfoQueryFlags queryFlags = VersionInfoQueryFlags . None , CancellationToken cancellationToken = default )
294301 {
295- if ( ( queryFlags & VersionInfoQueryFlags . IgnoreCache ) != 0 ) {
296- var task = await ExclusiveOperationFactory . StartNew ( delegate {
297- // We shouldn't use IEnumerable because elements don't save property modifications.
298- return OnGetVersionInfoAsync ( paths , ( queryFlags & VersionInfoQueryFlags . IncludeRemoteStatus ) != 0 , cancellationToken ) ;
299- } ) . ConfigureAwait ( false ) ;
300-
301- var res = await task . ConfigureAwait ( false ) ;
302- foreach ( var vi in res )
303- if ( ! vi . IsInitialized ) await vi . InitAsync ( this , cancellationToken ) . ConfigureAwait ( false ) ;
304- await infoCache . SetStatusAsync ( res , cancellationToken : cancellationToken ) . ConfigureAwait ( false ) ;
305- return res ;
306- }
307- var pathsToQuery = new List < FilePath > ( ) ;
308- var result = new List < VersionInfo > ( ) ;
309- foreach ( var path in paths ) {
310- var vi = infoCache . GetStatus ( path ) ;
311- if ( vi != null ) {
312- result . Add ( vi ) ;
313- // This status has been invalidated, query it asynchronously
314- if ( vi . RequiresRefresh )
315- pathsToQuery . Add ( path ) ;
302+ using ( LinkTokenToDispose ( ref cancellationToken ) ) {
303+ if ( ( queryFlags & VersionInfoQueryFlags . IgnoreCache ) != 0 ) {
304+ var task = await ExclusiveOperationFactory . StartNew ( delegate {
305+ // We shouldn't use IEnumerable because elements don't save property modifications.
306+ return OnGetVersionInfoAsync ( paths , ( queryFlags & VersionInfoQueryFlags . IncludeRemoteStatus ) != 0 , cancellationToken ) ;
307+ } ) . ConfigureAwait ( false ) ;
308+
309+ var res = await task . ConfigureAwait ( false ) ;
310+ foreach ( var vi in res )
311+ if ( ! vi . IsInitialized ) await vi . InitAsync ( this , cancellationToken ) . ConfigureAwait ( false ) ;
312+ await infoCache . SetStatusAsync ( res , cancellationToken : cancellationToken ) . ConfigureAwait ( false ) ;
313+ return res ;
316314 }
317- else {
318- // If there is no cached status, query it asynchronously
319- vi = new VersionInfo ( path , "" , Directory . Exists ( path ) , VersionStatus . Versioned , null , VersionStatus . Versioned , null ) ;
320- await infoCache . SetStatusAsync ( vi , false ) . ConfigureAwait ( false ) ;
321- result . Add ( vi ) ;
322- pathsToQuery . Add ( path ) ;
315+ var pathsToQuery = new List < FilePath > ( ) ;
316+ var result = new List < VersionInfo > ( ) ;
317+ foreach ( var path in paths ) {
318+ var vi = infoCache . GetStatus ( path ) ;
319+ if ( vi != null ) {
320+ result . Add ( vi ) ;
321+ // This status has been invalidated, query it asynchronously
322+ if ( vi . RequiresRefresh )
323+ pathsToQuery . Add ( path ) ;
324+ } else {
325+ // If there is no cached status, query it asynchronously
326+ vi = new VersionInfo ( path , "" , Directory . Exists ( path ) , VersionStatus . Versioned , null , VersionStatus . Versioned , null ) ;
327+ await infoCache . SetStatusAsync ( vi , false ) . ConfigureAwait ( false ) ;
328+ result . Add ( vi ) ;
329+ pathsToQuery . Add ( path ) ;
330+ }
331+ // Console.WriteLine ("GetVersionInfo " + string.Join (", ", paths.Select (p => p.FullPath)));
323332 }
324- // Console.WriteLine ("GetVersionInfo " + string.Join (", ", paths.Select (p => p.FullPath)));
325- }
326- if ( pathsToQuery . Count > 0 ) {
327- ExclusiveOperationFactory . StartNew ( async delegate {
328- var status = await OnGetVersionInfoAsync ( pathsToQuery , ( queryFlags & VersionInfoQueryFlags . IncludeRemoteStatus ) != 0 , cancellationToken ) . ConfigureAwait ( false ) ;
329- foreach ( var vi in status ) {
330- if ( ! vi . IsInitialized ) {
331- await vi . InitAsync ( this , cancellationToken ) . ConfigureAwait ( false ) ;
333+ if ( pathsToQuery . Count > 0 ) {
334+ ExclusiveOperationFactory . StartNew ( async delegate {
335+ var status = await OnGetVersionInfoAsync ( pathsToQuery , ( queryFlags & VersionInfoQueryFlags . IncludeRemoteStatus ) != 0 , cancellationToken ) . ConfigureAwait ( false ) ;
336+ foreach ( var vi in status ) {
337+ if ( ! vi . IsInitialized ) {
338+ await vi . InitAsync ( this , cancellationToken ) . ConfigureAwait ( false ) ;
339+ }
332340 }
333- }
334- await infoCache . SetStatusAsync ( status , cancellationToken ) . ConfigureAwait ( false ) ;
335- } ) . Ignore ( ) ;
341+ await infoCache . SetStatusAsync ( status , cancellationToken ) . ConfigureAwait ( false ) ;
342+ } ) . Ignore ( ) ;
343+ }
344+ return result ;
336345 }
337- return result ;
338346 }
339347
340348 public bool TryGetVersionInfo ( IEnumerable < FilePath > paths , out IReadOnlyList < VersionInfo > infos )
@@ -360,16 +368,17 @@ public bool TryGetVersionInfo (IEnumerable<FilePath> paths, VersionInfoQueryFlag
360368 }
361369 }
362370 if ( pathsToQuery . Count > 0 ) {
371+ var token = disposeTokenSource . Token ;
363372 ExclusiveOperationFactory . StartNew ( async delegate {
364373 // we don't care about initialization and setstatus async to happen on the exclusive thread, as we're not running a query here.
365- var status = await OnGetVersionInfoAsync ( paths , ( queryFlags & VersionInfoQueryFlags . IncludeRemoteStatus ) != 0 ) . ConfigureAwait ( false ) ;
374+ var status = await OnGetVersionInfoAsync ( paths , ( queryFlags & VersionInfoQueryFlags . IncludeRemoteStatus ) != 0 , token ) . ConfigureAwait ( false ) ;
366375 foreach ( var vi in status ) {
367376 if ( ! vi . IsInitialized ) {
368- await vi . InitAsync ( this ) . ConfigureAwait ( false ) ;
377+ await vi . InitAsync ( this , token ) . ConfigureAwait ( false ) ;
369378 }
370379 }
371- await infoCache . SetStatusAsync ( status ) . ConfigureAwait ( false ) ;
372- } ) . Ignore ( ) ;
380+ await infoCache . SetStatusAsync ( status , token ) . ConfigureAwait ( false ) ;
381+ } , token ) . Ignore ( ) ;
373382 }
374383 if ( getVersionInfoFailed ) {
375384 infos = null ;
@@ -379,8 +388,16 @@ public bool TryGetVersionInfo (IEnumerable<FilePath> paths, VersionInfoQueryFlag
379388 return true ;
380389 }
381390
391+ protected IDisposable LinkTokenToDispose ( ref CancellationToken originalToken )
392+ {
393+ var combinedTokenSource = CancellationTokenSource . CreateLinkedTokenSource ( originalToken , disposeTokenSource . Token ) ;
394+ originalToken = combinedTokenSource . Token ;
395+ return combinedTokenSource ;
396+ }
397+
382398 public async Task < VersionInfo [ ] > GetDirectoryVersionInfoAsync ( FilePath localDirectory , bool getRemoteStatus , bool recursive , CancellationToken cancellationToken = default )
383399 {
400+ var combinedTokenSource = LinkTokenToDispose ( ref cancellationToken ) ;
384401 try {
385402 if ( recursive ) {
386403 return await OnGetDirectoryVersionInfoAsync ( localDirectory , getRemoteStatus , true , cancellationToken ) . ConfigureAwait ( false ) ;
@@ -402,6 +419,7 @@ public async Task<VersionInfo[]> GetDirectoryVersionInfoAsync (FilePath localDir
402419 return status . FileInfo ;
403420 return Array . Empty < VersionInfo > ( ) ;
404421 } finally {
422+ combinedTokenSource . Dispose ( ) ;
405423 //Console.WriteLine ("GetDirectoryVersionInfo " + localDirectory + " - " + (DateTime.Now - now).TotalMilliseconds);
406424 }
407425 }
@@ -440,6 +458,7 @@ class DirectoryInfoQuery
440458 /// </param>
441459 public async Task < RevisionPath [ ] > GetRevisionChangesAsync ( Revision revision , CancellationToken cancellationToken = default )
442460 {
461+ using ( LinkTokenToDispose ( ref cancellationToken ) )
443462 using ( var tracker = Instrumentation . GetRevisionChangesCounter . BeginTiming ( new RepositoryMetadata ( VersionControlSystem ) ) ) {
444463 try {
445464 return await OnGetRevisionChangesAsync ( revision , cancellationToken ) . ConfigureAwait ( false ) ;
@@ -457,6 +476,7 @@ public async Task<RevisionPath []> GetRevisionChangesAsync (Revision revision, C
457476 // Returns the revision history of a file
458477 public async Task < Revision [ ] > GetHistoryAsync ( FilePath localFile , Revision since , CancellationToken cancellationToken = default )
459478 {
479+ using ( LinkTokenToDispose ( ref cancellationToken ) )
460480 using ( var tracker = Instrumentation . GetHistoryCounter . BeginTiming ( new RepositoryMetadata ( VersionControlSystem ) ) ) {
461481 try {
462482 return await OnGetHistoryAsync ( localFile , since , cancellationToken ) . ConfigureAwait ( false ) ;
0 commit comments