Isolate avatar preparation generations and guard reentrant signals
This commit is contained in:
1 parent
d307492d22
commit
8c30c8e6ce
2 files changed
+23
-6
No files matched your search
@@ -7,6 +7,7 @@
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
#include <QMetaObject>
|
||||
#include <QPointer>
|
||||
#include <QRandomGenerator>
|
||||
#include <QRunnable>
|
||||
#include <QStandardPaths>
|
||||
@@ -270,12 +271,14 @@ void AvatarManager::saveDefault() {
|
||||
const QByteArray value = m_defaultServer ? (QString::number(m_defaultServer, 16) + '/' + m_defaultHash + ".json").toLatin1() : QByteArray("-");
|
||||
const char name[] = "avatar_default";
|
||||
memcpy(q->data, name, sizeof(name)); memcpy(q->data + sizeof(name), value.constData(), value.size());
|
||||
gui_bridge_post_uasync_fn(chat_core_save_ui_state_trampoline, q);
|
||||
if (gui_bridge_post_uasync_owned_fn(chat_core_save_ui_state_trampoline, q) < 0) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "avatar: default preference queue rejected"); u_free(q);
|
||||
}
|
||||
}
|
||||
|
||||
void AvatarManager::uploadDefault(const QString& source, quint64 server, const QRectF& crop) {
|
||||
if (!m_ready || m_preparing || !server || !gui_bridge_is_uasync_ready()) return;
|
||||
m_preparing = true; emit statusChanged(text("Preparing avatar…"));
|
||||
m_preparing = true;
|
||||
const QString directory = m_directory; const quint64 generation = ++m_generation; cancelUploads();
|
||||
QStringList pinned; if (!m_defaultHash.isEmpty()) pinned << m_defaultHash;
|
||||
for (const auto& job : m_jobs) if (job.upload) pinned << job.ref.hash;
|
||||
@@ -283,8 +286,11 @@ void AvatarManager::uploadDefault(const QString& source, quint64 server, const Q
|
||||
const auto result = AvatarCache::prepare(source, directory, crop);
|
||||
if (!result.image.isNull()) { auto keep = pinned; keep << result.hash; AvatarCache::prune(directory, keep); }
|
||||
QMetaObject::invokeMethod(this, [this, result, server, generation] {
|
||||
if (generation != m_generation) {
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_MEDIA, "avatar: discard stale preparation generation=%llu current=%llu",
|
||||
(unsigned long long)generation, (unsigned long long)m_generation); return;
|
||||
}
|
||||
m_preparing = false;
|
||||
if (generation != m_generation) return;
|
||||
if (result.image.isNull()) {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_MEDIA, "avatar: preparation failed reason=%s", qPrintable(result.error));
|
||||
emit statusChanged(text("Cannot prepare avatar: %1").arg(result.error)); return;
|
||||
@@ -292,11 +298,17 @@ void AvatarManager::uploadDefault(const QString& source, quint64 server, const Q
|
||||
m_defaultHash = result.hash; m_defaultServer = server; saveDefault();
|
||||
m_records[result.hash] = result.record;
|
||||
m_images.insert(result.hash, new QImage(result.image), qMax(1, int(result.image.sizeInBytes() / 1024)));
|
||||
m_missing.remove(result.hash); emit serversChanged(); emit statusChanged(text("Uploading avatar…"));
|
||||
m_missing.remove(result.hash);
|
||||
QPointer<AvatarManager> self(this);
|
||||
emit serversChanged();
|
||||
if (!self || generation != m_generation) return;
|
||||
emit statusChanged(text("Uploading avatar…"));
|
||||
if (!self || generation != m_generation) return;
|
||||
for (auto group : m_servers.keys()) { m_retry.remove(key(group, m_db->myNodeId())); scheduleOwn(group); }
|
||||
pump();
|
||||
}, Qt::QueuedConnection);
|
||||
}));
|
||||
emit statusChanged(text("Preparing avatar…"));
|
||||
}
|
||||
|
||||
void AvatarManager::cancelUploads() {
|
||||
@@ -309,7 +321,7 @@ void AvatarManager::cancelUploads() {
|
||||
|
||||
void AvatarManager::clearDefault() {
|
||||
if (!m_ready || !gui_bridge_is_uasync_ready()) return;
|
||||
++m_generation; cancelUploads(); m_defaultHash.clear(); m_defaultServer = 0; saveDefault();
|
||||
++m_generation; m_preparing = false; cancelUploads(); m_defaultHash.clear(); m_defaultServer = 0; saveDefault();
|
||||
for (const auto& ref : m_refs) if (ref.owner == m_db->myNodeId()) {
|
||||
Job job; job.ref = ref; job.ref.server = 0; job.ref.hash.clear(); job.upload = true; job.generation = m_generation;
|
||||
enqueue(job);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
/* Один менеджер на аккаунт: подписанные ссылки, общий кеш и ограниченная очередь передач. */
|
||||
/* Один менеджер на аккаунт: подписанные ссылки, общий кеш и ограниченная очередь передач.
|
||||
* Методы/сигналы — GUI-поток; DbManager заимствуется и должен переживать менеджер. */
|
||||
#pragma once
|
||||
#include "avatarcache.h"
|
||||
#include <QObject>
|
||||
@@ -26,6 +27,10 @@ public:
|
||||
void onPublished(const uint8_t* data, int length);
|
||||
QImage image(const QString& channel, quint64 owner);
|
||||
QMap<quint64, QString> servers() const;
|
||||
/* Начинает worker-подготовку локального source; требует ready, ненулевой server и готовое ядро.
|
||||
* crop — нормализованный после EXIF квадрат из selectAvatarCrop, пустой — центральный квадрат.
|
||||
* Пока текущая подготовка занята, повтор игнорируется; clearDefault отменяет поколение и позволяет новый upload.
|
||||
* Устаревший worker не меняет default/preparing. Из обработчиков сигналов подготовки можно удалить manager. */
|
||||
void uploadDefault(const QString& source, quint64 server, const QRectF& crop = {});
|
||||
void openGallery(const QString& channel, quint64 owner);
|
||||
void closeGallery();
|
||||
|
||||
Reference in new issue
Block a user