Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit e9d4377

Browse files
feat: add safe command termination (#1012)
1 parent 4868ea1 commit e9d4377

File tree

5 files changed

+115
-13
lines changed

5 files changed

+115
-13
lines changed

‎src/Commands/Command.cs

Lines changed: 51 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,12 @@ public enum EditorType
3737
public string Args { get; set; } = string.Empty;
3838
public bool RaiseError { get; set; } = true;
3939
public bool TraitErrorAsOutput { get; set; } = false;
40+
public bool Cancelable { get; } = false;
41+
42+
public Command(bool cancelable = false)
43+
{
44+
Cancelable = cancelable;
45+
}
4046

4147
public bool Exec()
4248
{
@@ -109,12 +115,22 @@ public bool Exec()
109115
return false;
110116
}
111117

112-
proc.BeginOutputReadLine();
113-
proc.BeginErrorReadLine();
114-
proc.WaitForExit();
118+
_proc = proc;
119+
120+
int exitCode;
121+
try
122+
{
123+
proc.BeginOutputReadLine();
124+
proc.BeginErrorReadLine();
125+
proc.WaitForExit();
115126

116-
int exitCode = proc.ExitCode;
117-
proc.Close();
127+
exitCode = proc.ExitCode;
128+
proc.Close();
129+
}
130+
finally
131+
{
132+
_proc = null;
133+
}
118134

119135
if (!isCancelled && exitCode != 0)
120136
{
@@ -150,17 +166,37 @@ public ReadToEndResult ReadToEnd()
150166
};
151167
}
152168

153-
var rs = new ReadToEndResult()
169+
_proc = proc;
170+
171+
try
154172
{
155-
StdOut = proc.StandardOutput.ReadToEnd(),
156-
StdErr = proc.StandardError.ReadToEnd(),
157-
};
173+
var rs = new ReadToEndResult()
174+
{
175+
StdOut = proc.StandardOutput.ReadToEnd(),
176+
StdErr = proc.StandardError.ReadToEnd(),
177+
};
158178

159-
proc.WaitForExit();
160-
rs.IsSuccess = proc.ExitCode == 0;
161-
proc.Close();
179+
proc.WaitForExit();
180+
rs.IsSuccess = proc.ExitCode == 0;
181+
proc.Close();
162182

163-
return rs;
183+
return rs;
184+
}
185+
finally
186+
{
187+
_proc = null;
188+
}
189+
}
190+
191+
public void TryCancel()
192+
{
193+
if (!this.Cancelable)
194+
throw new Exception("Command is not cancelable!");
195+
196+
if (_proc is null)
197+
return;
198+
199+
Native.OS.TerminateSafely(_proc);
164200
}
165201

166202
protected virtual void OnReadline(string line)
@@ -226,5 +262,7 @@ private ProcessStartInfo CreateGitStartInfo()
226262

227263
[GeneratedRegex(@"\d+%")]
228264
private static partial Regex REG_PROGRESS();
265+
266+
private Process _proc = null;
229267
}
230268
}

‎src/Native/Linux.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Collections.Generic;
33
using System.Diagnostics;
44
using System.IO;
5+
using System.Runtime.InteropServices;
56
using System.Runtime.Versioning;
67

78
using Avalonia;
@@ -11,6 +12,14 @@ namespace SourceGit.Native
1112
[SupportedOSPlatform("linux")]
1213
internal class Linux : OS.IBackend
1314
{
15+
private enum SIGNAL : int
16+
{
17+
TERM = 15
18+
}
19+
20+
[DllImport("c")]
21+
private static extern int kill(int pid, int sig);
22+
1423
public void SetupApp(AppBuilder builder)
1524
{
1625
builder.With(new X11PlatformOptions() { EnableIme = true });
@@ -97,6 +106,12 @@ public void OpenWithDefaultEditor(string file)
97106
}
98107
}
99108

109+
public void TerminateSafely(Process process)
110+
{
111+
if (kill(process.Id, (int)SIGNAL.TERM) == 0)
112+
process.WaitForExit();
113+
}
114+
100115
private string FindExecutable(string filename)
101116
{
102117
var pathVariable = Environment.GetEnvironmentVariable("PATH") ?? string.Empty;

‎src/Native/MacOS.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Collections.Generic;
33
using System.Diagnostics;
44
using System.IO;
5+
using System.Runtime.InteropServices;
56
using System.Runtime.Versioning;
67

78
using Avalonia;
@@ -11,6 +12,14 @@ namespace SourceGit.Native
1112
[SupportedOSPlatform("macOS")]
1213
internal class MacOS : OS.IBackend
1314
{
15+
private enum SIGNAL : int
16+
{
17+
TERM = 15
18+
}
19+
20+
[DllImport("System")]
21+
private static extern int kill(int pid, int sig);
22+
1423
public void SetupApp(AppBuilder builder)
1524
{
1625
builder.With(new MacOSPlatformOptions()
@@ -88,5 +97,11 @@ public void OpenWithDefaultEditor(string file)
8897
{
8998
Process.Start("open", $"\"{file}\"");
9099
}
100+
101+
public void TerminateSafely(Process process)
102+
{
103+
if (kill(process.Id, (int)SIGNAL.TERM) == 0)
104+
process.WaitForExit();
105+
}
91106
}
92107
}

‎src/Native/OS.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ public interface IBackend
2323
void OpenInFileManager(string path, bool select);
2424
void OpenBrowser(string url);
2525
void OpenWithDefaultEditor(string file);
26+
27+
void TerminateSafely(Process process);
2628
}
2729

2830
public static string DataDir
@@ -168,6 +170,11 @@ public static void OpenWithDefaultEditor(string file)
168170
_backend.OpenWithDefaultEditor(file);
169171
}
170172

173+
public static void TerminateSafely(Process process)
174+
{
175+
_backend.TerminateSafely(process);
176+
}
177+
171178
private static void UpdateGitVersion()
172179
{
173180
if (string.IsNullOrEmpty(_gitExecutable) || !File.Exists(_gitExecutable))

‎src/Native/Windows.cs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,11 @@ internal struct MARGINS
3535
public int cyBottomHeight;
3636
}
3737

38+
private enum CTRL_EVENT : int
39+
{
40+
CTRL_C = 0
41+
}
42+
3843
[DllImport("ntdll.dll")]
3944
private static extern int RtlGetVersion(ref RTL_OSVERSIONINFOEX lpVersionInformation);
4045

@@ -53,6 +58,12 @@ internal struct MARGINS
5358
[DllImport("shell32.dll", CharSet = CharSet.Unicode, SetLastError = false)]
5459
private static extern int SHOpenFolderAndSelectItems(IntPtr pidlFolder, int cild, IntPtr apidl, int dwFlags);
5560

61+
[DllImport("kernel32.dll")]
62+
public static extern bool SetConsoleCtrlHandler(IntPtr handlerRoutine, bool add);
63+
64+
[DllImport("kernel32.dll")]
65+
private static extern bool GenerateConsoleCtrlEvent(int dwCtrlEvent, int dwProcessGroupId);
66+
5667
public void SetupApp(AppBuilder builder)
5768
{
5869
// Fix drop shadow issue on Windows 10
@@ -212,6 +223,22 @@ public void OpenWithDefaultEditor(string file)
212223
Process.Start(start);
213224
}
214225

226+
public void TerminateSafely(Process process)
227+
{
228+
if (SetConsoleCtrlHandler(IntPtr.Zero, true))
229+
{
230+
try
231+
{
232+
if (GenerateConsoleCtrlEvent((int)CTRL_EVENT.CTRL_C, process.Id))
233+
process.WaitForExit();
234+
}
235+
finally
236+
{
237+
SetConsoleCtrlHandler(IntPtr.Zero, false);
238+
}
239+
}
240+
}
241+
215242
private void FixWindowFrameOnWin10(Window w)
216243
{
217244
var platformHandle = w.TryGetPlatformHandle();

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /