From 471452646b48c58b6bf64a069e777ba6fe0d9afa Mon Sep 17 00:00:00 2001 From: leo Date: Tue, 11 Mar 2025 16:53:51 +0800 Subject: [PATCH] refactor: use `System.Threading.CancellationToken` instead of `SourceGit.Commands.Command.CancelToken` to cancel fetching information of selected commit Signed-off-by: leo --- src/Commands/Command.cs | 48 ++++++++++++++-------------------- src/ViewModels/CommitDetail.cs | 35 ++++++++++++++----------- 2 files changed, 40 insertions(+), 43 deletions(-) diff --git a/src/Commands/Command.cs b/src/Commands/Command.cs index a3e6673b..cedaf674 100644 --- a/src/Commands/Command.cs +++ b/src/Commands/Command.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Text; using System.Text.RegularExpressions; +using System.Threading; using Avalonia.Threading; @@ -10,11 +11,6 @@ namespace SourceGit.Commands { public partial class Command { - public class CancelToken - { - public bool Requested { get; set; } = false; - } - public class ReadToEndResult { public bool IsSuccess { get; set; } = false; @@ -30,7 +26,7 @@ namespace SourceGit.Commands } public string Context { get; set; } = string.Empty; - public CancelToken Cancel { get; set; } = null; + public CancellationToken CancellationToken { get; set; } = CancellationToken.None; public string WorkingDirectory { get; set; } = null; public EditorType Editor { get; set; } = EditorType.CoreEditor; // Only used in Exec() mode public string SSHKey { get; set; } = string.Empty; @@ -43,36 +39,15 @@ namespace SourceGit.Commands var start = CreateGitStartInfo(); var errs = new List(); var proc = new Process() { StartInfo = start }; - var isCancelled = false; proc.OutputDataReceived += (_, e) => { - if (Cancel != null && Cancel.Requested) - { - isCancelled = true; - proc.CancelErrorRead(); - proc.CancelOutputRead(); - if (!proc.HasExited) - proc.Kill(true); - return; - } - if (e.Data != null) OnReadline(e.Data); }; proc.ErrorDataReceived += (_, e) => { - if (Cancel != null && Cancel.Requested) - { - isCancelled = true; - proc.CancelErrorRead(); - proc.CancelOutputRead(); - if (!proc.HasExited) - proc.Kill(true); - return; - } - if (string.IsNullOrEmpty(e.Data)) { errs.Add(string.Empty); @@ -97,9 +72,25 @@ namespace SourceGit.Commands errs.Add(e.Data); }; + var dummy = null as Process; try { proc.Start(); + + // It not safe, please only use `CancellationToken` in readonly commands. + if (CancellationToken.CanBeCanceled) + { + dummy = proc; + CancellationToken.Register(() => + { + if (dummy is { HasExited: false }) + { + dummy.CancelErrorRead(); + dummy.CancelOutputRead(); + dummy.Kill(); + } + }); + } } catch (Exception e) { @@ -113,10 +104,11 @@ namespace SourceGit.Commands proc.BeginErrorReadLine(); proc.WaitForExit(); + dummy = null; int exitCode = proc.ExitCode; proc.Close(); - if (!isCancelled && exitCode != 0) + if (!CancellationToken.IsCancellationRequested && exitCode != 0) { if (RaiseError) { diff --git a/src/ViewModels/CommitDetail.cs b/src/ViewModels/CommitDetail.cs index 456e99f8..c6ef8367 100644 --- a/src/ViewModels/CommitDetail.cs +++ b/src/ViewModels/CommitDetail.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Globalization; using System.IO; using System.Text.RegularExpressions; +using System.Threading; using System.Threading.Tasks; using Avalonia.Controls; @@ -171,7 +172,7 @@ namespace SourceGit.ViewModels _searchChangeFilter = null; _diffContext = null; _viewRevisionFileContent = null; - _cancelToken = null; + _cancellationSource = null; WebLinks.Clear(); _revisionFiles = null; _revisionFileSearchSuggestion = null; @@ -589,32 +590,36 @@ namespace SourceGit.ViewModels if (_commit == null) return; + if (_cancellationSource is { IsCancellationRequested: false }) + _cancellationSource.Cancel(); + + _cancellationSource = new CancellationTokenSource(); + var token = _cancellationSource.Token; + Task.Run(() => { var message = new Commands.QueryCommitFullMessage(_repo.FullPath, _commit.SHA).Result(); var links = ParseLinksInMessage(message); - Dispatcher.UIThread.Invoke(() => FullMessage = new Models.CommitFullMessage { Message = message, Links = links }); + + if (!token.IsCancellationRequested) + Dispatcher.UIThread.Invoke(() => FullMessage = new Models.CommitFullMessage { Message = message, Links = links }); }); Task.Run(() => { var signInfo = new Commands.QueryCommitSignInfo(_repo.FullPath, _commit.SHA, !_repo.HasAllowedSignersFile).Result(); - Dispatcher.UIThread.Invoke(() => SignInfo = signInfo); + if (!token.IsCancellationRequested) + Dispatcher.UIThread.Invoke(() => SignInfo = signInfo); }); - if (_cancelToken != null) - _cancelToken.Requested = true; - - _cancelToken = new Commands.Command.CancelToken(); - if (Preferences.Instance.ShowChildren) { Task.Run(() => { var max = Preferences.Instance.MaxHistoryCommits; - var cmdChildren = new Commands.QueryCommitChildren(_repo.FullPath, _commit.SHA, max) { Cancel = _cancelToken }; - var children = cmdChildren.Result(); - if (!cmdChildren.Cancel.Requested) + var cmd = new Commands.QueryCommitChildren(_repo.FullPath, _commit.SHA, max) { CancellationToken = token }; + var children = cmd.Result(); + if (!token.IsCancellationRequested) Dispatcher.UIThread.Post(() => Children = children); }); } @@ -622,8 +627,8 @@ namespace SourceGit.ViewModels Task.Run(() => { var parent = _commit.Parents.Count == 0 ? "4b825dc642cb6eb9a060e54bf8d69288fbee4904" : _commit.Parents[0]; - var cmdChanges = new Commands.CompareRevisions(_repo.FullPath, parent, _commit.SHA) { Cancel = _cancelToken }; - var changes = cmdChanges.Result(); + var cmd = new Commands.CompareRevisions(_repo.FullPath, parent, _commit.SHA) { CancellationToken = token }; + var changes = cmd.Result(); var visible = changes; if (!string.IsNullOrWhiteSpace(_searchChangeFilter)) { @@ -635,7 +640,7 @@ namespace SourceGit.ViewModels } } - if (!cmdChanges.Cancel.Requested) + if (!token.IsCancellationRequested) { Dispatcher.UIThread.Post(() => { @@ -873,7 +878,7 @@ namespace SourceGit.ViewModels private string _searchChangeFilter = string.Empty; private DiffContext _diffContext = null; private object _viewRevisionFileContent = null; - private Commands.Command.CancelToken _cancelToken = null; + private CancellationTokenSource _cancellationSource = null; private List _revisionFiles = null; private string _revisionFileSearchFilter = string.Empty; private List _revisionFileSearchSuggestion = null;