mirror of
https://github.com/rakshasa/rtorrent.git
synced 2026-08-11 20:52:30 +00:00
Properly propagate errors from download session save.
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user