Let the download list own its downloads through a shared pointer.

A multicall could then dispatch commands on an erased download.
This commit is contained in:
xirvik
2026-08-16 19:17:00 +00:00
committed by Jari Sundell
parent 58e627f2be
commit 385e149ccc
14 changed files with 116 additions and 56 deletions
+1
View File
@@ -1,6 +1,7 @@
#ifndef RTORRENT_CORE_DOWNLOAD_H
#define RTORRENT_CORE_DOWNLOAD_H
#include <memory>
#include <torrent/common.h>
#include <torrent/download.h>
#include <torrent/download_info.h>
+10 -12
View File
@@ -37,7 +37,7 @@ namespace core {
inline void
DownloadList::check_contains([[maybe_unused]] Download* d) {
#ifdef USE_EXTRA_DEBUG
if (std::find(begin(), end(), d) == end())
if (std::find_if(begin(), end(), [d](const auto& entry) { return entry.get() == d; }) == end())
throw torrent::internal_error("DownloadList::check_contains(...) failed.");
#endif
}
@@ -54,7 +54,6 @@ DownloadList::clear() {
base_type::pop_back();
torrent::download_remove(*download->download());
delete download;
} catch (torrent::internal_error& e) {
lt_log_print(torrent::LOG_ERROR, "DownloadList::clear() failed to close or remove download: %s", e.what());
@@ -70,7 +69,7 @@ DownloadList::clear() {
void
DownloadList::session_save() {
for (auto& download : *this)
session_thread::manager()->save_resume_download(download);
session_thread::manager()->save_resume_download(download.get());
control->dht_manager()->save_dht_cache();
control->ui()->save_input_history();
@@ -78,7 +77,7 @@ DownloadList::session_save() {
DownloadList::iterator
DownloadList::find(const torrent::HashString& hash) {
return std::find_if(begin(), end(), [hash](Download* d) { return hash == d->info()->hash(); });
return std::find_if(begin(), end(), [hash](const auto& d) { return hash == d->info()->hash(); });
}
DownloadList::iterator
@@ -91,14 +90,14 @@ DownloadList::find_hex(const char* hash) {
if (torrent::utils::transform_from_hex(hash, hash + 40, key) != key.end())
return end();
return std::find_if(begin(), end(), [key](Download* d) { return key == d->info()->hash(); });
return std::find_if(begin(), end(), [key](const auto& d) { return key == d->info()->hash(); });
}
Download*
DownloadList::find_hex_ptr(const char* hash) {
iterator itr = find_hex(hash);
return itr != end() ? *itr : NULL;
return itr != end() ? itr->get() : NULL;
}
Download*
@@ -159,7 +158,7 @@ DownloadList::create(std::istream* str, uint32_t tracker_key, bool printLog) {
DownloadList::iterator
DownloadList::insert(Download* download) {
iterator itr = base_type::insert(end(), download);
iterator itr = base_type::insert(end(), std::shared_ptr<Download>(download));
lt_log_print_info(torrent::LOG_TORRENT_INFO, download->info(), "download_list", "Inserting download.");
@@ -170,7 +169,7 @@ DownloadList::insert(Download* download) {
// This needs to be separated into two different calls to ensure
// the download remains in the view.
for (auto v : *control->view_manager())
v->insert(download);
v->insert(*itr);
for (auto v : *control->view_manager())
v->filter_download(download);
@@ -187,7 +186,7 @@ DownloadList::insert(Download* download) {
void
DownloadList::erase_ptr(Download* download) {
erase(std::find(begin(), end(), download));
erase(std::find_if(begin(), end(), [download](const auto& entry) { return entry.get() == download; }));
}
DownloadList::iterator
@@ -201,15 +200,14 @@ DownloadList::erase(iterator itr) {
(*itr)->set_hash_failed(true);
close(*itr);
session_thread::manager()->remove_download(*itr);
session_thread::manager()->remove_download(itr->get());
DL_TRIGGER_EVENT(*itr, "event.download.erased");
for (auto v : *control->view_manager())
v->erase(*itr);
v->erase(itr->get());
torrent::download_remove(*(*itr)->download());
delete *itr;
return base_type::erase(itr);
}
+11 -2
View File
@@ -3,6 +3,7 @@
#include <iosfwd>
#include <list>
#include <memory>
#include <string>
namespace torrent {
@@ -20,9 +21,9 @@ class Download;
//
// Fix apply_on_ratio if the base_type is changed.
class DownloadList : private std::list<Download*> {
class DownloadList : private std::list<std::shared_ptr<Download>> {
public:
typedef std::list<Download*> base_type;
typedef std::list<std::shared_ptr<Download>> base_type;
using base_type::iterator;
using base_type::const_iterator;
@@ -64,6 +65,14 @@ public:
bool open(Download* d);
void open_throw(Download* d);
// Overloads for the list's own entries, so callers iterating it do not need
// to unwrap.
bool open(const value_type& d) { return open(d.get()); }
void close(const value_type& d) { close(d.get()); }
void close_quick(const value_type& d) { close_quick(d.get()); }
void pause(const value_type& d) { pause(d.get()); }
void resume(const value_type& d) { resume(d.get()); }
void close(Download* d);
void close_directly(Download* d);
void close_quick(Download* d);
+4 -4
View File
@@ -154,10 +154,10 @@ Manager::cleanup() {
void
Manager::shutdown(bool force) {
if (!force) {
for (auto d : *m_download_list)
m_download_list->pause_default(d);
for (const auto& d : *m_download_list)
m_download_list->pause_default(d.get());
} else {
for (auto d : *m_download_list)
for (const auto& d : *m_download_list)
m_download_list->close_quick(d);
}
}
@@ -405,7 +405,7 @@ Manager::receive_hashing_changed() {
continue;
try {
m_download_list->open_throw(*itr);
m_download_list->open_throw(itr->get());
// Since the bitfield is allocated on loading of resume load or
// hash start, and unallocated on close, we know that if it it
+40 -16
View File
@@ -16,10 +16,20 @@
namespace core {
// Also add focus thingie here?
// Matches the list entry that owns a given download.
inline auto
entry_is(Download* download) {
return [download](const std::shared_ptr<Download>& entry) { return entry.get() == download; };
}
struct view_downloads_compare {
view_downloads_compare(const torrent::Object& cmd) :
m_command(cmd) {}
bool operator()(const std::shared_ptr<Download>& d1, const std::shared_ptr<Download>& d2) const {
return (*this)(d1.get(), d2.get());
}
bool operator()(Download* d1, Download* d2) const {
try {
if (m_command.is_empty())
@@ -54,6 +64,10 @@ struct view_downloads_filter {
view_downloads_filter(const torrent::Object& cmd, const torrent::Object& cmd2) :
m_command(cmd), m_command2(cmd2) {}
bool operator()(const std::shared_ptr<Download>& d1) const {
return (*this)(d1.get());
}
bool operator()(Download* d1) const {
return this->evalCmd(m_command, d1) && this->evalCmd(m_command2, d1);
}
@@ -152,7 +166,7 @@ View::initialize(const std::string& name) {
void
View::erase(Download* download) {
iterator itr = std::find(base_type::begin(), base_type::end(), download);
iterator itr = std::find_if(base_type::begin(), base_type::end(), entry_is(download));
if (itr >= end_visible()) {
erase_internal(itr);
@@ -165,22 +179,24 @@ View::erase(Download* download) {
void
View::set_visible(Download* download) {
iterator itr = std::find(begin_filtered(), end_filtered(), download);
iterator itr = std::find_if(begin_filtered(), end_filtered(), entry_is(download));
if (itr == end_filtered())
return;
// Don't optimize erase since we want to keep the order of the
// non-visible elements.
auto entry = *itr;
base_type::erase(itr);
insert_visible(download);
insert_visible(entry);
rpc::call_object_nothrow(m_event_added, rpc::make_target(download));
}
void
View::set_not_visible(Download* download) {
iterator itr = std::find(begin_visible(), end_visible(), download);
iterator itr = std::find_if(begin_visible(), end_visible(), entry_is(download));
if (itr == end_visible())
return;
@@ -190,8 +206,10 @@ View::set_not_visible(Download* download) {
// Don't optimize erase since we want to keep the order of the
// non-visible elements.
auto entry = *itr;
base_type::erase(itr);
base_type::push_back(download);
base_type::push_back(entry);
rpc::call_object_nothrow(m_event_removed, rpc::make_target(download));
}
@@ -241,12 +259,12 @@ View::prev_focus(unsigned int i) {
void
View::sort() {
Download* curFocus = focus() != end_visible() ? *focus() : NULL;
Download* curFocus = focus() != end_visible() ? focus()->get() : NULL;
// Don't go randomly switching around equivalent elements.
std::stable_sort(begin(), end_visible(), view_downloads_compare(m_sortCurrent));
m_focus = position(std::find(begin(), end_visible(), curFocus));
m_focus = position(std::find_if(begin(), end_visible(), entry_is(curFocus)));
emit_changed();
}
@@ -280,10 +298,10 @@ View::filter() {
// set the elements to NULL as we trigger commands on them. Or
// perhaps always clear them, thus not throwing anything.
if (!m_event_removed.is_empty())
std::for_each(changed.begin(), splitChanged, std::bind(&rpc::call_object_d_nothrow, m_event_removed, std::placeholders::_1));
std::for_each(changed.begin(), splitChanged, [this](const auto& d) { rpc::call_object_d_nothrow(m_event_removed, d.get()); });
if (!m_event_added.is_empty())
std::for_each(changed.begin(), splitChanged, std::bind(&rpc::call_object_d_nothrow, m_event_added, std::placeholders::_1));
std::for_each(changed.begin(), splitChanged, [this](const auto& d) { rpc::call_object_d_nothrow(m_event_added, d.get()); });
emit_changed();
}
@@ -294,21 +312,23 @@ View::filter_by(const torrent::Object& condition, View::base_type& result) {
view_downloads_filter matches = view_downloads_filter(condition, m_temp_filter);
for (iterator itr = begin_visible(); itr != end_visible(); ++itr)
if (matches(*itr))
if (matches(itr->get()))
result.push_back(*itr);
}
void
View::filter_download(core::Download* download) {
iterator itr = std::find(base_type::begin(), base_type::end(), download);
iterator itr = std::find_if(base_type::begin(), base_type::end(), entry_is(download));
if (itr == base_type::end())
throw torrent::internal_error("View::filter_download(...) could not find download.");
if (view_downloads_filter(m_filter, m_temp_filter)(download)) {
if (itr >= end_visible()) {
auto entry = *itr;
erase_internal(itr);
insert_visible(download);
insert_visible(entry);
rpc::call_object_nothrow(m_event_added, rpc::make_target(download));
@@ -317,16 +337,20 @@ View::filter_download(core::Download* download) {
// already visible.
//
// Consider removing this.
auto entry = *itr;
erase_internal(itr);
insert_visible(download);
insert_visible(entry);
}
} else {
if (itr >= end_visible())
return;
auto entry = *itr;
erase_internal(itr);
base_type::push_back(download);
base_type::push_back(entry);
rpc::call_object_nothrow(m_event_removed, rpc::make_target(download));
}
@@ -345,8 +369,8 @@ View::clear_filter_on() {
}
inline void
View::insert_visible(Download* d) {
auto itr = std::find_if(begin_visible(), end_visible(), [this, d](auto d2) { return view_downloads_compare(m_sortNew)(d, d2); });
View::insert_visible(const std::shared_ptr<Download>& d) {
auto itr = std::find_if(begin_visible(), end_visible(), [this, &d](const auto& d2) { return view_downloads_compare(m_sortNew)(d.get(), d2.get()); });
m_size++;
m_focus += (m_focus >= position(itr));
+6 -5
View File
@@ -15,6 +15,7 @@
#include <functional>
#include <list>
#include <memory>
#include <string>
#include <vector>
#include <torrent/object.h>
@@ -26,9 +27,9 @@ namespace core {
class Download;
class View : private std::vector<Download*> {
class View : private std::vector<std::shared_ptr<Download>> {
public:
typedef std::vector<Download*> base_type;
typedef std::vector<std::shared_ptr<Download>> base_type;
typedef std::function<void()> slot_void;
typedef std::list<slot_void> signal_void;
@@ -72,7 +73,7 @@ public:
emit_changed();
}
void insert(Download* download) { base_type::push_back(download); }
void insert(const std::shared_ptr<Download>& download) { base_type::push_back(download); }
void erase(Download* download);
void set_visible(Download* download);
@@ -124,9 +125,9 @@ private:
View(const View&);
void operator=(const View&);
void push_back(Download* d) { base_type::push_back(d); }
void push_back(const std::shared_ptr<Download>& d) { base_type::push_back(d); }
inline void insert_visible(Download* d);
inline void insert_visible(const std::shared_ptr<Download>& d);
inline void erase_internal(iterator itr);
void emit_changed();