From 0bde95650d7e05bedec7f8a6510246bf0abe87cc Mon Sep 17 00:00:00 2001 From: David Capello Date: Mon, 6 Apr 2015 12:06:50 -0300 Subject: [PATCH] Save document backups in background session each 5 minutes --- src/app/CMakeLists.txt | 1 + src/app/crash/backup_observer.cpp | 85 +++++++++++++++++++++++++++++++ src/app/crash/backup_observer.h | 54 ++++++++++++++++++++ src/app/crash/data_recovery.cpp | 30 +++-------- src/app/crash/data_recovery.h | 13 ++--- src/app/crash/session.cpp | 35 ++++++++++++- src/app/crash/session.h | 4 ++ 7 files changed, 187 insertions(+), 35 deletions(-) create mode 100644 src/app/crash/backup_observer.cpp create mode 100644 src/app/crash/backup_observer.h diff --git a/src/app/CMakeLists.txt b/src/app/CMakeLists.txt index 14db47117..8efb811ed 100644 --- a/src/app/CMakeLists.txt +++ b/src/app/CMakeLists.txt @@ -225,6 +225,7 @@ add_library(app-lib console.cpp context.cpp context_flags.cpp + crash/backup_observer.cpp crash/data_recovery.cpp crash/session.cpp document.cpp diff --git a/src/app/crash/backup_observer.cpp b/src/app/crash/backup_observer.cpp new file mode 100644 index 000000000..ec75f8e6c --- /dev/null +++ b/src/app/crash/backup_observer.cpp @@ -0,0 +1,85 @@ +// Aseprite +// Copyright (C) 2001-2015 David Capello +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "app/crash/backup_observer.h" + +#include "app/crash/session.h" +#include "app/document.h" +#include "base/bind.h" +#include "base/remove_from_container.h" +#include "base/scoped_lock.h" +#include "doc/context.h" + +namespace app { +namespace crash { + +BackupObserver::BackupObserver(Session* session, doc::Context* ctx) + : m_session(session) + , m_ctx(ctx) + , m_done(false) + , m_thread(Bind(&BackupObserver::backgroundThread, this)) +{ + m_ctx->addObserver(this); + m_ctx->documents().addObserver(this); +} + +BackupObserver::~BackupObserver() +{ + m_thread.join(); + m_ctx->documents().removeObserver(this); + m_ctx->removeObserver(this); +} + +void BackupObserver::stop() +{ + m_done = true; +} + +void BackupObserver::onAddDocument(doc::Document* document) +{ + TRACE("DataRecovery: Observe document %p\n", document); + base::scoped_lock hold(m_mutex); + m_documents.push_back(document); +} + +void BackupObserver::onRemoveDocument(doc::Document* document) +{ + TRACE("DataRecovery:: Remove document %p\n", document); + base::scoped_lock hold(m_mutex); + base::remove_from_container(m_documents, document); +} + +void BackupObserver::backgroundThread() +{ + int counter = 0; + + while (!m_done) { + counter++; + if (counter == 5*60) { // Each 5 minutes + counter = 0; + + base::scoped_lock hold(m_mutex); + TRACE("DataRecovery: Start backup process for %d documents\n", m_documents.size()); + for (doc::Document* doc : m_documents) { + try { + m_session->saveDocumentChanges(static_cast(doc)); + } + catch (const std::exception&) { + TRACE("DataRecovery: Document '%d' is locked\n", doc->id()); + } + } + } + base::this_thread::sleep_for(1.0); + } +} + +} // namespace crash +} // namespace app diff --git a/src/app/crash/backup_observer.h b/src/app/crash/backup_observer.h new file mode 100644 index 000000000..faa5e6e3b --- /dev/null +++ b/src/app/crash/backup_observer.h @@ -0,0 +1,54 @@ +// Aseprite +// Copyright (C) 2001-2015 David Capello +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. + +#ifndef APP_CRASH_BACKUP_OBSERVER_H_INCLUDED +#define APP_CRASH_BACKUP_OBSERVER_H_INCLUDED +#pragma once + +#include "base/mutex.h" +#include "base/thread.h" +#include "doc/context_observer.h" +#include "doc/document_observer.h" +#include "doc/documents_observer.h" + +#include + +namespace doc { + class Context; +} + +namespace app { +namespace crash { + class Session; + + class BackupObserver : public doc::ContextObserver + , public doc::DocumentsObserver + , public doc::DocumentObserver { + public: + BackupObserver(Session* session, doc::Context* ctx); + ~BackupObserver(); + + void stop(); + + void onAddDocument(doc::Document* document) override; + void onRemoveDocument(doc::Document* document) override; + + private: + void backgroundThread(); + + Session* m_session; + base::mutex m_mutex; + doc::Context* m_ctx; + std::vector m_documents; + bool m_done; + base::thread m_thread; + }; + +} // namespace crash +} // namespace app + +#endif diff --git a/src/app/crash/data_recovery.cpp b/src/app/crash/data_recovery.cpp index e856fde0c..f5a3d9437 100644 --- a/src/app/crash/data_recovery.cpp +++ b/src/app/crash/data_recovery.cpp @@ -11,24 +11,19 @@ #include "app/crash/data_recovery.h" +#include "app/crash/backup_observer.h" #include "app/crash/session.h" -#include "app/document.h" #include "app/resource_finder.h" -#include "app/ui_context.h" -#include "base/convert_to.h" #include "base/fs.h" #include "base/path.h" -#include "base/process.h" -#include "base/string.h" -#include "base/thread.h" #include "base/time.h" namespace app { namespace crash { -DataRecovery::DataRecovery(doc::Context* context) +DataRecovery::DataRecovery(doc::Context* ctx) : m_inProgress(nullptr) - , m_context(context) + , m_backup(nullptr) { ResourceFinder rf; rf.includeUserDir(base::join_path("sessions", ".").c_str()); @@ -83,29 +78,16 @@ DataRecovery::DataRecovery(doc::Context* context) m_inProgress->create(pid); TRACE("DataRecovery: Session in progress '%s'\n", newSessionDir.c_str()); - m_context->addObserver(this); - m_context->documents().addObserver(this); + m_backup = new BackupObserver(m_inProgress.get(), ctx); } DataRecovery::~DataRecovery() { - m_context->documents().removeObserver(this); - m_context->removeObserver(this); + m_backup->stop(); + delete m_backup; m_inProgress.reset(); } -void DataRecovery::onAddDocument(doc::Document* document) -{ - TRACE("DataRecovery: Observe document %p\n", document); - document->addObserver(this); -} - -void DataRecovery::onRemoveDocument(doc::Document* document) -{ - TRACE("DataRecovery:: Remove document %p\n", document); - document->removeObserver(this); -} - } // namespace crash } // namespace app diff --git a/src/app/crash/data_recovery.h b/src/app/crash/data_recovery.h index 76a8999a6..37d1753ba 100644 --- a/src/app/crash/data_recovery.h +++ b/src/app/crash/data_recovery.h @@ -11,9 +11,6 @@ #include "app/crash/session.h" #include "base/disable_copying.h" -#include "doc/context_observer.h" -#include "doc/document_observer.h" -#include "doc/documents_observer.h" #include @@ -23,10 +20,9 @@ namespace doc { namespace app { namespace crash { + class BackupObserver; - class DataRecovery : public doc::ContextObserver - , public doc::DocumentsObserver - , public doc::DocumentObserver { + class DataRecovery { public: typedef std::vector Sessions; @@ -37,12 +33,9 @@ namespace crash { const Sessions& sessions() { return m_sessions; } private: - virtual void onAddDocument(doc::Document* document) override; - virtual void onRemoveDocument(doc::Document* document) override; - Sessions m_sessions; SessionPtr m_inProgress; - doc::Context* m_context; + BackupObserver* m_backup; DISABLE_COPYING(DataRecovery); }; diff --git a/src/app/crash/session.cpp b/src/app/crash/session.cpp index 95c2abc35..f234149ea 100644 --- a/src/app/crash/session.cpp +++ b/src/app/crash/session.cpp @@ -11,6 +11,12 @@ #include "app/crash/session.h" +#include "app/context.h" +#include "app/document.h" +#include "app/document_access.h" +#include "app/file/file.h" +#include "base/bind.h" +#include "base/convert_to.h" #include "base/fs.h" #include "base/path.h" #include "base/process.h" @@ -36,7 +42,11 @@ bool Session::isRunning() bool Session::isEmpty() { - return !base::is_file(dataFilename()); + for (auto& item : base::list_files(m_path)) { + if (base::get_file_extension(item) == "ase") + return false; + } + return true; } void Session::create(base::pid pid) @@ -53,6 +63,29 @@ void Session::removeFromDisk() base::remove_directory(m_path); } +void Session::saveDocumentChanges(app::Document* doc) +{ + DocumentReader reader(doc); + DocumentWriter writer(reader); + app::Context ctx; + std::string fn = base::join_path(m_path, + base::convert_to(doc->id()) + ".ase"); + TRACE("DataRecovery: Saving document '%s'...\n", fn.c_str()); + + FileOp* fop = fop_to_save_document(&ctx, + static_cast(doc), fn.c_str(), ""); + if (!fop) { + TRACE("DataRecovery: Cannot create save file operation\n"); + return; + } + + fop_operate(fop, NULL); + fop_done(fop); + if (fop->has_error()) + TRACE("DataRecovery: Error saving changes '%s'\n", fop->error.c_str()); + fop_free(fop); +} + void Session::loadPid() { if (m_pid) diff --git a/src/app/crash/session.h b/src/app/crash/session.h index 8427f3b3b..4cc80f1c8 100644 --- a/src/app/crash/session.h +++ b/src/app/crash/session.h @@ -17,6 +17,8 @@ #include namespace app { + class Document; + namespace crash { // A class to record/restore session information. @@ -31,6 +33,8 @@ namespace crash { void create(base::pid pid); void removeFromDisk(); + void saveDocumentChanges(app::Document* doc); + private: void loadPid(); std::string pidFilename() const;