Skip to content

Code Quality: Improved performance of Git operations #14688

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
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
3 changes: 0 additions & 3 deletions src/Files.App/App.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -317,9 +317,6 @@ await SafetyExtensions.IgnoreExceptions(async () =>
},
Logger);

// Dispose git operations' thread
GitHelpers.TryDispose();

// Destroy cached properties windows
FilePropertiesHelpers.DestroyCachedWindows();
AppModel.IsMainWindowClosed = true;
Expand Down
51 changes: 17 additions & 34 deletions src/Files.App/Utils/Git/GitHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,7 @@ internal static class GitHelpers
? CLIENT_ID_SECRET
: string.Empty;

private static ThreadWithMessageQueue? _owningThread;

private static int _activeOperationsCount = 0;
private static readonly SemaphoreSlim GitOperationSemaphore = new SemaphoreSlim(1, 1);

private static bool _IsExecutingGitAction;
public static bool IsExecutingGitAction
Expand All @@ -66,14 +64,6 @@ private set

public static event EventHandler? GitFetchCompleted;

public static void TryDispose()
{
var threadToDispose = _owningThread;
_owningThread = null;
Interlocked.Exchange(ref _activeOperationsCount, 0);
threadToDispose?.Dispose();
}

public static string? GetGitRepositoryPath(string? path, string root)
{
if (string.IsNullOrEmpty(root))
Expand Down Expand Up @@ -125,7 +115,7 @@ public static async Task<BranchItem[]> GetBranchesNames(string? path)
if (string.IsNullOrWhiteSpace(path) || !Repository.IsValid(path))
return Array.Empty<BranchItem>();

var (result, returnValue) = await PostMethodToThreadWithMessageQueueAsync<(GitOperationResult, BranchItem[])>(() =>
var (result, returnValue) = await DoGitOperationAsync<(GitOperationResult, BranchItem[])>(() =>
{
var branches = Array.Empty<BranchItem>();
var result = GitOperationResult.Success;
Expand Down Expand Up @@ -157,7 +147,7 @@ public static async Task<BranchItem[]> GetBranchesNames(string? path)
if (string.IsNullOrWhiteSpace(path) || !Repository.IsValid(path))
return null;

var (_, returnValue) = await PostMethodToThreadWithMessageQueueAsync<(GitOperationResult, BranchItem?)>(() =>
var (_, returnValue) = await DoGitOperationAsync<(GitOperationResult, BranchItem?)>(() =>
{
BranchItem? head = null;
try
Expand All @@ -179,7 +169,7 @@ public static async Task<BranchItem[]> GetBranchesNames(string? path)
}

return (GitOperationResult.Success, head);
});
}, true);

return returnValue;
}
Expand Down Expand Up @@ -228,7 +218,7 @@ public static async Task<bool> Checkout(string? repositoryPath, string? branch)
}
}

var result = await PostMethodToThreadWithMessageQueueAsync<GitOperationResult>(() =>
var result = await DoGitOperationAsync<GitOperationResult>(() =>
{
try
{
Expand Down Expand Up @@ -306,7 +296,7 @@ public static async Task DeleteBranchAsync(string? repositoryPath, string? activ

IsExecutingGitAction = true;

await PostMethodToThreadWithMessageQueueAsync<GitOperationResult>(() =>
await DoGitOperationAsync<GitOperationResult>(() =>
{
try
{
Expand Down Expand Up @@ -363,7 +353,7 @@ public static async void FetchOrigin(string? repositoryPath)
IsExecutingGitAction = true;
});

await PostMethodToThreadWithMessageQueueAsync<GitOperationResult>(() =>
await DoGitOperationAsync<GitOperationResult>(() =>
{
var result = GitOperationResult.Success;
try
Expand Down Expand Up @@ -422,7 +412,7 @@ public static async Task PullOriginAsync(string? repositoryPath)
IsExecutingGitAction = true;
});

var result = await PostMethodToThreadWithMessageQueueAsync<GitOperationResult>(() =>
var result = await DoGitOperationAsync<GitOperationResult>(() =>
{
try
{
Expand Down Expand Up @@ -508,7 +498,7 @@ public static async Task PushToOriginAsync(string? repositoryPath, string? branc
b => b.UpstreamBranch = branch.CanonicalName);
}

var result = await PostMethodToThreadWithMessageQueueAsync<GitOperationResult>(() =>
var result = await DoGitOperationAsync<GitOperationResult>(() =>
{
try
{
Expand Down Expand Up @@ -830,29 +820,22 @@ private static bool IsAuthorizationException(Exception ex)
ex.Message.Contains("authentication replays", StringComparison.OrdinalIgnoreCase);
}

private static void DisposeIfFinished()
private static async Task<T?> DoGitOperationAsync<T>(Func<object> payload, bool useSemaphore = false)
{
if (Interlocked.Decrement(ref _activeOperationsCount) == 0)
TryDispose();
}

private static async Task<T?> PostMethodToThreadWithMessageQueueAsync<T>(Func<object> payload)
{
T? returnValue = default;

Interlocked.Increment(ref _activeOperationsCount);
_owningThread ??= new ThreadWithMessageQueue();
if (useSemaphore)
await GitOperationSemaphore.WaitAsync();
else
await Task.Yield();

try
{
returnValue = await _owningThread.PostMethod<T>(payload);
return (T)payload();
}
finally
{
DisposeIfFinished();
if (useSemaphore)
GitOperationSemaphore.Release();
}

return returnValue;
}
}
}