Skip to content

Commit 8fbda46

Browse files
committed
feature: support terminate clone/fetch/pull/push gracefully on macOS
Signed-off-by: leo <longshuang@msn.cn>
1 parent de8e211 commit 8fbda46

8 files changed

Lines changed: 98 additions & 6 deletions

File tree

build/scripts/package.osx-app.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,6 @@ cp resources/app/App.icns SourceGit.app/Contents/Resources/App.icns
1313
sed "s/SOURCE_GIT_VERSION/$VERSION/g" resources/app/App.plist > SourceGit.app/Contents/Info.plist
1414
rm -rf SourceGit.app/Contents/MacOS/SourceGit.dsym
1515
rm -f SourceGit.app/Contents/MacOS/*.pdb
16+
clang ../tools/setsid-macos/setsid.c -o SourceGit.app/Contents/MacOS/setsid -mmacosx-version-min=13.0
1617

1718
zip "sourcegit_$VERSION.$RUNTIME.zip" -r SourceGit.app

src/Commands/Command.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ protected async Task<Result> ReadToEndAsync()
158158

159159
protected ProcessStartInfo CreateGitStartInfo(bool redirect)
160160
{
161-
var useSetSid = OperatingSystem.IsLinux() && CancellationToken.CanBeCanceled;
161+
var useSetSid = CancellationToken.CanBeCanceled && Native.OS.SupportSetSid();
162162
var selfExecFile = Environment.ProcessPath;
163163
var builder = new StringBuilder(2048);
164164

@@ -186,7 +186,7 @@ protected ProcessStartInfo CreateGitStartInfo(bool redirect)
186186
builder.Append(Args);
187187

188188
var start = new ProcessStartInfo();
189-
start.FileName = useSetSid ? "setsid" : Native.OS.GitExecutable;
189+
start.FileName = useSetSid ? Native.OS.GetSetSidExecutable() : Native.OS.GitExecutable;
190190
start.Arguments = builder.ToString();
191191
start.UseShellExecute = false;
192192
start.CreateNoWindow = true;

src/Native/Linux.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,16 @@ public void OpenWithDefaultEditor(string file)
177177
}
178178
}
179179

180+
public bool SupportSetSid()
181+
{
182+
return true;
183+
}
184+
185+
public string GetSetSidExecutable()
186+
{
187+
return "setsid";
188+
}
189+
180190
public void TerminateProcess(Process proc)
181191
{
182192
if (kill(-proc.Id, 15) != 0)

src/Native/MacOS.cs

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,14 @@ namespace SourceGit.Native
1515
[SupportedOSPlatform("macOS")]
1616
internal class MacOS : OS.IBackend
1717
{
18+
[DllImport("/usr/lib/libSystem.B.dylib", SetLastError = true)]
19+
private static extern int kill(int pid, int sig);
20+
21+
public MacOS()
22+
{
23+
_setsidExecutable = Path.Combine(Path.GetDirectoryName(Environment.ProcessPath), "setsid");
24+
}
25+
1826
public void SetupApp(AppBuilder builder)
1927
{
2028
builder.With(new MacOSPlatformOptions()
@@ -122,10 +130,46 @@ public void OpenWithDefaultEditor(string file)
122130
Process.Start("open", file.Quoted());
123131
}
124132

133+
public bool SupportSetSid()
134+
{
135+
return File.Exists(_setsidExecutable);
136+
}
137+
138+
public string GetSetSidExecutable()
139+
{
140+
return _setsidExecutable;
141+
}
142+
125143
public void TerminateProcess(Process proc)
126144
{
127-
proc.Kill(true);
145+
if (!SupportSetSid())
146+
{
147+
proc.Kill(true);
148+
return;
149+
}
150+
151+
if (kill(-proc.Id, 15) != 0)
152+
{
153+
// If the process already exited, we can just ignore the error.
154+
if (Marshal.GetLastPInvokeError() == 3 /* ESRCH */)
155+
return;
156+
157+
// Actually, this will not be called since the process is
158+
// spawned by us (EPERM will not happen), and SIGTERM (15)
159+
// is a valid signal (EINVAL will not happen).
160+
// See https://www.man7.org/linux/man-pages/man2/kill.2.html
161+
try
162+
{
163+
proc.Kill(true);
164+
}
165+
catch
166+
{
167+
// Ignore any errors when trying to kill the process
168+
}
169+
}
128170
}
171+
172+
private string _setsidExecutable = null;
129173
}
130174

131175
[SupportedOSPlatform("macOS")]

src/Native/OS.cs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ public interface IBackend
3535
void OpenBrowser(string url);
3636
void OpenWithDefaultEditor(string file);
3737

38+
bool SupportSetSid();
39+
string GetSetSidExecutable();
3840
void TerminateProcess(Process proc);
3941
}
4042

@@ -303,9 +305,19 @@ public static string GetRelativePathToHome(string path)
303305
return path;
304306
}
305307

308+
public static bool SupportSetSid()
309+
{
310+
return _backend.SupportSetSid();
311+
}
312+
313+
public static string GetSetSidExecutable()
314+
{
315+
return _backend.GetSetSidExecutable();
316+
}
317+
306318
public static void TerminateProcess(Process proc)
307319
{
308-
_backend?.TerminateProcess(proc);
320+
_backend.TerminateProcess(proc);
309321
}
310322

311323
private static void UpdateGitVersion()

src/Native/Windows.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,16 @@ public void OpenWithDefaultEditor(string file)
222222
Process.Start(start);
223223
}
224224

225+
public bool SupportSetSid()
226+
{
227+
return false;
228+
}
229+
230+
public string GetSetSidExecutable()
231+
{
232+
return null;
233+
}
234+
225235
public void TerminateProcess(Process proc)
226236
{
227237
var pid = proc.Id;

src/ViewModels/Popup.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,7 @@ public bool CanTerminate
1212
get => _canTerminate;
1313
protected set
1414
{
15-
// Terminating a process is only supported on Windows/Linux.
16-
if (!OperatingSystem.IsMacOS())
15+
if (OperatingSystem.IsWindows() || Native.OS.SupportSetSid())
1716
SetProperty(ref _canTerminate, value);
1817
}
1918
}

tools/setsid-macos/setsid.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#include <stdlib.h>
2+
#include <unistd.h>
3+
#include <err.h>
4+
5+
int main(int argc, char* argv[]) {
6+
if (argc < 2) {
7+
err(EXIT_FAILURE, "Usage: setsid <program> [arguments ...]");
8+
}
9+
10+
if (setsid() < 0) {
11+
err(EXIT_FAILURE, "setsid failed");
12+
}
13+
14+
execvp(argv[1], argv + 1);
15+
err(EXIT_FAILURE, "Failed to execute %s", argv[1]);
16+
}

0 commit comments

Comments
 (0)