mirror of https://github.com/aseprite/aseprite.git
				
				
				
			Save document backups in background session each 5 minutes
This commit is contained in:
		
							parent
							
								
									0a4e710ad7
								
							
						
					
					
						commit
						0bde95650d
					
				| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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<void>(&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<app::Document*>(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
 | 
			
		||||
| 
						 | 
				
			
			@ -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 <vector>
 | 
			
		||||
 | 
			
		||||
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<doc::Document*> m_documents;
 | 
			
		||||
    bool m_done;
 | 
			
		||||
    base::thread m_thread;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
} // namespace crash
 | 
			
		||||
} // namespace app
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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 <vector>
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -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<SessionPtr> 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);
 | 
			
		||||
  };
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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<std::string>(doc->id()) + ".ase");
 | 
			
		||||
  TRACE("DataRecovery: Saving document '%s'...\n", fn.c_str());
 | 
			
		||||
 | 
			
		||||
  FileOp* fop = fop_to_save_document(&ctx,
 | 
			
		||||
    static_cast<app::Document*>(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)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -17,6 +17,8 @@
 | 
			
		|||
#include <string>
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue