mirror of
https://github.com/rakshasa/rtorrent.git
synced 2026-10-21 16:46:18 +00:00
Save session data once on shutdown, before waiting for stopped announces
Write it after downloads stop, or after the loop if skipped; one error policy for saves.
This commit is contained in:
@@ -10,6 +10,7 @@
|
|||||||
#include <torrent/utils/directory_events.h>
|
#include <torrent/utils/directory_events.h>
|
||||||
|
|
||||||
#include "core/dht_manager.h"
|
#include "core/dht_manager.h"
|
||||||
|
#include "core/download_list.h"
|
||||||
#include "core/http_queue.h"
|
#include "core/http_queue.h"
|
||||||
#include "core/manager.h"
|
#include "core/manager.h"
|
||||||
#include "core/view_manager.h"
|
#include "core/view_manager.h"
|
||||||
@@ -152,6 +153,15 @@ Control::handle_shutdown() {
|
|||||||
m_core->shutdown(true);
|
m_core->shutdown(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!m_shutdown_session_saved) {
|
||||||
|
// Transfers are stopped, so this is the final state; write it before the
|
||||||
|
// stopped announces can hold the shutdown. A shutdown hook may already
|
||||||
|
// have queued these resume builds.
|
||||||
|
m_core->download_list()->session_save();
|
||||||
|
session_thread::manager()->flush_shutdown_saves();
|
||||||
|
m_shutdown_session_saved = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (!m_task_shutdown_clear_requests.is_scheduled())
|
if (!m_task_shutdown_clear_requests.is_scheduled())
|
||||||
torrent::this_thread::scheduler()->wait_for_ceil_seconds(&m_task_shutdown_clear_requests, 10s);
|
torrent::this_thread::scheduler()->wait_for_ceil_seconds(&m_task_shutdown_clear_requests, 10s);
|
||||||
|
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ public:
|
|||||||
bool is_shutdown_completed();
|
bool is_shutdown_completed();
|
||||||
bool is_shutdown_received() { return m_shutdown_received; }
|
bool is_shutdown_received() { return m_shutdown_received; }
|
||||||
bool is_shutdown_started() { return m_shutdown_quick; }
|
bool is_shutdown_started() { return m_shutdown_quick; }
|
||||||
|
bool is_shutdown_session_saved() { return m_shutdown_session_saved; }
|
||||||
|
|
||||||
void initialize();
|
void initialize();
|
||||||
void cleanup();
|
void cleanup();
|
||||||
@@ -114,6 +115,7 @@ private:
|
|||||||
|
|
||||||
align_cacheline
|
align_cacheline
|
||||||
|
|
||||||
|
bool m_shutdown_session_saved{};
|
||||||
std::atomic<bool> m_shutdown_received{};
|
std::atomic<bool> m_shutdown_received{};
|
||||||
std::atomic<bool> m_shutdown_quick{};
|
std::atomic<bool> m_shutdown_quick{};
|
||||||
};
|
};
|
||||||
|
|||||||
+5
-1
@@ -489,7 +489,11 @@ main(int argc, char** argv) {
|
|||||||
|
|
||||||
torrent::system::Thread::self()->event_loop();
|
torrent::system::Thread::self()->event_loop();
|
||||||
|
|
||||||
control->core()->download_list()->session_save();
|
// A quick shutdown with no tracker request pending leaves the loop before
|
||||||
|
// handle_shutdown() could save.
|
||||||
|
if (!control->is_shutdown_session_saved())
|
||||||
|
control->core()->download_list()->session_save();
|
||||||
|
|
||||||
control->cleanup();
|
control->cleanup();
|
||||||
|
|
||||||
} catch (torrent::internal_error& e) {
|
} catch (torrent::internal_error& e) {
|
||||||
|
|||||||
@@ -429,42 +429,62 @@ SessionManager::process_finished_saves() {
|
|||||||
if (!m_active)
|
if (!m_active)
|
||||||
throw torrent::internal_error("SessionManager::process_finished_saves() called while not 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 {
|
consume_finished_save_unsafe(request);
|
||||||
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_ERRORS, "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();
|
m_finished_saves.clear();
|
||||||
|
|
||||||
callback_save_request();
|
callback_save_request();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Storage errors share a five-minute log limit and count what they suppress.
|
||||||
|
// Callers hold m_mutex.
|
||||||
|
void
|
||||||
|
SessionManager::report_storage_error_unsafe(const std::string& what) {
|
||||||
|
if (m_last_storage_error_message + std::chrono::minutes(5) > torrent::this_thread::cached_time()) {
|
||||||
|
m_ignored_storage_error_count++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
lt_log_print(torrent::LOG_ERRORS, "Storage errors saving session data for download: ignored:%u : %s", m_ignored_storage_error_count, what.c_str());
|
||||||
|
|
||||||
|
m_last_storage_error_message = torrent::this_thread::cached_time();
|
||||||
|
m_ignored_storage_error_count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The one policy for a finished save, on the session thread and in shutdown
|
||||||
|
// flushes: storage errors go to report_storage_error_unsafe(), internal and
|
||||||
|
// unknown errors propagate. Callers hold m_mutex.
|
||||||
|
void
|
||||||
|
SessionManager::consume_finished_save_unsafe(ProcessingSave& request) {
|
||||||
|
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());
|
||||||
|
report_storage_error_unsafe(e.what());
|
||||||
|
return;
|
||||||
|
|
||||||
|
} 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());
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
SessionManager::flush_shutdown_saves() {
|
||||||
|
assert(torrent::this_thread::thread() == torrent::main_thread::thread());
|
||||||
|
flush_all_pending_builds();
|
||||||
|
std::unique_lock<std::mutex> lock(m_mutex);
|
||||||
|
flush_all_and_wait_unsafe(lock);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
SessionManager::flush_all_and_wait_unsafe(std::unique_lock<std::mutex>& lock) {
|
SessionManager::flush_all_and_wait_unsafe(std::unique_lock<std::mutex>& lock) {
|
||||||
LT_LOG("flushing all pending saves", 0);
|
LT_LOG("flushing all pending saves", 0);
|
||||||
@@ -484,10 +504,17 @@ SessionManager::flush_all_and_wait_unsafe(std::unique_lock<std::mutex>& lock) {
|
|||||||
m_finished_condition.wait(lock);
|
m_finished_condition.wait(lock);
|
||||||
|
|
||||||
for (auto& request : m_finished_saves)
|
for (auto& request : m_finished_saves)
|
||||||
LT_LOG("finished saving download : download:%p path:%s", request.second.download, request.second.path.c_str());
|
consume_finished_save_unsafe(request);
|
||||||
|
|
||||||
m_finished_saves.clear();
|
m_finished_saves.clear();
|
||||||
|
|
||||||
|
// Report and reset what was suppressed since the last error line. The count
|
||||||
|
// includes saves the session thread consumed while this flush waited.
|
||||||
|
if (m_ignored_storage_error_count != 0) {
|
||||||
|
lt_log_print(torrent::LOG_ERRORS, "Storage errors flushing session data: ignored:%u", m_ignored_storage_error_count);
|
||||||
|
m_ignored_storage_error_count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
LT_LOG("flushed all pending saves", 0);
|
LT_LOG("flushed all pending saves", 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -79,6 +79,7 @@ protected:
|
|||||||
void cleanup();
|
void cleanup();
|
||||||
|
|
||||||
void flush_all_pending_builds();
|
void flush_all_pending_builds();
|
||||||
|
void flush_shutdown_saves();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void callback_pending_builds();
|
void callback_pending_builds();
|
||||||
@@ -98,6 +99,9 @@ private:
|
|||||||
|
|
||||||
using ProcessingSave = std::pair<std::future<void>, SaveRequest>;
|
using ProcessingSave = std::pair<std::future<void>, SaveRequest>;
|
||||||
|
|
||||||
|
void consume_finished_save_unsafe(ProcessingSave& request);
|
||||||
|
void report_storage_error_unsafe(const std::string& what);
|
||||||
|
|
||||||
torrent::system::Thread* m_thread;
|
torrent::system::Thread* m_thread;
|
||||||
torrent::system::callback_id m_callback_id;
|
torrent::system::callback_id m_callback_id;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user