mirror of
https://github.com/aseprite/aseprite.git
synced 2025-02-23 00:40:04 +00:00
Save closed docs backup data when we close the application correctly
Also some extra improvements like: * Wake up every X seconds/minutes to check if we have to save some backup data (instead of each one second) * Use a condition variable to wakeup when we quit the application
This commit is contained in:
parent
f3731c9c28
commit
fbbb3238a6
@ -29,7 +29,6 @@
|
|||||||
#include "base/bind.h"
|
#include "base/bind.h"
|
||||||
#include "base/chrono.h"
|
#include "base/chrono.h"
|
||||||
#include "base/remove_from_container.h"
|
#include "base/remove_from_container.h"
|
||||||
#include "base/scoped_lock.h"
|
|
||||||
#include "ui/system.h"
|
#include "ui/system.h"
|
||||||
|
|
||||||
namespace app {
|
namespace app {
|
||||||
@ -80,12 +79,14 @@ BackupObserver::~BackupObserver()
|
|||||||
void BackupObserver::stop()
|
void BackupObserver::stop()
|
||||||
{
|
{
|
||||||
m_done = true;
|
m_done = true;
|
||||||
|
m_wakeup.notify_one();
|
||||||
}
|
}
|
||||||
|
|
||||||
void BackupObserver::onAddDocument(Doc* document)
|
void BackupObserver::onAddDocument(Doc* document)
|
||||||
{
|
{
|
||||||
TRACE("RECO: Observe document %p\n", document);
|
TRACE("RECO: Observe document %p\n", document);
|
||||||
base::scoped_lock hold(m_mutex);
|
|
||||||
|
std::unique_lock<std::mutex> lock(m_mutex);
|
||||||
m_documents.push_back(document);
|
m_documents.push_back(document);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,15 +94,19 @@ void BackupObserver::onRemoveDocument(Doc* doc)
|
|||||||
{
|
{
|
||||||
TRACE("RECO: Remove document %p\n", doc);
|
TRACE("RECO: Remove document %p\n", doc);
|
||||||
{
|
{
|
||||||
base::scoped_lock hold(m_mutex);
|
std::unique_lock<std::mutex> lock(m_mutex);
|
||||||
base::remove_from_container(m_documents, doc);
|
base::remove_from_container(m_documents, doc);
|
||||||
}
|
}
|
||||||
// TODO save backup data of the closed document in a background thread
|
if (m_config->keepEditedSpriteData)
|
||||||
|
m_closedDocs.push_back(doc);
|
||||||
|
else
|
||||||
m_session->removeDocument(doc);
|
m_session->removeDocument(doc);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BackupObserver::backgroundThread()
|
void BackupObserver::backgroundThread()
|
||||||
{
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(m_mutex);
|
||||||
|
|
||||||
int normalPeriod = int(60.0*m_config->dataRecoveryPeriod);
|
int normalPeriod = int(60.0*m_config->dataRecoveryPeriod);
|
||||||
int lockedPeriod = 5;
|
int lockedPeriod = 5;
|
||||||
#ifdef TEST_BACKUPS_WITH_A_SHORT_PERIOD
|
#ifdef TEST_BACKUPS_WITH_A_SHORT_PERIOD
|
||||||
@ -109,29 +114,52 @@ void BackupObserver::backgroundThread()
|
|||||||
lockedPeriod = 5;
|
lockedPeriod = 5;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int waitUntil = normalPeriod;
|
int waitFor = normalPeriod;
|
||||||
int seconds = 0;
|
|
||||||
|
|
||||||
while (!m_done) {
|
while (!m_done) {
|
||||||
seconds++;
|
m_wakeup.wait_for(lock, std::chrono::seconds(waitFor));
|
||||||
if (seconds >= waitUntil) {
|
|
||||||
TRACE("RECO: Start backup process for %d documents\n", m_documents.size());
|
TRACE("RECO: Start backup process for %d documents\n", m_documents.size());
|
||||||
|
|
||||||
SwitchBackupIcon icon;
|
SwitchBackupIcon icon;
|
||||||
base::scoped_lock hold(m_mutex);
|
|
||||||
base::Chrono chrono;
|
base::Chrono chrono;
|
||||||
bool somethingLocked = false;
|
bool somethingLocked = false;
|
||||||
|
|
||||||
for (Doc* doc : m_documents) {
|
for (Doc* doc : m_documents) {
|
||||||
|
if (!saveDocData(doc))
|
||||||
|
somethingLocked = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_closedDocs.empty()) {
|
||||||
|
for (auto it=m_closedDocs.begin(); it != m_closedDocs.end(); ) {
|
||||||
|
Doc* doc = *it;
|
||||||
|
if (saveDocData(doc))
|
||||||
|
it = m_closedDocs.erase(it);
|
||||||
|
else {
|
||||||
|
somethingLocked = true;
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
waitFor = (somethingLocked ? lockedPeriod: normalPeriod);
|
||||||
|
|
||||||
|
TRACE("RECO: Backup process done (%.16g)\n", chrono.elapsed());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Executed from the backgroundThread() (non-UI thread)
|
||||||
|
bool BackupObserver::saveDocData(Doc* doc)
|
||||||
|
{
|
||||||
try {
|
try {
|
||||||
if (doc->needsBackup()) {
|
if (!doc->needsBackup())
|
||||||
|
return true;
|
||||||
|
|
||||||
if (doc->inhibitBackup()) {
|
if (doc->inhibitBackup()) {
|
||||||
TRACE("RECO: Document '%d' backup is temporarily inhibited\n", doc->id());
|
TRACE("RECO: Document '%d' backup is temporarily inhibited\n", doc->id());
|
||||||
somethingLocked = true;
|
|
||||||
}
|
}
|
||||||
else if (!m_session->saveDocumentChanges(doc)) {
|
else if (!m_session->saveDocumentChanges(doc)) {
|
||||||
TRACE("RECO: Document '%d' backup was canceled by UI\n", doc->id());
|
TRACE("RECO: Document '%d' backup was canceled by UI\n", doc->id());
|
||||||
somethingLocked = true;
|
|
||||||
}
|
}
|
||||||
#ifdef TEST_BACKUP_INTEGRITY
|
#ifdef TEST_BACKUP_INTEGRITY
|
||||||
else {
|
else {
|
||||||
@ -160,23 +188,14 @@ void BackupObserver::backgroundThread()
|
|||||||
else {
|
else {
|
||||||
TRACE("RECO: No differences\n");
|
TRACE("RECO: No differences\n");
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
|
||||||
catch (const std::exception&) {
|
catch (const std::exception&) {
|
||||||
TRACE("RECO: Document '%d' is locked\n", doc->id());
|
TRACE("RECO: Document '%d' is locked\n", doc->id());
|
||||||
somethingLocked = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
seconds = 0;
|
|
||||||
waitUntil = (somethingLocked ? lockedPeriod: normalPeriod);
|
|
||||||
|
|
||||||
TRACE("RECO: Backup process done (%.16g)\n", chrono.elapsed());
|
|
||||||
}
|
|
||||||
base::this_thread::sleep_for(1.0);
|
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace crash
|
} // namespace crash
|
||||||
|
@ -12,9 +12,9 @@
|
|||||||
#include "app/context_observer.h"
|
#include "app/context_observer.h"
|
||||||
#include "app/doc_observer.h"
|
#include "app/doc_observer.h"
|
||||||
#include "app/docs_observer.h"
|
#include "app/docs_observer.h"
|
||||||
#include "base/mutex.h"
|
|
||||||
#include "base/thread.h"
|
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
#include <thread>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace app {
|
namespace app {
|
||||||
@ -40,14 +40,21 @@ namespace crash {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
void backgroundThread();
|
void backgroundThread();
|
||||||
|
bool saveDocData(Doc* doc);
|
||||||
|
|
||||||
RecoveryConfig* m_config;
|
RecoveryConfig* m_config;
|
||||||
Session* m_session;
|
Session* m_session;
|
||||||
base::mutex m_mutex;
|
|
||||||
Context* m_ctx;
|
Context* m_ctx;
|
||||||
std::vector<Doc*> m_documents;
|
std::vector<Doc*> m_documents;
|
||||||
|
std::vector<Doc*> m_closedDocs;
|
||||||
bool m_done;
|
bool m_done;
|
||||||
base::thread m_thread;
|
|
||||||
|
std::mutex m_mutex;
|
||||||
|
std::thread m_thread;
|
||||||
|
|
||||||
|
// Used to wakeup the backgroundThread() when we have to stop the
|
||||||
|
// thread that saves backups (i.e. when we are closing the application).
|
||||||
|
std::condition_variable m_wakeup;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace crash
|
} // namespace crash
|
||||||
|
@ -20,6 +20,8 @@
|
|||||||
#include "ui/system.h"
|
#include "ui/system.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <chrono>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
namespace app {
|
namespace app {
|
||||||
namespace crash {
|
namespace crash {
|
||||||
@ -59,7 +61,7 @@ DataRecovery::DataRecovery(Context* ctx)
|
|||||||
if (!base::is_directory(newSessionDir))
|
if (!base::is_directory(newSessionDir))
|
||||||
base::make_directory(newSessionDir);
|
base::make_directory(newSessionDir);
|
||||||
else {
|
else {
|
||||||
base::this_thread::sleep_for(1);
|
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||||
newSessionDir.clear();
|
newSessionDir.clear();
|
||||||
}
|
}
|
||||||
} while (newSessionDir.empty());
|
} while (newSessionDir.empty());
|
||||||
|
Loading…
x
Reference in New Issue
Block a user