diff --git a/src/fileutils.cpp b/src/fileutils.cpp index b6834d0e4461d96e00375eda2fe7a20f8d462b8b..550b5587ed76ca823e597fef683a276a0953121a 100644 --- a/src/fileutils.cpp +++ b/src/fileutils.cpp @@ -217,6 +217,14 @@ expand_path(const std::string &path) #endif } +std::map<std::string, std::mutex> fileLocks {}; + +std::mutex& +getFileLock(const std::string& path) +{ + return fileLocks[path]; +} + bool isFile (const std::string& path) { struct stat s; return (stat (path.c_str(), &s) == 0) and not (s.st_mode & S_IFDIR); diff --git a/src/fileutils.h b/src/fileutils.h index 5b25b1c508da796977690a82294836f89b817777..7f143586262fa8eac2d6c1543db0fbf31c8fe318 100644 --- a/src/fileutils.h +++ b/src/fileutils.h @@ -24,6 +24,7 @@ #include <string> #include <vector> #include <chrono> +#include <mutex> #include <cstdio> #ifndef RING_UWP @@ -106,6 +107,8 @@ namespace ring { namespace fileutils { std::vector<uint8_t> readArchive(const std::string& path, const std::string& password = {}); void writeArchive(const std::string& data, const std::string& path, const std::string& password = {}); + std::mutex& getFileLock(const std::string& path); + struct FileHandle { int fd; const std::string name; diff --git a/src/im/message_engine.cpp b/src/im/message_engine.cpp index 2b632114722481efe9aeee51845b74e114698cad..bc45a33dece6c4081aff2f302bbc1d442f20f992 100644 --- a/src/im/message_engine.cpp +++ b/src/im/message_engine.cpp @@ -20,6 +20,8 @@ #include "message_engine.h" #include "sip/sipaccountbase.h" #include "manager.h" +#include "thread_pool.h" +#include "fileutils.h" #include "client/ring_signal.h" #include "dring/account_const.h" @@ -51,8 +53,8 @@ MessageEngine::sendMessage(const std::string& to, const std::map<std::string, st auto m = messages_.emplace(token, Message{}); m.first->second.to = to; m.first->second.payloads = payloads; + save_(); } - save(); runOnMainThread([this]() { retrySend(); }); @@ -146,6 +148,7 @@ MessageEngine::onMessageSent(MessageToken token, bool ok) token, f->second.to, static_cast<int>(DRing::Account::MessageStates::SENT)); + save_(); } else if (f->second.retried >= MAX_RETRIES) { f->second.status = MessageStatus::FAILURE; RING_WARN("[message %" PRIx64 "] status changed to FAILURE", token); @@ -153,6 +156,7 @@ MessageEngine::onMessageSent(MessageToken token, bool ok) token, f->second.to, static_cast<int>(DRing::Account::MessageStates::FAILURE)); + save_(); } else { f->second.status = MessageStatus::IDLE; RING_DBG("[message %" PRIx64 "] status changed to IDLE", token); @@ -171,15 +175,16 @@ MessageEngine::onMessageSent(MessageToken token, bool ok) void MessageEngine::load() { - std::lock_guard<std::mutex> lock(messagesMutex_); try { - std::ifstream file; - file.exceptions(std::ifstream::failbit | std::ifstream::badbit); - file.open(savePath_); - Json::Value root; - file >> root; - + { + std::lock_guard<std::mutex> lock(fileutils::getFileLock(savePath_)); + std::ifstream file; + file.exceptions(std::ifstream::failbit | std::ifstream::badbit); + file.open(savePath_); + file >> root; + } + std::lock_guard<std::mutex> lock(messagesMutex_); long unsigned loaded {0}; for (auto i = root.begin(); i != root.end(); ++i) { const auto& jmsg = *i; @@ -199,9 +204,6 @@ MessageEngine::load() loaded++; } RING_DBG("[Account %s] loaded %lu messages from %s", account_.getAccountID().c_str(), loaded, savePath_.c_str()); - - // everything whent fine, removing the file - std::remove(savePath_.c_str()); } catch (const std::exception& e) { RING_ERR("[Account %s] couldn't load messages from %s: %s", account_.getAccountID().c_str(), savePath_.c_str(), e.what()); } @@ -210,12 +212,20 @@ MessageEngine::load() void MessageEngine::save() const +{ + std::lock_guard<std::mutex> lock(messagesMutex_); + save_(); +} + +void +MessageEngine::save_() const { try { Json::Value root(Json::objectValue); - std::unique_lock<std::mutex> lock(messagesMutex_); for (auto& c : messages_) { auto& v = c.second; + if (v.status == MessageStatus::FAILURE || v.status == MessageStatus::SENT) + continue; Json::Value msg; std::ostringstream msgsId; msgsId << std::hex << c.first; @@ -229,15 +239,27 @@ MessageEngine::save() const payloads[p.first] = p.second; root[msgsId.str()] = std::move(msg); } - lock.unlock(); - std::ofstream file; - file.exceptions(std::ifstream::failbit | std::ifstream::badbit); - file.open(savePath_, std::ios::trunc); - Json::StreamWriterBuilder wbuilder; - wbuilder["commentStyle"] = "None"; - wbuilder["indentation"] = ""; - file << Json::writeString(wbuilder, root); - RING_DBG("[Account %s] saved %lu messages to %s", account_.getAccountID().c_str(), messages_.size(), savePath_.c_str()); + // Save asynchronously + ThreadPool::instance().run([path = savePath_, + root = std::move(root), + accountID = account_.getAccountID(), + messageNum = messages_.size()] + { + std::lock_guard<std::mutex> lock(fileutils::getFileLock(path)); + try { + Json::StreamWriterBuilder wbuilder; + wbuilder["commentStyle"] = "None"; + wbuilder["indentation"] = ""; + const std::unique_ptr<Json::StreamWriter> writer(wbuilder.newStreamWriter()); + std::ofstream file; + file.exceptions(std::ifstream::failbit | std::ifstream::badbit); + file.open(path, std::ios::trunc); + writer->write(root, &file); + } catch (const std::exception& e) { + RING_ERR("[Account %s] Couldn't save messages to %s: %s", accountID.c_str(), path.c_str(), e.what()); + } + RING_DBG("[Account %s] saved %zu messages to %s", accountID.c_str(), messageNum, path.c_str()); + }); } catch (const std::exception& e) { RING_ERR("[Account %s] couldn't save messages to %s: %s", account_.getAccountID().c_str(), savePath_.c_str(), e.what()); } diff --git a/src/im/message_engine.h b/src/im/message_engine.h index 6da54104fa28f9b5f07f910368eee3d0f312d201..6d3991ee5ba84dae38cb95169419f9e0c47f7895 100644 --- a/src/im/message_engine.h +++ b/src/im/message_engine.h @@ -77,6 +77,7 @@ private: clock::time_point nextEvent() const; void retrySend(); void reschedule(); + void save_() const; struct Message { std::string to;