update
This commit is contained in:
@@ -0,0 +1,139 @@
|
||||
#include "persistence/AsyncProjectWriter.h"
|
||||
|
||||
#include "persistence/AnimationBundleStore.h"
|
||||
#include "persistence/EntityBundleStore.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDir>
|
||||
#include <QEventLoop>
|
||||
#include <QTimer>
|
||||
#include <QtConcurrent/QtConcurrent>
|
||||
|
||||
namespace core::persistence {
|
||||
|
||||
AsyncProjectWriter::AsyncProjectWriter(QObject* parent)
|
||||
: QObject(parent) {
|
||||
m_debounce = new QTimer(this);
|
||||
m_debounce->setSingleShot(true);
|
||||
m_debounce->setInterval(80);
|
||||
connect(m_debounce, &QTimer::timeout, this, [this]() { scheduleDrain(); });
|
||||
}
|
||||
|
||||
void AsyncProjectWriter::setProjectDir(const QString& projectDirAbs) {
|
||||
m_projectDir = projectDirAbs;
|
||||
}
|
||||
|
||||
void AsyncProjectWriter::requestWriteEntity(const Project::Entity& entity) {
|
||||
if (m_projectDir.isEmpty() || entity.id.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
{
|
||||
std::lock_guard lock(m_mutex);
|
||||
m_pendingEntities.insert(entity.id, entity);
|
||||
}
|
||||
if (m_debounce) {
|
||||
m_debounce->start();
|
||||
}
|
||||
}
|
||||
|
||||
void AsyncProjectWriter::requestWriteAnimation(const Project::Animation& animation) {
|
||||
if (m_projectDir.isEmpty() || animation.id.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
{
|
||||
std::lock_guard lock(m_mutex);
|
||||
m_pendingAnimations.insert(animation.id, animation);
|
||||
}
|
||||
if (m_debounce) {
|
||||
m_debounce->start();
|
||||
}
|
||||
}
|
||||
|
||||
void AsyncProjectWriter::scheduleDrain() {
|
||||
const QString dir = m_projectDir;
|
||||
if (dir.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QHash<QString, Project::Entity> ents;
|
||||
QHash<QString, Project::Animation> anims;
|
||||
{
|
||||
std::lock_guard lock(m_mutex);
|
||||
ents = std::move(m_pendingEntities);
|
||||
anims = std::move(m_pendingAnimations);
|
||||
m_pendingEntities.clear();
|
||||
m_pendingAnimations.clear();
|
||||
}
|
||||
if (ents.isEmpty() && anims.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_outstandingDrains.fetch_add(1, std::memory_order_acq_rel);
|
||||
|
||||
(void)QtConcurrent::run([this, dir, ents = std::move(ents), anims = std::move(anims)]() mutable {
|
||||
bool anyFail = false;
|
||||
for (auto it = ents.begin(); it != ents.end(); ++it) {
|
||||
const Project::Entity& e = it.value();
|
||||
if (!EntityBundleStore::save(dir, e)) {
|
||||
anyFail = true;
|
||||
QMetaObject::invokeMethod(this, [this, id = e.id]() { emit entityWriteFailed(id); },
|
||||
Qt::QueuedConnection);
|
||||
}
|
||||
}
|
||||
for (auto it = anims.begin(); it != anims.end(); ++it) {
|
||||
const Project::Animation& a = it.value();
|
||||
if (!AnimationBundleStore::save(dir, a)) {
|
||||
anyFail = true;
|
||||
QMetaObject::invokeMethod(this, [this, id = a.id]() { emit animationWriteFailed(id); },
|
||||
Qt::QueuedConnection);
|
||||
}
|
||||
}
|
||||
Q_UNUSED(anyFail);
|
||||
m_outstandingDrains.fetch_sub(1, std::memory_order_acq_rel);
|
||||
});
|
||||
|
||||
// drain 期间若又有新请求,再开一轮
|
||||
{
|
||||
std::lock_guard lock(m_mutex);
|
||||
if (!m_pendingEntities.isEmpty() || !m_pendingAnimations.isEmpty()) {
|
||||
if (m_debounce) {
|
||||
m_debounce->start();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AsyncProjectWriter::flushBlocking(int timeoutMs) {
|
||||
if (m_projectDir.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
if (m_debounce) {
|
||||
m_debounce->stop();
|
||||
}
|
||||
|
||||
QMetaObject::invokeMethod(
|
||||
this,
|
||||
[this]() {
|
||||
scheduleDrain();
|
||||
},
|
||||
Qt::QueuedConnection);
|
||||
|
||||
QTimer killer;
|
||||
killer.setSingleShot(true);
|
||||
killer.start(std::max(0, timeoutMs));
|
||||
while (killer.isActive()) {
|
||||
bool idle = false;
|
||||
{
|
||||
std::lock_guard lock(m_mutex);
|
||||
idle =
|
||||
m_pendingEntities.isEmpty() && m_pendingAnimations.isEmpty() &&
|
||||
m_outstandingDrains.load(std::memory_order_acquire) == 0;
|
||||
}
|
||||
if (idle) {
|
||||
break;
|
||||
}
|
||||
QCoreApplication::processEvents(QEventLoop::AllEvents, 20);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace core::persistence
|
||||
Reference in New Issue
Block a user