Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
58 changes: 54 additions & 4 deletions GVFS/GVFS.Common/GitStatusCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ public class GitStatusCache : IDisposable
private bool isStopping;
private bool isInitialized;
private StatusStatistics statistics;
private CancellationTokenSource shutdownTokenSource;
private Task activeHydrationTask;

private volatile CacheState cacheState = CacheState.Dirty;

Expand All @@ -65,6 +67,7 @@ public GitStatusCache(GVFSContext context, TimeSpan backoffTime)
this.backoffTime = backoffTime;
this.serializedGitStatusFilePath = this.context.Enlistment.GitStatusCachePath;
this.statistics = new StatusStatistics();
this.shutdownTokenSource = new CancellationTokenSource();

this.wakeUpThread = new AutoResetEvent(false);
}
Expand All @@ -79,6 +82,7 @@ public virtual void Initialize()
public virtual void Shutdown()
{
this.isStopping = true;
this.shutdownTokenSource.Cancel();

if (this.isInitialized && this.updateStatusCacheThread != null)
{
Expand Down Expand Up @@ -177,6 +181,27 @@ public virtual void Dispose()
{
this.Shutdown();

// Wait for the hydration task to complete before disposing the
// token source it may still be using.
if (this.activeHydrationTask != null)
{
try
{
this.activeHydrationTask.Wait();
}
catch (AggregateException)
{
}

this.activeHydrationTask = null;
}

if (this.shutdownTokenSource != null)
{
this.shutdownTokenSource.Dispose();
this.shutdownTokenSource = null;
}

if (this.wakeUpThread != null)
{
this.wakeUpThread.Dispose();
Expand Down Expand Up @@ -317,10 +342,29 @@ private void RebuildStatusCacheIfNeeded(bool ignoreBackoff)
if (needToRebuild)
{
this.statistics.RecordBackgroundStatusScanRun();
this.UpdateHydrationSummary();

// Run hydration summary in parallel with git status — they are independent
// operations and neither should delay the other.
Task hydrationTask = Task.Run(() => this.UpdateHydrationSummary());
this.activeHydrationTask = hydrationTask;

bool rebuildStatusCacheSucceeded = this.TryRebuildStatusCache();

// Wait for hydration to complete before logging final stats.
try
{
hydrationTask.Wait();
}
catch (AggregateException ex)
{
EventMetadata errorMetadata = new EventMetadata();
errorMetadata.Add("Area", EtwArea);
errorMetadata.Add("Exception", ex.InnerException?.ToString());
this.context.Tracer.RelatedError(
errorMetadata,
$"{nameof(GitStatusCache)}.{nameof(RebuildStatusCacheIfNeeded)}: Unhandled exception in hydration summary task.");
}

TimeSpan delayedTime = startTime - this.initialDelayTime;
TimeSpan statusRunTime = DateTime.UtcNow - startTime;

Expand Down Expand Up @@ -356,7 +400,7 @@ private void UpdateHydrationSummary()
* and this is also a convenient place to log telemetry for it.
*/
EnlistmentHydrationSummary hydrationSummary =
EnlistmentHydrationSummary.CreateSummary(this.context.Enlistment, this.context.FileSystem);
EnlistmentHydrationSummary.CreateSummary(this.context.Enlistment, this.context.FileSystem, cancellationToken: this.shutdownTokenSource.Token);
EventMetadata metadata = new EventMetadata();
metadata.Add("Area", EtwArea);
if (hydrationSummary.IsValid)
Expand All @@ -372,14 +416,20 @@ private void UpdateHydrationSummary()
metadata,
Keywords.Telemetry);
}
else
else if (hydrationSummary.Error != null)
{
metadata["Exception"] = hydrationSummary.Error?.ToString();
metadata["Exception"] = hydrationSummary.Error.ToString();
this.context.Tracer.RelatedWarning(
metadata,
$"{nameof(GitStatusCache)}{nameof(RebuildStatusCacheIfNeeded)}: hydration summary could not be calculated.",
Keywords.Telemetry);
}
else
{
// Invalid summary with no error — likely cancelled during shutdown
this.context.Tracer.RelatedInfo(
$"{nameof(GitStatusCache)}{nameof(RebuildStatusCacheIfNeeded)}: hydration summary was cancelled.");
}
}
catch (Exception ex)
{
Expand Down
25 changes: 22 additions & 3 deletions GVFS/GVFS.Common/HealthCalculator/EnlistmentHydrationSummary.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
using GVFS.Common.FileSystem;
using GVFS.Common.FileSystem;
using GVFS.Common.Git;
using System;
using System.IO;
using System.Linq;
using System.Linq;
using System.Threading;

namespace GVFS.Common
{
Expand Down Expand Up @@ -40,7 +41,8 @@ public string ToMessage()

public static EnlistmentHydrationSummary CreateSummary(
GVFSEnlistment enlistment,
PhysicalFileSystem fileSystem)
PhysicalFileSystem fileSystem,
CancellationToken cancellationToken = default)
{
try
{
Expand All @@ -51,14 +53,21 @@ public static EnlistmentHydrationSummary CreateSummary(
/* Getting all the directories is also slow, but not as slow as reading the entire index,
* GetTotalPathCount caches the count so this is only slow occasionally,
* and the GitStatusCache manager also calls this to ensure it is updated frequently. */
cancellationToken.ThrowIfCancellationRequested();
int totalFolderCount = GetHeadTreeCount(enlistment, fileSystem);

EnlistmentPathData pathData = new EnlistmentPathData();

/* FUTURE: These could be optimized to only deal with counts instead of full path lists */
pathData.LoadPlaceholdersFromDatabase(enlistment);

cancellationToken.ThrowIfCancellationRequested();

pathData.LoadModifiedPaths(enlistment);


cancellationToken.ThrowIfCancellationRequested();

int hydratedFileCount = pathData.ModifiedFilePaths.Count + pathData.PlaceholderFilePaths.Count;
int hydratedFolderCount = pathData.ModifiedFolderPaths.Count + pathData.PlaceholderFolderPaths.Count;
return new EnlistmentHydrationSummary()
Expand All @@ -68,6 +77,16 @@ public static EnlistmentHydrationSummary CreateSummary(
TotalFileCount = totalFileCount,
TotalFolderCount = totalFolderCount,
};
}
catch (OperationCanceledException)
{
return new EnlistmentHydrationSummary()
{
HydratedFileCount = -1,
HydratedFolderCount = -1,
TotalFileCount = -1,
TotalFolderCount = -1,
};
}
catch (Exception e)
{
Expand Down
Loading