From 79fdce5ca642efea0bbd7ca7f61803654f2da0bc Mon Sep 17 00:00:00 2001 From: "kenneth.skovhede@gmail.com" Date: Wed, 1 Sep 2010 21:04:18 +0000 Subject: [PATCH] Update issue #185 Status: Fixed The code to support this was already in place, but did not work because Application.Exit() had been invoked, making all messageboxes return Cancel without showing. I also improved the logic a bit so a cancel does not invoke the cleanup afterwards, making the exit procedure faster. I also added checks for the stop command to the code that fetches the signature files, to make the application respond faster to the stop command. git-svn-id: https://duplicati.googlecode.com/svn/trunk@490 59da171f-624f-0410-aa54-27559c288bec --- Duplicati/GUI/DuplicatiRunner.cs | 8 ++- Duplicati/GUI/MainForm.cs | 65 +++++++++++++++++++ Duplicati/GUI/Program.cs | 32 --------- Duplicati/GUI/WizardHandler.cs | 5 ++ Duplicati/GUI/WorkerThread.cs | 12 ++++ Duplicati/Library/Main/BackendWrapper.cs | 20 +++++- Duplicati/Library/Main/Interface.cs | 8 ++- .../LiveControl/ExecutionStoppedException.cs | 2 +- Duplicati/Library/Main/RSyncDir.cs | 27 +++++--- 9 files changed, 132 insertions(+), 47 deletions(-) diff --git a/Duplicati/GUI/DuplicatiRunner.cs b/Duplicati/GUI/DuplicatiRunner.cs index 05b665148..c19c819d1 100644 --- a/Duplicati/GUI/DuplicatiRunner.cs +++ b/Duplicati/GUI/DuplicatiRunner.cs @@ -59,6 +59,7 @@ namespace Duplicati.GUI string destination = task.GetConfiguration(options); string results = ""; + bool isAbortException = false; try { @@ -224,7 +225,12 @@ namespace Duplicati.GUI { //TODO: Extract ex.Message and save it in seperate field in the database if (ex is System.Threading.ThreadAbortException) + { + isAbortException = true; System.Threading.Thread.ResetAbort(); + } + else if (ex is Library.Main.LiveControl.ExecutionStoppedException) + isAbortException = true; while (ex is System.Reflection.TargetInvocationException && ex.InnerException != null) ex = ex.InnerException; @@ -240,7 +246,7 @@ namespace Duplicati.GUI try { - if (task.TaskType == DuplicityTaskType.FullBackup || task.TaskType == DuplicityTaskType.IncrementalBackup) + if (!isAbortException && (task.TaskType == DuplicityTaskType.FullBackup || task.TaskType == DuplicityTaskType.IncrementalBackup)) { if (task.Schedule.Task.KeepFull > 0) { diff --git a/Duplicati/GUI/MainForm.cs b/Duplicati/GUI/MainForm.cs index 6b8318fc7..23d1fc5df 100644 --- a/Duplicati/GUI/MainForm.cs +++ b/Duplicati/GUI/MainForm.cs @@ -179,6 +179,15 @@ namespace Duplicati.GUI Program.LiveControl.Pause(); Program.Runner.Stop(); + + TrayIcon.Visible = false; + if (StatusDialog != null && StatusDialog.Visible) + StatusDialog.Close(); + if (WizardDialog != null && WizardDialog.Visible) + WizardDialog.Close(); + + EnsureBackupIsTerminated(); + Application.Exit(); } @@ -305,5 +314,61 @@ namespace Duplicati.GUI return false; } + + private void EnsureBackupIsTerminated() + { + if (Program.Runner != null && Program.WorkThread != null && Program.WorkThread.Active) + { + //Make sure no new items can enter the queue + if (Program.Scheduler != null) + Program.Scheduler.Terminate(true); + + //We want no new items to enter the queue + Program.WorkThread.Terminate(false); + + Program.Runner.Pause(); + if (!Program.Runner.IsStopRequested) + Program.Runner.Stop(); + + //Wait 15 seconds to see if the stop works + for (int i = 0; i < 15; i++) + { + Program.WorkThread.Join(1000); + Application.DoEvents(); + if (!Program.WorkThread.Active) + break; + } + + while (Program.WorkThread.Active) + { + //Ask the user if we should abort + if (MessageBox.Show(Strings.Program.TerminateForExitQuestion, Application.ProductName, MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.No) + { + //Abort the thread + Program.Runner.Terminate(); + + //Give last 5 second chance to write the remaining data + int i = 5; + while (i > 0 && !Program.WorkThread.Join(1000)) + { + //The Join call blocks the main thread, so let pending events through + Application.DoEvents(); + i--; + } + break; + } + + //Wait 18 * 10 seconds = 3 minutes before asking again + for (int i = 0; i < 18; i++) + { + Program.WorkThread.Join(1000 * 10); + Application.DoEvents(); + + if (!Program.WorkThread.Active) + break; + } + } + } + } } } diff --git a/Duplicati/GUI/Program.cs b/Duplicati/GUI/Program.cs index c80dc23af..5a978a783 100644 --- a/Duplicati/GUI/Program.cs +++ b/Duplicati/GUI/Program.cs @@ -207,38 +207,6 @@ namespace Duplicati.GUI MessageBox.Show(string.Format(Strings.Program.SeriousError, ex.ToString()), Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error); } - if (Runner != null && WorkThread != null && WorkThread.Active) - { - Runner.Pause(); - if (!Runner.IsStopRequested) - Runner.Stop(); - - //Wait 10 seconds to see if the stop works - for (int i = 0; i < 10; i++) - { - System.Threading.Thread.Sleep(1000); - if (!WorkThread.Active) - break; - } - - while (WorkThread.Active) - { - if (MessageBox.Show(Strings.Program.TerminateForExitQuestion, Application.ProductName, MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.No) - { - Runner.Terminate(); - System.Threading.Thread.Sleep(500); - break; - } - - //Wait 18 * 10 seconds = 3 minutes before asking again - for (int i = 0; i < 18; i++) - { - System.Threading.Thread.Sleep(1000 * 10); - if (!WorkThread.Active) - break; - } - } - } if (Scheduler != null) Scheduler.Terminate(true); diff --git a/Duplicati/GUI/WizardHandler.cs b/Duplicati/GUI/WizardHandler.cs index afd4d882c..8e406bca1 100644 --- a/Duplicati/GUI/WizardHandler.cs +++ b/Duplicati/GUI/WizardHandler.cs @@ -273,6 +273,11 @@ namespace Duplicati.GUI (m_form as Form).ShowDialog(); } + public void Close() + { + if ((m_form as Form).Visible) + (m_form as Form).Close(); + } private bool AskToResumeIfPaused() { diff --git a/Duplicati/GUI/WorkerThread.cs b/Duplicati/GUI/WorkerThread.cs index df9bce611..b61fbb0c7 100644 --- a/Duplicati/GUI/WorkerThread.cs +++ b/Duplicati/GUI/WorkerThread.cs @@ -295,5 +295,17 @@ namespace Duplicati.GUI m_state = RunState.Paused; m_event.Set(); } + + /// + /// Waits the specified number of milliseconds for the thread to terminate + /// + /// The number of milliseconds to wait + /// True if the thread is terminated, false if a timeout occured + public bool Join(int millisecondTimeout) + { + if (m_thread != null) + return m_thread.Join(millisecondTimeout); + return true; + } } } diff --git a/Duplicati/Library/Main/BackendWrapper.cs b/Duplicati/Library/Main/BackendWrapper.cs index e43beedf6..6e3ffa029 100644 --- a/Duplicati/Library/Main/BackendWrapper.cs +++ b/Duplicati/Library/Main/BackendWrapper.cs @@ -574,6 +574,11 @@ namespace Duplicati.Library.Main m_statistics.NumberOfRemoteCalls++; return m_backend.List(); } + catch (System.Threading.ThreadAbortException tex) + { + lastEx = tex; + retries = 0; + } catch (Exception ex) { lastEx = ex; @@ -601,6 +606,11 @@ namespace Duplicati.Library.Main m_backend.Delete(remote.Filename); lastEx = null; } + catch (System.Threading.ThreadAbortException tex) + { + lastEx = tex; + retries = 0; + } catch (Exception ex) { lastEx = ex; @@ -682,7 +692,7 @@ namespace Duplicati.Library.Main { try { - using(Library.Interface.IEncryption enc = DynamicLoader.EncryptionLoader.GetModule(remote.EncryptionMode, m_options.Passphrase, m_options.RawOptions)) + using (Library.Interface.IEncryption enc = DynamicLoader.EncryptionLoader.GetModule(remote.EncryptionMode, m_options.Passphrase, m_options.RawOptions)) enc.Decrypt(tempfile, filename); } catch (Exception ex) @@ -724,6 +734,11 @@ namespace Duplicati.Library.Main } } } + catch (System.Threading.ThreadAbortException tex) + { + lastEx = tex; + retries = 0; + } catch (Exception ex) { lastEx = ex; @@ -821,9 +836,8 @@ namespace Duplicati.Library.Main } catch (System.Threading.ThreadAbortException tex) { - //Do not retry after we are aborted lastEx = tex; - break; + retries = 0; } catch (Exception ex) { diff --git a/Duplicati/Library/Main/Interface.cs b/Duplicati/Library/Main/Interface.cs index 91ece37cd..29a62196c 100644 --- a/Duplicati/Library/Main/Interface.cs +++ b/Duplicati/Library/Main/Interface.cs @@ -197,8 +197,12 @@ namespace Duplicati.Library.Main entries.Add(backupsets[backupsets.Count - 1]); entries.AddRange(backupsets[backupsets.Count - 1].Incrementals); + //Check before we start the download + CheckLiveControl(); patches = FindPatches(backend, entries, tempfolder); + //Check before we start the download + CheckLiveControl(); Manifestfile latest = GetManifest(backend, backupsets[0]); //Manifest version 1 does not support multiple folders @@ -1136,7 +1140,6 @@ namespace Duplicati.Library.Main { m_progress += unitCost; - OperationProgress(this, DuplicatiOperation.Backup, (int)(m_progress * 100), -1, string.Format(Strings.Interface.StatusReadingManifest, be.Time.ToShortDateString() + " " + be.Time.ToShortTimeString()), ""); Manifestfile manifest = GetManifest(backend, be); @@ -1157,6 +1160,9 @@ namespace Duplicati.Library.Main string filename = System.IO.Path.Combine(tempfolder, "patch-" + patches.Count.ToString() + ".zip"); + //Check just before we download stuff + CheckLiveControl(); + using (new Logging.Timer("Get " + bes.Key.Filename)) backend.Get(bes.Key, filename, manifest.SignatureHashes == null ? null : manifest.SignatureHashes[bes.Key.Volumenumber - 1]); diff --git a/Duplicati/Library/Main/LiveControl/ExecutionStoppedException.cs b/Duplicati/Library/Main/LiveControl/ExecutionStoppedException.cs index f4e13166b..ff8347b5b 100644 --- a/Duplicati/Library/Main/LiveControl/ExecutionStoppedException.cs +++ b/Duplicati/Library/Main/LiveControl/ExecutionStoppedException.cs @@ -23,7 +23,7 @@ using System.Text; namespace Duplicati.Library.Main.LiveControl { - class ExecutionStoppedException : Exception + public class ExecutionStoppedException : Exception { public ExecutionStoppedException() : base(Strings.ExecutionStoppedException.DefaultMessage) diff --git a/Duplicati/Library/Main/RSyncDir.cs b/Duplicati/Library/Main/RSyncDir.cs index 42c347fe6..0d565d4f0 100644 --- a/Duplicati/Library/Main/RSyncDir.cs +++ b/Duplicati/Library/Main/RSyncDir.cs @@ -1558,17 +1558,26 @@ namespace Duplicati.Library.Main.RSync { BackupStatistics bs = m_stat as BackupStatistics; - bs.DeletedFiles = m_deletedfiles.Count; - bs.DeletedFolders = m_deletedfolders.Count; - bs.ModifiedFiles = m_diffedfiles; - bs.AddedFiles = m_addedfiles; - bs.ExaminedFiles = m_examinedfiles; - bs.SizeOfModifiedFiles = m_diffedfilessize; - bs.SizeOfAddedFiles = m_addedfilessize; - bs.SizeOfExaminedFiles = m_examinedfilesize; + if (m_deletedfiles != null) + bs.DeletedFiles = m_deletedfiles.Count; + if (m_deletedfolders != null) + bs.DeletedFolders = m_deletedfolders.Count; + if (m_diffedfiles != null) + bs.ModifiedFiles = m_diffedfiles; + if (m_addedfiles != null) + bs.AddedFiles = m_addedfiles; + if (m_examinedfiles != null) + bs.ExaminedFiles = m_examinedfiles; + if (m_diffedfilessize != null) + bs.SizeOfModifiedFiles = m_diffedfilessize; + if (m_addedfilessize != null) + bs.SizeOfAddedFiles = m_addedfilessize; + if (m_examinedfilesize != null) + bs.SizeOfExaminedFiles = m_examinedfilesize; if (m_unproccesed != null && m_unproccesed.Files != null) bs.UnprocessedFiles = m_unproccesed.Files.Count; - bs.AddedFolders = m_newfolders.Count; + if (m_newfolders != null) + bs.AddedFolders = m_newfolders.Count; } if (m_snapshot != null)