Push all QSaveFile-s to background thread.

This commit is contained in:
John Preston 2020-10-07 12:00:31 +03:00
parent 3cad89f299
commit 197b3c1cb5
6 changed files with 255 additions and 76 deletions

View File

@ -157,6 +157,7 @@ void Step::finish(const MTPUser &user, QImage &&photo) {
_account->logOut();
crl::on_main(raw, [=] {
Core::App().domain().activate(raw);
Local::sync();
});
return;
}
@ -203,6 +204,7 @@ void Step::createSession(
if (session.supportMode()) {
PrepareSupportMode(&session);
}
Local::sync();
}
void Step::paintEvent(QPaintEvent *e) {

View File

@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/platform/base_platform_file_utilities.h"
#include "base/openssl_help.h"
#include <crl/crl_object_on_thread.h>
#include <QtCore/QtEndian>
#include <QtCore/QSaveFile>
@ -23,6 +24,217 @@ constexpr auto TdfMagicLen = int(sizeof(TdfMagic));
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
QString ToFilePart(FileKey val) {
@ -165,14 +377,17 @@ void EncryptedDescriptor::finish() {
FileWriteDescriptor::FileWriteDescriptor(
const FileKey &key,
const QString &basePath)
: FileWriteDescriptor(ToFilePart(key), basePath) {
const QString &basePath,
bool sync)
: FileWriteDescriptor(ToFilePart(key), basePath, sync) {
}
FileWriteDescriptor::FileWriteDescriptor(
const QString &name,
const QString &basePath)
: _basePath(basePath) {
const QString &basePath,
bool sync)
: _basePath(basePath)
, _sync(sync) {
init(name);
}
@ -180,42 +395,6 @@ FileWriteDescriptor::~FileWriteDescriptor() {
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) {
_base = _basePath + name;
_buffer.setBuffer(&_safeData);
@ -257,35 +436,16 @@ void FileWriteDescriptor::finish() {
_buffer.close();
const auto safe = path('s');
const auto simple = path('0');
const auto backup = path('1');
QSaveFile save;
if (open(save, 's')) {
save.write(_safeData);
writeFooter(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')) {
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));
auto entry = WriteEntry{
.basePath = _basePath,
.base = _base,
.data = _safeData,
.md5 = QByteArray((const char*)_md5.result(), 0x10)
};
if (_sync) {
Manager.writeSync(std::move(entry));
} else {
Manager.write(std::move(entry));
}
}
@ -504,5 +664,13 @@ bool ReadEncryptedFile(
return ReadEncryptedFile(result, ToFilePart(fkey), basePath, key);
}
void Sync() {
Manager.sync();
}
void Finish() {
Manager.stop();
}
} // namespace details
} // namespace Storage

View File

@ -56,10 +56,12 @@ class FileWriteDescriptor final {
public:
FileWriteDescriptor(
const FileKey &key,
const QString &basePath);
const QString &basePath,
bool sync = false);
FileWriteDescriptor(
const QString &name,
const QString &basePath);
const QString &basePath,
bool sync = false);
~FileWriteDescriptor();
void writeData(const QByteArray &data);
@ -69,11 +71,6 @@ public:
private:
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();
const QString _basePath;
@ -83,6 +80,7 @@ private:
QString _base;
HashMd5 _md5;
int _fullSize = 0;
bool _sync = false;
};
@ -108,5 +106,8 @@ bool ReadEncryptedFile(
const QString &basePath,
const MTP::AuthKeyPtr &key);
void Sync();
void Finish();
} // namespace details
} // namespace Storage

View File

@ -338,8 +338,13 @@ bool _readOldMtpData(bool remove, ReadSettingsContext &context) {
} // namespace
void sync() {
Storage::details::Sync();
}
void finish() {
delete base::take(_localLoader);
Storage::details::Finish();
}
void InitialLoadTheme();

View File

@ -49,6 +49,7 @@ using AuthKeyPtr = std::shared_ptr<AuthKey>;
namespace Local {
void start();
void sync();
void finish();
void writeSettings();

View File

@ -577,6 +577,8 @@ void Account::reset() {
QDir(LegacyTempDirectory()).removeRecursively();
QDir(temp).removeRecursively();
});
Local::sync();
}
void Account::writeLocations() {