Properly propagate errors from download session save.

This commit is contained in:
Jari Sundell
2025-12-30 14:00:34 +01:00
committed by GitHub
parent 787738e36a
commit def6551488
3 changed files with 66 additions and 52 deletions
+17 -37
View File
@@ -113,32 +113,26 @@ is_correct_format(const std::string& f) {
return true;
}
bool
void
save_stream(const std::string& path, bool use_fsyncdisk, const std::stringstream& stream) {
std::fstream output(path.c_str(), std::ios::out | std::ios::trunc);
// TODO: If we cannot open more files, wait for some to finish and try again.
if (!output.is_open()) {
// LT_LOG("failed to open file for writing : path:%s", path.c_str());
return false;
}
if (!output.is_open())
throw torrent::storage_error("failed to open file for writing : " + path);
output << stream.rdbuf();
if (!output.good()) {
// LT_LOG("failed to write stream to file : path:%s", path.c_str());
return false;
}
if (!output.good())
throw torrent::storage_error("failed to write stream to file : " + path);
output.close();
// Ensure that the new file is actually written to the disk
int fd = ::open(path.c_str(), O_WRONLY);
if (fd < 0) {
// LT_LOG("failed to open file descriptor for fdatasync : path:%s", path.c_str());
return false;
}
if (fd < 0)
throw torrent::storage_error("failed to open file descriptor for fsync : " + path);
if (use_fsyncdisk) {
#ifdef __APPLE__
@@ -149,7 +143,6 @@ save_stream(const std::string& path, bool use_fsyncdisk, const std::stringstream
}
::close(fd);
return true;
}
} // namespace anonymous
@@ -159,39 +152,26 @@ DownloadStorer::save_and_move_streams(const std::string& path, bool use_fsyncdis
const std::stringstream* torrent_stream,
const std::stringstream* rtorrent_stream,
const std::stringstream* libtorrent_stream) {
// LT_LOG("saving download : download:%p path:%s", download, path.c_str());
auto torrent_path = path;
auto libtorrent_path = path + ".libtorrent_resume";
auto rtorrent_path = path + ".rtorrent";
if (torrent_stream) {
if (!save_stream(torrent_path + ".new", use_fsyncdisk, *torrent_stream))
return;
}
if (torrent_stream)
save_stream(torrent_path + ".new", use_fsyncdisk, *torrent_stream);
if (!save_stream(libtorrent_path + ".new", use_fsyncdisk, *libtorrent_stream))
return;
if (!save_stream(rtorrent_path + ".new", use_fsyncdisk, *rtorrent_stream))
return;
save_stream(libtorrent_path + ".new", use_fsyncdisk, *libtorrent_stream);
save_stream(rtorrent_path + ".new", use_fsyncdisk, *rtorrent_stream);
if (torrent_stream) {
if (::rename((torrent_path + ".new").c_str(), torrent_path.c_str()) == -1) {
// LT_LOG("failed to rename torrent file : %s", torrent_path.c_str());
return;
}
if (::rename((torrent_path + ".new").c_str(), torrent_path.c_str()) == -1)
throw torrent::storage_error("failed to rename torrent file : " + torrent_path);
}
if (::rename((libtorrent_path + ".new").c_str(), libtorrent_path.c_str()) == -1) {
// LT_LOG("failed to rename libtorrent resume file : %s", libtorrent_path.c_str());
return;
}
if (::rename((libtorrent_path + ".new").c_str(), libtorrent_path.c_str()) == -1)
throw torrent::storage_error("failed to rename libtorrent resume file : " + libtorrent_path);
if (::rename((rtorrent_path + ".new").c_str(), rtorrent_path.c_str()) == -1) {
// LT_LOG("failed to rename rtorrent resume file : %s", rtorrent_path.c_str());
return;
}
if (::rename((rtorrent_path + ".new").c_str(), rtorrent_path.c_str()) == -1)
throw torrent::storage_error("failed to rename rtorrent resume file : " + rtorrent_path);
}
utils::Directory
+46 -15
View File
@@ -317,25 +317,29 @@ SessionManager::process_next_save_request_unsafe() {
itr->second = std::move(request);
itr->first = std::async(std::launch::async, [this, itr]() {
// TODO: Properly handle errors here, and report back to session thread.
// TODO: Consider adding a failed_saves with error info.
auto cleanup_fn = [this, itr]() {
std::unique_lock<std::mutex> lock(m_mutex);
DownloadStorer::save_and_move_streams(itr->second.path, m_use_fsyncdisk,
itr->second.torrent_stream.get(),
itr->second.rtorrent_stream.get(),
itr->second.libtorrent_stream.get());
if (m_finished_saves.empty())
session_thread::callback(this, [this]() { process_finished_saves(); });
{
std::unique_lock<std::mutex> lock(m_mutex);
m_finished_saves.push_back(std::move(*itr));
m_finished_condition.notify_all();
if (m_finished_saves.empty())
session_thread::callback(this, [this]() { process_finished_saves(); });
m_processing_saves.erase(itr);
};
m_finished_saves.push_back(std::move(*itr));
m_finished_condition.notify_all();
m_processing_saves.erase(itr);
try {
DownloadStorer::save_and_move_streams(itr->second.path, m_use_fsyncdisk,
itr->second.torrent_stream.get(),
itr->second.rtorrent_stream.get(),
itr->second.libtorrent_stream.get());
} catch (...) {
cleanup_fn();
throw;
}
cleanup_fn();
});
}
@@ -348,8 +352,35 @@ SessionManager::process_finished_saves() {
if (!m_active)
throw torrent::internal_error("SessionManager::process_finished_saves() called while not active.");
for (auto& request : m_finished_saves)
for (auto& request : m_finished_saves) {
try {
request.first.get();
} catch (torrent::storage_error& e) {
LT_LOG("error saving download : storage error :download:%p path:%s : %s", request.second.download, request.second.path.c_str(), e.what());
if (m_last_storage_error_message + std::chrono::minutes(5) > torrent::this_thread::cached_time()) {
m_ignored_storage_error_count++;
continue;
}
lt_log_print(torrent::LOG_ERROR, "Storage errors saving session data for download: ignored:%u : %s", m_ignored_storage_error_count, e.what());
m_last_storage_error_message = torrent::this_thread::cached_time();
m_ignored_storage_error_count = 0;
continue;
} catch (torrent::internal_error& e) {
LT_LOG("error saving download : internal error : download:%p path:%s : %s", request.second.download, request.second.path.c_str(), e.what());
throw;
} catch (...) {
LT_LOG("error saving download : unknown error : download:%p path:%s", request.second.download, request.second.path.c_str());
throw;
}
LT_LOG("finished saving download : download:%p path:%s", request.second.download, request.second.path.c_str());
}
m_finished_saves.clear();
}
+3
View File
@@ -107,6 +107,9 @@ private:
std::unique_ptr<utils::Lockfile> m_lockfile;
std::chrono::microseconds m_last_storage_error_message{};
unsigned int m_ignored_storage_error_count{};
// Pending builds are only ever locked by main thread.
std::mutex m_pending_builds_mutex;
std::deque<core::Download*> m_pending_builds;