Skip to content
This repository was archived by the owner on Oct 16, 2020. It is now read-only.

Commit d303714

Browse files
fix #524: Reentrancy in 'external change' dialogs
1 parent ff7b95d commit d303714

2 files changed

Lines changed: 82 additions & 32 deletions

File tree

src/Main/Base/Project/Src/Project/ProjectChangeWatcher.cs

Lines changed: 29 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -172,27 +172,36 @@ static void MainFormActivated(object sender, EventArgs e)
172172

173173
static void MainFormActivated()
174174
{
175-
if (wasChangedExternally && !showingMessageBox) {
176-
177-
if (ProjectService.OpenSolution != null) {
178-
// Set wasChangedExternally=false only after the dialog is closed,
179-
// so that additional changes to the project while the dialog is open
180-
// don't cause it to appear twice.
181-
182-
// The MainFormActivated event occurs when the dialog is closed before
183-
// we get a change to set wasChangedExternally=false, so we use 'showingMessageBox'
184-
// to prevent the dialog from appearing infititely.
185-
showingMessageBox = true;
186-
int result = MessageService.ShowCustomDialog(MessageService.DefaultMessageBoxTitle, "${res:ICSharpCode.SharpDevelop.Project.SolutionAlteredExternallyMessage}", 0, 1, "${res:ICSharpCode.SharpDevelop.Project.ReloadSolution}", "${res:ICSharpCode.SharpDevelop.Project.KeepOldSolution}", "${res:ICSharpCode.SharpDevelop.Project.CloseSolution}");
187-
showingMessageBox = false;
188-
wasChangedExternally = false;
189-
if (result == 0)
190-
SD.ProjectService.OpenSolutionOrProject(ProjectService.OpenSolution.FileName);
191-
else if (result == 2)
192-
new CloseSolution().Run();
193-
} else {
194-
wasChangedExternally = false;
175+
if (wasChangedExternally) {
176+
if (!showingMessageBox) {
177+
if (ProjectService.OpenSolution != null) {
178+
// Set wasChangedExternally=false only after the dialog is closed,
179+
// so that additional changes to the project while the dialog is open
180+
// don't cause it to appear twice.
181+
182+
// The MainFormActivated event occurs when the dialog is closed before
183+
// we get a change to set wasChangedExternally=false, so we use 'showingMessageBox'
184+
// to prevent the dialog from appearing infititely.
185+
showingMessageBox = true;
186+
int result = MessageService.ShowCustomDialog(MessageService.DefaultMessageBoxTitle, "${res:ICSharpCode.SharpDevelop.Project.SolutionAlteredExternallyMessage}", 0, 1, "${res:ICSharpCode.SharpDevelop.Project.ReloadSolution}", "${res:ICSharpCode.SharpDevelop.Project.KeepOldSolution}", "${res:ICSharpCode.SharpDevelop.Project.CloseSolution}");
187+
showingMessageBox = false;
188+
wasChangedExternally = false;
189+
if (result == 1) {
190+
FileChangeWatcher.AskForReload();
191+
} else {
192+
FileChangeWatcher.CancelReloadQueue();
193+
if (result == 0) {
194+
SD.ProjectService.OpenSolutionOrProject(ProjectService.OpenSolution.FileName);
195+
} else {
196+
new CloseSolution().Run();
197+
}
198+
}
199+
} else {
200+
wasChangedExternally = false;
201+
}
195202
}
203+
} else {
204+
FileChangeWatcher.AskForReload();
196205
}
197206
}
198207

src/Main/Base/Project/Workbench/FileChangeWatcher.cs

Lines changed: 53 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,7 @@
1818

1919
using System;
2020
using System.Collections.Generic;
21-
using System.Diagnostics;
2221
using System.IO;
23-
using System.Windows.Forms;
24-
using ICSharpCode.SharpDevelop.Gui;
2522
using ICSharpCode.Core;
2623

2724
namespace ICSharpCode.SharpDevelop.Workbench
@@ -206,21 +203,65 @@ void MainForm_Activated(object sender, EventArgs e)
206203
string fileName = file.FileName;
207204
if (!File.Exists(fileName))
208205
return;
209-
210-
string message = StringParser.Parse(
211-
"${res:ICSharpCode.SharpDevelop.DefaultEditor.Gui.Editor.TextEditorDisplayBinding.FileAlteredMessage}",
212-
new StringTagPair("File", Path.GetFullPath(fileName))
213-
);
214-
if ((AutoLoadExternalChangesOption && file.IsDirty == false)
215-
|| MessageService.AskQuestion(message, StringParser.Parse("${res:MainWindow.DialogName}")))
216-
{
206+
207+
if (AutoLoadExternalChangesOption && !file.IsDirty) {
217208
if (File.Exists(fileName)) {
218209
file.ReloadFromDisk();
219210
}
220211
} else {
221-
file.MakeDirty();
212+
QueueFileForReloadDialog(file);
213+
}
214+
}
215+
}
216+
217+
#region Reload Queue
218+
static readonly HashSet<OpenedFile> queue = new HashSet<OpenedFile>();
219+
static volatile bool currentlyReloading;
220+
221+
static void QueueFileForReloadDialog(OpenedFile file)
222+
{
223+
if (file == null)
224+
throw new ArgumentNullException("file");
225+
lock (queue) {
226+
queue.Add(file);
227+
}
228+
}
229+
230+
public static void AskForReload()
231+
{
232+
if (currentlyReloading) return;
233+
currentlyReloading = true;
234+
try {
235+
lock (queue) {
236+
foreach (var file in queue) {
237+
string fileName = file.FileName;
238+
if (!File.Exists(fileName))
239+
continue;
240+
string message = StringParser.Parse(
241+
"${res:ICSharpCode.SharpDevelop.DefaultEditor.Gui.Editor.TextEditorDisplayBinding.FileAlteredMessage}",
242+
new StringTagPair("File", Path.GetFullPath(fileName))
243+
);
244+
if (SD.MessageService.AskQuestion(message, StringParser.Parse("${res:MainWindow.DialogName}"))) {
245+
if (File.Exists(fileName)) {
246+
file.ReloadFromDisk();
247+
}
248+
} else {
249+
file.MakeDirty();
250+
}
251+
}
252+
queue.Clear();
222253
}
254+
} finally {
255+
currentlyReloading = false;
256+
}
257+
}
258+
259+
public static void CancelReloadQueue()
260+
{
261+
lock (queue) {
262+
queue.Clear();
223263
}
224264
}
265+
#endregion
225266
}
226267
}

0 commit comments

Comments
 (0)