aseprite/src/app/closed_docs.cpp

173 lines
4.4 KiB
C++

// Aseprite
// Copyright (C) 2019-2020 Igara Studio S.A.
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "app/closed_docs.h"
#include "app/doc.h"
#include "app/pref/preferences.h"
#include <algorithm>
#include <limits>
#define CLOSEDOC_TRACE(...) // TRACEARGS
namespace app {
ClosedDocs::ClosedDocs(const Preferences& pref)
: m_done(false)
{
if (pref.general.dataRecovery())
m_dataRecoveryPeriodMSecs = int(1000.0*60.0*pref.general.dataRecoveryPeriod());
else
m_dataRecoveryPeriodMSecs = 0;
if (pref.general.keepClosedSpriteOnMemory())
m_keepClosedDocAliveForMSecs = int(1000.0*60.0*pref.general.keepClosedSpriteOnMemoryFor());
else
m_keepClosedDocAliveForMSecs = 0;
CLOSEDOC_TRACE("CLOSEDOC: Init",
"dataRecoveryPeriod", m_dataRecoveryPeriodMSecs,
"keepClosedDocs", m_keepClosedDocAliveForMSecs);
}
ClosedDocs::~ClosedDocs()
{
CLOSEDOC_TRACE("CLOSEDOC: Exit");
if (m_thread.joinable()) {
CLOSEDOC_TRACE("CLOSEDOC: Join thread");
m_done = true;
m_cv.notify_one();
m_thread.join();
CLOSEDOC_TRACE("CLOSEDOC: Join done");
}
ASSERT(m_docs.empty());
}
bool ClosedDocs::hasClosedDocs()
{
bool result;
{
std::unique_lock<std::mutex> lock(m_mutex);
result = !m_docs.empty();
}
CLOSEDOC_TRACE("CLOSEDOC: Has closed docs?",
(result ? "true": "false"));
return result;
}
void ClosedDocs::addClosedDoc(Doc* doc)
{
CLOSEDOC_TRACE("CLOSEDOC: Add closed doc", doc);
ASSERT(doc != nullptr);
ASSERT(doc->context() == nullptr);
ClosedDoc closedDoc = { doc, base::current_tick() };
std::unique_lock<std::mutex> lock(m_mutex);
m_docs.insert(m_docs.begin(), std::move(closedDoc));
if (!m_thread.joinable())
m_thread = std::thread([this]{ backgroundThread(); });
else
m_cv.notify_one();
}
Doc* ClosedDocs::reopenLastClosedDoc()
{
Doc* doc = nullptr;
{
std::unique_lock<std::mutex> lock(m_mutex);
if (!m_docs.empty()) {
doc = m_docs.front().doc;
m_docs.erase(m_docs.begin());
}
CLOSEDOC_TRACE(" -> ", doc);
}
CLOSEDOC_TRACE("CLOSEDOC: Reopen last closed doc", doc);
return doc;
}
std::vector<Doc*> ClosedDocs::getAndRemoveAllClosedDocs()
{
std::vector<Doc*> docs;
{
std::unique_lock<std::mutex> lock(m_mutex);
CLOSEDOC_TRACE("CLOSEDOC: Get and remove all closed", m_docs.size(), "docs");
for (const ClosedDoc& closedDoc : m_docs)
docs.push_back(closedDoc.doc);
m_docs.clear();
m_done = true;
m_cv.notify_one();
}
return docs;
}
void ClosedDocs::backgroundThread()
{
CLOSEDOC_TRACE("CLOSEDOC: [BG] Background thread start");
std::unique_lock<std::mutex> lock(m_mutex);
while (!m_done) {
base::tick_t now = base::current_tick();
base::tick_t waitForMSecs = std::numeric_limits<base::tick_t>::max();
for (auto it=m_docs.begin(); it != m_docs.end(); ) {
const ClosedDoc& closedDoc = *it;
auto doc = closedDoc.doc;
base::tick_t diff = now - closedDoc.timestamp;
if (diff >= m_keepClosedDocAliveForMSecs) {
if (// If we backup process is disabled
m_dataRecoveryPeriodMSecs == 0 ||
// Or this document doesn't need a backup (e.g. an unmodified document)
!doc->needsBackup() ||
// Or the document already has the backup done
doc->isFullyBackedUp()) {
// Finally delete the document (this is the place where we
// delete all documents created/loaded by the user)
CLOSEDOC_TRACE("CLOSEDOC: [BG] Delete doc", doc);
delete doc;
it = m_docs.erase(it);
}
else {
waitForMSecs = std::min(waitForMSecs, m_dataRecoveryPeriodMSecs);
++it;
}
}
else {
waitForMSecs = std::min(waitForMSecs, m_keepClosedDocAliveForMSecs-diff);
++it;
}
}
if (waitForMSecs < std::numeric_limits<base::tick_t>::max()) {
CLOSEDOC_TRACE("CLOSEDOC: [BG] Wait for", waitForMSecs, "milliseconds");
ASSERT(!m_docs.empty());
m_cv.wait_for(lock, std::chrono::milliseconds(waitForMSecs));
}
else {
CLOSEDOC_TRACE("CLOSEDOC: [BG] Wait for condition variable");
ASSERT(m_docs.empty());
m_cv.wait(lock);
}
}
CLOSEDOC_TRACE("CLOSEDOC: [BG] Background thread end");
}
} // namespace app