diff --git a/src/session/download_storer.cc b/src/session/download_storer.cc index e7b016f1..51e27b18 100644 --- a/src/session/download_storer.cc +++ b/src/session/download_storer.cc @@ -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 diff --git a/src/session/session_manager.cc b/src/session/session_manager.cc index 8790710a..f95f728c 100644 --- a/src/session/session_manager.cc +++ b/src/session/session_manager.cc @@ -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 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 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(); } diff --git a/src/session/session_manager.h b/src/session/session_manager.h index e311ae73..82c4d1bc 100644 --- a/src/session/session_manager.h +++ b/src/session/session_manager.h @@ -107,6 +107,9 @@ private: std::unique_ptr 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 m_pending_builds;