From d253388c8eb8e7ebfcc9a6391a1b4de969fbd45e Mon Sep 17 00:00:00 2001 From: Thomas Goyne Date: Sun, 16 Aug 2015 18:17:33 -0700 Subject: [PATCH] Perform autosaves on a background thread rather than blocking the UI --- src/subs_controller.cpp | 61 +++++++++++++++++++++++------------------ src/subs_controller.h | 17 ++++++++---- 2 files changed, 47 insertions(+), 31 deletions(-) diff --git a/src/subs_controller.cpp b/src/subs_controller.cpp index 318cc7a3d..74cfbca39 100644 --- a/src/subs_controller.cpp +++ b/src/subs_controller.cpp @@ -32,6 +32,7 @@ #include "subtitle_format.h" #include "text_selection_controller.h" +#include #include #include #include @@ -146,27 +147,18 @@ SubsController::SubsController(agi::Context *context) : context(context) , undo_connection(context->ass->AddUndoManager(&SubsController::OnCommit, this)) , text_selection_connection(context->textSelectionController->AddSelectionListener(&SubsController::OnTextSelectionChanged, this)) +, autosave_queue(agi::dispatch::Create()) { autosave_timer_changed(&autosave_timer); OPT_SUB("App/Auto/Save", [=] { autosave_timer_changed(&autosave_timer); }); OPT_SUB("App/Auto/Save Every Seconds", [=] { autosave_timer_changed(&autosave_timer); }); - - autosave_timer.Bind(wxEVT_TIMER, [=](wxTimerEvent&) { - try { - auto fn = AutoSave(); - if (!fn.empty()) - context->frame->StatusTimeout(fmt_tl("File backup saved as \"%s\".", fn)); - } - catch (const agi::Exception& err) { - context->frame->StatusTimeout(to_wx("Exception when attempting to autosave file: " + err.GetMessage())); - } - catch (...) { - context->frame->StatusTimeout("Unhandled exception when attempting to autosave file."); - } - }); + autosave_timer.Bind(wxEVT_TIMER, [=](wxTimerEvent&) { AutoSave(); }); } -SubsController::~SubsController() { } +SubsController::~SubsController() { + // Make sure there are no autosaves in progress + autosave_queue->Sync([]{ }); +} void SubsController::SetSelectionController(SelectionController *selection_controller) { active_line_connection = context->selectionController->AddActiveLineListener(&SubsController::OnActiveLineChanged, this); @@ -260,26 +252,43 @@ int SubsController::TryToClose(bool allow_cancel) const { return result; } -agi::fs::path SubsController::AutoSave() { +void SubsController::AutoSave() { if (commit_id == autosaved_commit_id) - return ""; + return; - auto path = config::path->Decode(OPT_GET("Path/Auto/Save")->GetString()); - if (path.empty()) - path = filename.parent_path(); - - agi::fs::CreateDirectory(path); + auto directory = config::path->Decode(OPT_GET("Path/Auto/Save")->GetString()); + if (directory.empty()) + directory = filename.parent_path(); auto name = filename.filename(); if (name.empty()) name = "Untitled"; - path /= agi::format("%s.%s.AUTOSAVE.ass", name.string(), agi::util::strftime("%Y-%m-%d-%H-%M-%S")); - - SubtitleFormat::GetWriter(path)->WriteFile(context->ass.get(), path, 0); autosaved_commit_id = commit_id; + auto frame = context->frame; + auto subs_copy = new AssFile(*context->ass); + autosave_queue->Async([subs_copy, name, directory, frame] { + wxString msg; + std::unique_ptr subs(subs_copy); - return path; + try { + agi::fs::CreateDirectory(directory); + auto path = directory / agi::format("%s.%s.AUTOSAVE.ass", name.string(), + agi::util::strftime("%Y-%m-%d-%H-%M-%S")); + SubtitleFormat::GetWriter(path)->WriteFile(subs.get(), path, 0); + msg = fmt_tl("File backup saved as \"%s\".", path); + } + catch (const agi::Exception& err) { + msg = to_wx("Exception when attempting to autosave file: " + err.GetMessage()); + } + catch (...) { + msg = "Unhandled exception when attempting to autosave file."; + } + + agi::dispatch::Main().Async([frame, msg] { + frame->StatusTimeout(msg); + }); + }); } bool SubsController::CanSave() const { diff --git a/src/subs_controller.h b/src/subs_controller.h index 9c58fd815..82e44a4b0 100644 --- a/src/subs_controller.h +++ b/src/subs_controller.h @@ -22,7 +22,12 @@ #include class SelectionController; -namespace agi { struct Context; } +namespace agi { + namespace dispatch { + class Queue; + } + struct Context; +} struct AssFileCommit; struct ProjectProperties; @@ -47,6 +52,9 @@ class SubsController { /// Timer for triggering autosaves wxTimer autosave_timer; + /// Queue which autosaves are performed on + std::unique_ptr autosave_queue; + /// A new file has been opened (filename) agi::signal::Signal FileOpen; /// The file has been saved @@ -58,6 +66,9 @@ class SubsController { /// Set the filename, updating things like the MRU and last used path void SetFileName(agi::fs::path const& file); + /// Autosave the file if there have been any chances since the last autosave + void AutoSave(); + void OnCommit(AssFileCommit c); void OnActiveLineChanged(); void OnSelectionChanged(); @@ -97,10 +108,6 @@ public: /// @return wxYES, wxNO or wxCANCEL (note: all three are true in a boolean context) int TryToClose(bool allow_cancel = true) const; - /// @brief Autosave the file if there have been any chances since the last autosave - /// @return File name used or empty if no save was performed - agi::fs::path AutoSave(); - /// Can the file be saved in its current format? bool CanSave() const;