fix: race condition when removing torrents in GTK client (#8341)

* fix: race condition crash when removing torrents in GTK client

* refactor: remove unused on_remove_done arg from tr_torrentRemove()
This commit is contained in:
Charles Kerr
2026-02-03 18:10:54 -06:00
committed by GitHub
parent 90402fd97b
commit bd4f81f87c
4 changed files with 13 additions and 70 deletions

View File

@@ -172,8 +172,6 @@ private:
void on_torrent_completeness_changed(tr_torrent* tor, tr_completeness completeness, bool was_running);
void on_torrent_metadata_changed(tr_torrent* raw_torrent);
void on_torrent_removal_done(tr_torrent_id_t id, bool succeeded);
private:
Session& core_;
@@ -928,30 +926,11 @@ void Session::remove_torrent(tr_torrent_id_t id, bool delete_files)
void Session::Impl::remove_torrent(tr_torrent_id_t const id, bool const delete_files)
{
auto const& [torrent, _] = find_torrent_by_id(id);
if (!torrent)
{
return;
}
auto const on_remove_done = [core = get_core_ptr()](tr_torrent_id_t const removed_id, bool const ok)
{
Glib::signal_idle().connect_once([core, removed_id, ok]() { core->impl_->on_torrent_removal_done(removed_id, ok); });
};
tr_torrentRemove(&torrent->get_underlying(), delete_files, gtr_file_trash_or_remove, std::move(on_remove_done));
}
void Session::Impl::on_torrent_removal_done(tr_torrent_id_t id, bool succeeded)
{
if (!succeeded)
{
return;
}
if (auto const& [torrent, position] = find_torrent_by_id(id); torrent)
{
get_raw_model()->remove(position);
tr_torrentRemove(&torrent->get_underlying(), delete_files, gtr_file_trash_or_remove);
}
}

View File

@@ -682,13 +682,11 @@ void tr_torrent::stop_now()
// By-value: arguments are moved into the session-thread work item.
void tr_torrentRemoveInSessionThread(
tr_torrent* tor,
bool delete_flag,
tr_torrent_remove_func remove_func,
tr_torrent_remove_done_func on_remove_done) // NOLINT(performance-unnecessary-value-param)
bool const delete_flag,
tr_torrent_remove_func remove_func) // NOLINT(performance-unnecessary-value-param)
{
auto const lock = tor->unique_lock();
bool ok = true;
if (delete_flag && tor->has_metainfo())
{
// ensure the files are all closed and idle before moving
@@ -700,31 +698,20 @@ void tr_torrentRemoveInSessionThread(
remove_func = tr_sys_path_remove;
}
tr_error error;
auto error = tr_error{};
tor->files().remove(tor->current_dir(), tor->name(), remove_func, &error);
if (error)
{
ok = false;
tor->is_deleting_ = false;
tor->error().set_local_error(
tr_logAddWarnTor(
tor,
fmt::format(
fmt::runtime(_("Couldn't remove all torrent files: {error} ({error_code})")),
fmt::arg("error", error.message()),
fmt::arg("error_code", error.code())));
tr_torrentStop(tor);
}
}
if (on_remove_done)
{
on_remove_done(tor->id(), ok);
}
if (ok)
{
tr_torrentFreeInSessionThread(tor);
}
tr_torrentFreeInSessionThread(tor);
}
void tr_torrentStop(tr_torrent* tor)
@@ -741,22 +728,13 @@ void tr_torrentStop(tr_torrent* tor)
tor->session->run_in_session_thread([tor]() { tor->stop_now(); });
}
void tr_torrentRemove(
tr_torrent* tor,
bool delete_flag,
tr_torrent_remove_func remove_func,
tr_torrent_remove_done_func on_remove_done)
void tr_torrentRemove(tr_torrent* tor, bool delete_flag, tr_torrent_remove_func remove_func)
{
TR_ASSERT(tr_isTorrent(tor));
tor->is_deleting_ = true;
tor->session->run_in_session_thread(
tr_torrentRemoveInSessionThread,
tor,
delete_flag,
std::move(remove_func),
std::move(on_remove_done));
tor->session->run_in_session_thread(tr_torrentRemoveInSessionThread, tor, delete_flag, std::move(remove_func));
}
void tr_torrentFreeInSessionThread(tr_torrent* tor)

View File

@@ -1046,16 +1046,8 @@ private:
friend tr_torrent* tr_torrentNew(tr_ctor* ctor, tr_torrent** setme_duplicate_of);
friend uint64_t tr_torrentGetBytesLeftToAllocate(tr_torrent const* tor);
friend void tr_torrentFreeInSessionThread(tr_torrent* tor);
friend void tr_torrentRemoveInSessionThread(
tr_torrent* tor,
bool delete_flag,
tr_torrent_remove_func remove_func,
tr_torrent_remove_done_func on_remove_done);
friend void tr_torrentRemove(
tr_torrent* tor,
bool delete_flag,
tr_torrent_remove_func remove_func,
tr_torrent_remove_done_func on_remove_done);
friend void tr_torrentRemoveInSessionThread(tr_torrent* tor, bool delete_flag, tr_torrent_remove_func remove_func);
friend void tr_torrentRemove(tr_torrent* tor, bool delete_flag, tr_torrent_remove_func remove_func);
friend void tr_torrentSetDownloadDir(tr_torrent* tor, std::string_view path);
friend void tr_torrentSetPriority(tr_torrent* tor, tr_priority_t priority);
friend void tr_torrentStart(tr_torrent* tor);

View File

@@ -810,7 +810,6 @@ tr_torrent* tr_torrentNew(tr_ctor* ctor, tr_torrent** setme_duplicate_of);
@{ */
using tr_torrent_remove_func = std::function<bool(std::string_view filename, tr_error* error)>;
using tr_torrent_remove_done_func = std::function<void(tr_torrent_id_t, bool success)>;
/**
* @brief Removes our torrent and .resume files for this torrent
@@ -820,13 +819,8 @@ using tr_torrent_remove_done_func = std::function<void(tr_torrent_id_t, bool suc
* to move to a recycle bin instead of deleting.
* The callback is invoked in the session thread and the filename view
* is only valid for the duration of the call.
* @param on_remove_done A callback to invoke in the session thread when removal is done.
*/
void tr_torrentRemove(
tr_torrent* tor,
bool delete_flag,
tr_torrent_remove_func remove_func = {},
tr_torrent_remove_done_func on_remove_done = {});
void tr_torrentRemove(tr_torrent* tor, bool delete_flag, tr_torrent_remove_func remove_func = {});
/** @brief Start a torrent */
void tr_torrentStart(tr_torrent* torrent);