Push all QSaveFile-s to background thread.
This commit is contained in:
parent
3cad89f299
commit
197b3c1cb5
|
@ -157,6 +157,7 @@ void Step::finish(const MTPUser &user, QImage &&photo) {
|
||||||
_account->logOut();
|
_account->logOut();
|
||||||
crl::on_main(raw, [=] {
|
crl::on_main(raw, [=] {
|
||||||
Core::App().domain().activate(raw);
|
Core::App().domain().activate(raw);
|
||||||
|
Local::sync();
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -203,6 +204,7 @@ void Step::createSession(
|
||||||
if (session.supportMode()) {
|
if (session.supportMode()) {
|
||||||
PrepareSupportMode(&session);
|
PrepareSupportMode(&session);
|
||||||
}
|
}
|
||||||
|
Local::sync();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Step::paintEvent(QPaintEvent *e) {
|
void Step::paintEvent(QPaintEvent *e) {
|
||||||
|
|
|
@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "base/platform/base_platform_file_utilities.h"
|
#include "base/platform/base_platform_file_utilities.h"
|
||||||
#include "base/openssl_help.h"
|
#include "base/openssl_help.h"
|
||||||
|
|
||||||
|
#include <crl/crl_object_on_thread.h>
|
||||||
#include <QtCore/QtEndian>
|
#include <QtCore/QtEndian>
|
||||||
#include <QtCore/QSaveFile>
|
#include <QtCore/QSaveFile>
|
||||||
|
|
||||||
|
@ -23,6 +24,217 @@ constexpr auto TdfMagicLen = int(sizeof(TdfMagic));
|
||||||
|
|
||||||
constexpr auto kStrongIterationsCount = 100'000;
|
constexpr auto kStrongIterationsCount = 100'000;
|
||||||
|
|
||||||
|
struct WriteEntry {
|
||||||
|
QString basePath;
|
||||||
|
QString base;
|
||||||
|
QByteArray data;
|
||||||
|
QByteArray md5;
|
||||||
|
};
|
||||||
|
|
||||||
|
class WriteManager final {
|
||||||
|
public:
|
||||||
|
explicit WriteManager(crl::weak_on_thread<WriteManager> weak);
|
||||||
|
|
||||||
|
void write(WriteEntry &&entry);
|
||||||
|
void writeSync(WriteEntry &&entry);
|
||||||
|
void writeSyncAll();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void scheduleWrite();
|
||||||
|
void writeScheduled();
|
||||||
|
bool writeOneScheduledNow();
|
||||||
|
void writeNow(WriteEntry &&entry);
|
||||||
|
|
||||||
|
template <typename File>
|
||||||
|
[[nodiscard]] bool open(File &file, const WriteEntry &entry, char postfix);
|
||||||
|
|
||||||
|
[[nodiscard]] QString path(const WriteEntry &entry, char postfix) const;
|
||||||
|
[[nodiscard]] bool writeHeader(
|
||||||
|
const QString &basePath,
|
||||||
|
QFileDevice &file);
|
||||||
|
|
||||||
|
crl::weak_on_thread<WriteManager> _weak;
|
||||||
|
std::deque<WriteEntry> _scheduled;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class AsyncWriteManager final {
|
||||||
|
public:
|
||||||
|
void write(WriteEntry &&entry);
|
||||||
|
void writeSync(WriteEntry &&entry);
|
||||||
|
void sync();
|
||||||
|
void stop();
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::optional<crl::object_on_thread<WriteManager>> _manager;
|
||||||
|
bool _finished = false;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
WriteManager::WriteManager(crl::weak_on_thread<WriteManager> weak)
|
||||||
|
: _weak(std::move(weak)) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteManager::write(WriteEntry &&entry) {
|
||||||
|
const auto i = ranges::find(_scheduled, entry.base, &WriteEntry::base);
|
||||||
|
if (i == end(_scheduled)) {
|
||||||
|
_scheduled.push_back(std::move(entry));
|
||||||
|
} else {
|
||||||
|
*i = std::move(entry);
|
||||||
|
}
|
||||||
|
scheduleWrite();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteManager::writeSync(WriteEntry &&entry) {
|
||||||
|
const auto i = ranges::find(_scheduled, entry.base, &WriteEntry::base);
|
||||||
|
if (i != end(_scheduled)) {
|
||||||
|
_scheduled.erase(i);
|
||||||
|
}
|
||||||
|
writeNow(std::move(entry));
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteManager::writeNow(WriteEntry &&entry) {
|
||||||
|
const auto path = [&](char postfix) {
|
||||||
|
return this->path(entry, postfix);
|
||||||
|
};
|
||||||
|
const auto open = [&](auto &file, char postfix) {
|
||||||
|
return this->open(file, entry, postfix);
|
||||||
|
};
|
||||||
|
const auto write = [&](auto &file) {
|
||||||
|
file.write(entry.data);
|
||||||
|
file.write(entry.md5);
|
||||||
|
};
|
||||||
|
const auto safe = path('s');
|
||||||
|
const auto simple = path('0');
|
||||||
|
const auto backup = path('1');
|
||||||
|
QSaveFile save;
|
||||||
|
if (open(save, 's')) {
|
||||||
|
write(save);
|
||||||
|
if (save.commit()) {
|
||||||
|
QFile::remove(simple);
|
||||||
|
QFile::remove(backup);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
LOG(("Storage Error: Could not commit '%1'.").arg(safe));
|
||||||
|
}
|
||||||
|
QFile plain;
|
||||||
|
if (open(plain, '0')) {
|
||||||
|
write(plain);
|
||||||
|
base::Platform::FlushFileData(plain);
|
||||||
|
plain.close();
|
||||||
|
|
||||||
|
QFile::remove(backup);
|
||||||
|
if (base::Platform::RenameWithOverwrite(simple, safe)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
QFile::remove(safe);
|
||||||
|
LOG(("Storage Error: Could not rename '%1' to '%2', removing.").arg(
|
||||||
|
simple,
|
||||||
|
safe));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteManager::writeSyncAll() {
|
||||||
|
while (writeOneScheduledNow()) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WriteManager::writeOneScheduledNow() {
|
||||||
|
if (_scheduled.empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto entry = std::move(_scheduled.front());
|
||||||
|
_scheduled.pop_front();
|
||||||
|
|
||||||
|
writeNow(std::move(entry));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WriteManager::writeHeader(const QString &basePath, QFileDevice &file) {
|
||||||
|
if (!file.open(QIODevice::WriteOnly)) {
|
||||||
|
const auto dir = QDir(basePath);
|
||||||
|
if (dir.exists()) {
|
||||||
|
return false;
|
||||||
|
} else if (!QDir().mkpath(dir.absolutePath())) {
|
||||||
|
return false;
|
||||||
|
} else if (!file.open(QIODevice::WriteOnly)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file.write(TdfMagic, TdfMagicLen);
|
||||||
|
const auto version = qint32(AppVersion);
|
||||||
|
file.write((const char*)&version, sizeof(version));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString WriteManager::path(const WriteEntry &entry, char postfix) const {
|
||||||
|
return entry.base + postfix;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename File>
|
||||||
|
bool WriteManager::open(File &file, const WriteEntry &entry, char postfix) {
|
||||||
|
const auto name = path(entry, postfix);
|
||||||
|
file.setFileName(name);
|
||||||
|
if (!writeHeader(entry.basePath, file)) {
|
||||||
|
LOG(("Storage Error: Could not open '%1' for writing.").arg(name));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteManager::scheduleWrite() {
|
||||||
|
_weak.with([](WriteManager &that) {
|
||||||
|
that.writeScheduled();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteManager::writeScheduled() {
|
||||||
|
if (writeOneScheduledNow() && !_scheduled.empty()) {
|
||||||
|
scheduleWrite();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AsyncWriteManager::write(WriteEntry &&entry) {
|
||||||
|
Expects(!_finished);
|
||||||
|
|
||||||
|
if (!_manager) {
|
||||||
|
_manager.emplace();
|
||||||
|
}
|
||||||
|
_manager->with([entry = std::move(entry)](WriteManager &manager) mutable {
|
||||||
|
manager.write(std::move(entry));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void AsyncWriteManager::writeSync(WriteEntry &&entry) {
|
||||||
|
Expects(!_finished);
|
||||||
|
|
||||||
|
if (!_manager) {
|
||||||
|
_manager.emplace();
|
||||||
|
}
|
||||||
|
_manager->with_sync([&](WriteManager &manager) {
|
||||||
|
manager.writeSync(std::move(entry));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void AsyncWriteManager::sync() {
|
||||||
|
if (_manager) {
|
||||||
|
_manager->with_sync([](WriteManager &manager) {
|
||||||
|
manager.writeSyncAll();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AsyncWriteManager::stop() {
|
||||||
|
if (_manager) {
|
||||||
|
sync();
|
||||||
|
_manager.reset();
|
||||||
|
}
|
||||||
|
_finished = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
AsyncWriteManager Manager;
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
QString ToFilePart(FileKey val) {
|
QString ToFilePart(FileKey val) {
|
||||||
|
@ -165,14 +377,17 @@ void EncryptedDescriptor::finish() {
|
||||||
|
|
||||||
FileWriteDescriptor::FileWriteDescriptor(
|
FileWriteDescriptor::FileWriteDescriptor(
|
||||||
const FileKey &key,
|
const FileKey &key,
|
||||||
const QString &basePath)
|
const QString &basePath,
|
||||||
: FileWriteDescriptor(ToFilePart(key), basePath) {
|
bool sync)
|
||||||
|
: FileWriteDescriptor(ToFilePart(key), basePath, sync) {
|
||||||
}
|
}
|
||||||
|
|
||||||
FileWriteDescriptor::FileWriteDescriptor(
|
FileWriteDescriptor::FileWriteDescriptor(
|
||||||
const QString &name,
|
const QString &name,
|
||||||
const QString &basePath)
|
const QString &basePath,
|
||||||
: _basePath(basePath) {
|
bool sync)
|
||||||
|
: _basePath(basePath)
|
||||||
|
, _sync(sync) {
|
||||||
init(name);
|
init(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -180,42 +395,6 @@ FileWriteDescriptor::~FileWriteDescriptor() {
|
||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString FileWriteDescriptor::path(char postfix) const {
|
|
||||||
return _base + postfix;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename File>
|
|
||||||
bool FileWriteDescriptor::open(File &file, char postfix) {
|
|
||||||
const auto name = path(postfix);
|
|
||||||
file.setFileName(name);
|
|
||||||
if (!writeHeader(file)) {
|
|
||||||
LOG(("Storage Error: Could not open '%1' for writing.").arg(name));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FileWriteDescriptor::writeHeader(QFileDevice &file) {
|
|
||||||
if (!file.open(QIODevice::WriteOnly)) {
|
|
||||||
const auto dir = QDir(_basePath);
|
|
||||||
if (dir.exists()) {
|
|
||||||
return false;
|
|
||||||
} else if (!QDir().mkpath(dir.absolutePath())) {
|
|
||||||
return false;
|
|
||||||
} else if (!file.open(QIODevice::WriteOnly)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
file.write(TdfMagic, TdfMagicLen);
|
|
||||||
const auto version = qint32(AppVersion);
|
|
||||||
file.write((const char*)&version, sizeof(version));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FileWriteDescriptor::writeFooter(QFileDevice &file) {
|
|
||||||
file.write((const char*)_md5.result(), 0x10);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FileWriteDescriptor::init(const QString &name) {
|
void FileWriteDescriptor::init(const QString &name) {
|
||||||
_base = _basePath + name;
|
_base = _basePath + name;
|
||||||
_buffer.setBuffer(&_safeData);
|
_buffer.setBuffer(&_safeData);
|
||||||
|
@ -257,35 +436,16 @@ void FileWriteDescriptor::finish() {
|
||||||
|
|
||||||
_buffer.close();
|
_buffer.close();
|
||||||
|
|
||||||
const auto safe = path('s');
|
auto entry = WriteEntry{
|
||||||
const auto simple = path('0');
|
.basePath = _basePath,
|
||||||
const auto backup = path('1');
|
.base = _base,
|
||||||
QSaveFile save;
|
.data = _safeData,
|
||||||
if (open(save, 's')) {
|
.md5 = QByteArray((const char*)_md5.result(), 0x10)
|
||||||
save.write(_safeData);
|
};
|
||||||
writeFooter(save);
|
if (_sync) {
|
||||||
if (save.commit()) {
|
Manager.writeSync(std::move(entry));
|
||||||
QFile::remove(simple);
|
} else {
|
||||||
QFile::remove(backup);
|
Manager.write(std::move(entry));
|
||||||
return;
|
|
||||||
}
|
|
||||||
LOG(("Storage Error: Could not commit '%1'.").arg(safe));
|
|
||||||
}
|
|
||||||
QFile plain;
|
|
||||||
if (open(plain, '0')) {
|
|
||||||
plain.write(_safeData);
|
|
||||||
writeFooter(plain);
|
|
||||||
base::Platform::FlushFileData(plain);
|
|
||||||
plain.close();
|
|
||||||
|
|
||||||
QFile::remove(backup);
|
|
||||||
if (base::Platform::RenameWithOverwrite(simple, safe)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
QFile::remove(safe);
|
|
||||||
LOG(("Storage Error: Could not rename '%1' to '%2', removing.").arg(
|
|
||||||
simple,
|
|
||||||
safe));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -504,5 +664,13 @@ bool ReadEncryptedFile(
|
||||||
return ReadEncryptedFile(result, ToFilePart(fkey), basePath, key);
|
return ReadEncryptedFile(result, ToFilePart(fkey), basePath, key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Sync() {
|
||||||
|
Manager.sync();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Finish() {
|
||||||
|
Manager.stop();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace details
|
} // namespace details
|
||||||
} // namespace Storage
|
} // namespace Storage
|
||||||
|
|
|
@ -56,10 +56,12 @@ class FileWriteDescriptor final {
|
||||||
public:
|
public:
|
||||||
FileWriteDescriptor(
|
FileWriteDescriptor(
|
||||||
const FileKey &key,
|
const FileKey &key,
|
||||||
const QString &basePath);
|
const QString &basePath,
|
||||||
|
bool sync = false);
|
||||||
FileWriteDescriptor(
|
FileWriteDescriptor(
|
||||||
const QString &name,
|
const QString &name,
|
||||||
const QString &basePath);
|
const QString &basePath,
|
||||||
|
bool sync = false);
|
||||||
~FileWriteDescriptor();
|
~FileWriteDescriptor();
|
||||||
|
|
||||||
void writeData(const QByteArray &data);
|
void writeData(const QByteArray &data);
|
||||||
|
@ -69,11 +71,6 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void init(const QString &name);
|
void init(const QString &name);
|
||||||
[[nodiscard]] QString path(char postfix) const;
|
|
||||||
template <typename File>
|
|
||||||
[[nodiscard]] bool open(File &file, char postfix);
|
|
||||||
[[nodiscard]] bool writeHeader(QFileDevice &file);
|
|
||||||
void writeFooter(QFileDevice &file);
|
|
||||||
void finish();
|
void finish();
|
||||||
|
|
||||||
const QString _basePath;
|
const QString _basePath;
|
||||||
|
@ -83,6 +80,7 @@ private:
|
||||||
QString _base;
|
QString _base;
|
||||||
HashMd5 _md5;
|
HashMd5 _md5;
|
||||||
int _fullSize = 0;
|
int _fullSize = 0;
|
||||||
|
bool _sync = false;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -108,5 +106,8 @@ bool ReadEncryptedFile(
|
||||||
const QString &basePath,
|
const QString &basePath,
|
||||||
const MTP::AuthKeyPtr &key);
|
const MTP::AuthKeyPtr &key);
|
||||||
|
|
||||||
|
void Sync();
|
||||||
|
void Finish();
|
||||||
|
|
||||||
} // namespace details
|
} // namespace details
|
||||||
} // namespace Storage
|
} // namespace Storage
|
||||||
|
|
|
@ -338,8 +338,13 @@ bool _readOldMtpData(bool remove, ReadSettingsContext &context) {
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
void sync() {
|
||||||
|
Storage::details::Sync();
|
||||||
|
}
|
||||||
|
|
||||||
void finish() {
|
void finish() {
|
||||||
delete base::take(_localLoader);
|
delete base::take(_localLoader);
|
||||||
|
Storage::details::Finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
void InitialLoadTheme();
|
void InitialLoadTheme();
|
||||||
|
|
|
@ -49,6 +49,7 @@ using AuthKeyPtr = std::shared_ptr<AuthKey>;
|
||||||
namespace Local {
|
namespace Local {
|
||||||
|
|
||||||
void start();
|
void start();
|
||||||
|
void sync();
|
||||||
void finish();
|
void finish();
|
||||||
|
|
||||||
void writeSettings();
|
void writeSettings();
|
||||||
|
|
|
@ -577,6 +577,8 @@ void Account::reset() {
|
||||||
QDir(LegacyTempDirectory()).removeRecursively();
|
QDir(LegacyTempDirectory()).removeRecursively();
|
||||||
QDir(temp).removeRecursively();
|
QDir(temp).removeRecursively();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Local::sync();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Account::writeLocations() {
|
void Account::writeLocations() {
|
||||||
|
|
Loading…
Reference in New Issue