#include "config.h" #include "control.h" #include #include #include #include #include #include #include "core/dht_manager.h" #include "core/http_queue.h" #include "core/manager.h" #include "core/view_manager.h" #include "display/canvas.h" #include "display/window.h" #include "display/window_http_queue.h" #include "display/window_input.h" #include "display/window_statusbar.h" #include "display/window_title.h" #include "display/manager.h" #include "input/manager.h" #include "input/input_event.h" #include "rpc/command_scheduler.h" #include "rpc/lua.h" #include "rpc/parse_commands.h" #include "rpc/object_storage.h" #include "session/session_manager.h" #include "ui/root.h" #include "utils/watch_ready_queue.h" Control::Control() : m_ui(new ui::Root()), m_display(new display::Manager()), m_input(new input::Manager()), m_inputStdin(new input::InputEvent(STDIN_FILENO)), m_commandScheduler(new rpc::CommandScheduler()), m_objectStorage(new rpc::object_storage()), m_lua_engine(new rpc::LuaEngine()), m_directory_events(new torrent::directory_events()), m_watch_ready_queue(new utils::WatchReadyQueue()) { m_core = std::make_unique(); m_view_manager = std::make_unique(); m_dht_manager = std::make_unique(); m_inputStdin->slot_pressed(std::bind(&input::Manager::pressed, m_input.get(), std::placeholders::_1)); m_task_shutdown.slot() = [this] { handle_shutdown(); }; m_task_shutdown_clear_requests.slot() = [this] { handle_shutdown_clear_requests(); }; m_commandScheduler->set_slot_error_message([this](const std::string& msg) { m_core->push_log_std(msg); }); } Control::~Control() { m_view_manager.reset(); m_ui.reset(); m_display.reset(); } void Control::initialize() { session_thread::thread()->start_thread(); scgi_thread::thread()->start_thread(); display::Canvas::initialize(); display::Window::slot_schedule([this](display::Window* w, std::chrono::microseconds t) { m_display->schedule(w, t); }); display::Window::slot_unschedule([this](display::Window* w) { m_display->unschedule(w); }); display::Window::slot_adjust([this]() { m_display->adjust_layout(); }); m_core->set_hashing_view(*m_view_manager->find_throw("hashing")); m_ui->init(this); if(!display::Canvas::daemon()) m_inputStdin->insert(); } void Control::cleanup() { rpc::rpc.cleanup(); torrent::this_thread::scheduler()->erase(&m_task_shutdown); torrent::this_thread::scheduler()->erase(&m_task_shutdown_clear_requests); if(!display::Canvas::daemon()) m_inputStdin->remove(); m_directory_events->close(); if (scgi_thread::thread()->is_active()) scgi_thread::thread()->stop_thread_wait(); // Wait for all session files to be written. session_thread::manager()->flush_all_pending_builds(); session_thread::thread()->stop_thread_wait(); m_ui->cleanup(); m_core->cleanup(); display::Canvas::erase_std(); display::Canvas::refresh_std(); display::Canvas::do_update(); display::Canvas::cleanup(); } void Control::cleanup_exception() { display::Canvas::cleanup(); } bool Control::is_shutdown_completed() { if (!m_shutdown_quick) return false; // Tracker requests can be disowned, so wait for these to // finish. The edge case of torrent http downloads may delay // shutdown. // TODO: We keep http requests in the queue for a while after, so improve this check to ignore // those. if (torrent::net_thread::http_stack()->size() != 0 || !core()->http_queue()->empty()) return false; return core()->is_download_shutdown_completed(); } void Control::handle_shutdown() { m_watch_ready_queue->shutdown(); rpc::commands.call_catch("event.system.shutdown", rpc::make_target(), "shutdown", "System shutdown event action failed: "); if (scgi_thread::thread()->is_active()) scgi_thread::thread()->stop_thread_wait(); if (!m_shutdown_quick) { torrent::runtime::network_manager()->listen_close(); torrent::runtime::shutdown(); m_directory_events->close(); m_core->shutdown(false); if (!m_task_shutdown.is_scheduled()) torrent::this_thread::scheduler()->wait_for_ceil_seconds(&m_task_shutdown, 5s); } else { torrent::runtime::quick_shutdown(); m_core->shutdown(true); } if (!m_task_shutdown_clear_requests.is_scheduled()) torrent::this_thread::scheduler()->wait_for_ceil_seconds(&m_task_shutdown_clear_requests, 10s); m_shutdown_quick = true; m_shutdown_received = false; } void Control::handle_shutdown_clear_requests() { torrent::net_thread::http_stack()->clear_requests(); // Use 5s for the initial wait to ensure trackers get a chance to finish both IPv4 and IPv6 requests. if (m_clear_requests_count++ == 0) torrent::this_thread::scheduler()->wait_for(&m_task_shutdown_clear_requests, 5s); else torrent::this_thread::scheduler()->wait_for(&m_task_shutdown_clear_requests, 1s); }