Skip to content

Commit 20baa59

Browse files
committed
kill process on purge
1 parent f7c0174 commit 20baa59

2 files changed

Lines changed: 148 additions & 19 deletions

File tree

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
public class FilePurgerTest
2+
{
3+
[Test]
4+
public async Task DeleteSucceeds_WhenFileNotLocked()
5+
{
6+
var file = Path.Combine(Path.GetTempPath(), $"FilePurgerTest_{Guid.NewGuid()}.verified.txt");
7+
File.WriteAllText(file, "content");
8+
9+
var result = FilePurger.TryDeleteWithLockKill(file);
10+
11+
await Assert.That(result.Deleted).IsTrue();
12+
await Assert.That(result.Exception).IsNull();
13+
await Assert.That(File.Exists(file)).IsFalse();
14+
}
15+
16+
[Test]
17+
public async Task DeleteSucceeds_WhenFileDoesNotExist()
18+
{
19+
var file = Path.Combine(Path.GetTempPath(), $"FilePurgerTest_{Guid.NewGuid()}.verified.txt");
20+
21+
var result = FilePurger.TryDeleteWithLockKill(file);
22+
23+
await Assert.That(result.Deleted).IsTrue();
24+
await Assert.That(result.Exception).IsNull();
25+
}
26+
27+
[Test]
28+
public async Task DeleteSucceeds_WhenFileLocked_KillsLockingProcess()
29+
{
30+
var file = Path.Combine(Path.GetTempPath(), $"FilePurgerTest_{Guid.NewGuid()}.verified.txt");
31+
File.WriteAllText(file, "content");
32+
33+
var lockProcess = StartFileLockProcess(file);
34+
35+
try
36+
{
37+
await Assert.That(IsFileLocked(file)).IsTrue();
38+
39+
var result = FilePurger.TryDeleteWithLockKill(file);
40+
41+
await Assert.That(result.Deleted).IsTrue();
42+
await Assert.That(result.Exception).IsNull();
43+
await Assert.That(File.Exists(file)).IsFalse();
44+
45+
var exited = lockProcess.WaitForExit(5000);
46+
await Assert.That(exited).IsTrue();
47+
}
48+
finally
49+
{
50+
if (!lockProcess.HasExited)
51+
{
52+
lockProcess.Kill();
53+
}
54+
55+
lockProcess.Dispose();
56+
57+
if (File.Exists(file))
58+
{
59+
File.Delete(file);
60+
}
61+
}
62+
}
63+
64+
static Process StartFileLockProcess(string path)
65+
{
66+
var script = $"$f = [System.IO.File]::Open('{path.Replace("'", "''")}', 'Open', 'ReadWrite', 'None'); [Console]::WriteLine('locked'); Start-Sleep -Seconds 60";
67+
var process = new Process
68+
{
69+
StartInfo = new()
70+
{
71+
FileName = "powershell.exe",
72+
Arguments = $"-NoProfile -Command \"{script}\"",
73+
UseShellExecute = false,
74+
CreateNoWindow = true,
75+
RedirectStandardOutput = true
76+
}
77+
};
78+
process.Start();
79+
80+
var line = process.StandardOutput.ReadLine();
81+
if (line != "locked")
82+
{
83+
throw new InvalidOperationException($"Expected 'locked' but got '{line}'");
84+
}
85+
86+
return process;
87+
}
88+
89+
static bool IsFileLocked(string path)
90+
{
91+
try
92+
{
93+
using var stream = File.Open(path, FileMode.Open, FileAccess.ReadWrite, FileShare.None);
94+
return false;
95+
}
96+
catch (IOException)
97+
{
98+
return true;
99+
}
100+
}
101+
}

src/DiffEngineTray/FilePurger.cs

Lines changed: 47 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -53,36 +53,64 @@ static void DeleteFiles(string[] files)
5353
for (var index = 0; index < files.Length; index++)
5454
{
5555
var file = files[index];
56-
try
56+
var result = TryDeleteWithLockKill(file);
57+
if (result.Deleted)
5758
{
58-
if (File.Exists(file))
59-
{
60-
File.Delete(file);
61-
}
59+
continue;
60+
}
61+
62+
var failedResult = AskQuestion(
63+
$"""
64+
Could not delete file: {file}
65+
Exception: {result.Exception!.Message}
66+
""",
67+
"Delete failed",
68+
MessageBoxButtons.AbortRetryIgnore);
69+
70+
if (failedResult == DialogResult.Abort)
71+
{
72+
return;
73+
}
74+
75+
if (failedResult == DialogResult.Retry)
76+
{
77+
index--;
78+
}
79+
}
80+
}
81+
82+
internal static DeleteResult TryDeleteWithLockKill(string file)
83+
{
84+
try
85+
{
86+
if (File.Exists(file))
87+
{
88+
File.Delete(file);
6289
}
63-
catch (Exception exception)
90+
91+
return new(true, null);
92+
}
93+
catch (Exception exception)
94+
{
95+
if (FileLockKiller.KillLockingProcesses(file))
6496
{
65-
var failedResult = AskQuestion(
66-
$"""
67-
Could not delete file: {file}
68-
Exception: {exception.Message}
69-
""",
70-
"Delete failed",
71-
MessageBoxButtons.AbortRetryIgnore);
72-
73-
if (failedResult == DialogResult.Abort)
97+
try
7498
{
75-
return;
99+
File.Delete(file);
100+
return new(true, null);
76101
}
77-
78-
if (failedResult == DialogResult.Retry)
102+
catch (Exception retryException)
79103
{
80-
index--;
104+
return new(false, retryException);
81105
}
82106
}
107+
108+
return new(false, exception);
83109
}
84110
}
85111

112+
internal record DeleteResult(bool Deleted, Exception? Exception);
113+
86114
static DialogResult AskQuestion(string text, string caption, MessageBoxButtons buttons) =>
87115
MessageBox.Show(
88116
text,

0 commit comments

Comments
 (0)