From 59ddca3f13b7258047df1588d780f83f49da550b Mon Sep 17 00:00:00 2001 From: Joris CARRIER Date: Fri, 17 Nov 2023 08:46:12 +0100 Subject: [PATCH 1/5] add alert exception for async methods --- include/libtorrent/alert_types.hpp | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/include/libtorrent/alert_types.hpp b/include/libtorrent/alert_types.hpp index a65d5624cca..f922a335778 100644 --- a/include/libtorrent/alert_types.hpp +++ b/include/libtorrent/alert_types.hpp @@ -79,6 +79,8 @@ POSSIBILITY OF SUCH DAMAGE. #include #include // for va_list +#include + #if TORRENT_ABI_VERSION == 1 #define PROGRESS_NOTIFICATION | alert::progress_notification #else @@ -3116,6 +3118,27 @@ TORRENT_VERSION_NAMESPACE_3_END TORRENT_EXTRA_EXPORT char const* performance_warning_str(performance_alert::performance_warning_t i); + template + class alert_exception : public std::exception { + public: + alert_exception(const T* a) : m_alert(a) {} + + const char* what() const noexcept override { + return m_alert->message().c_str(); + } + + private: + const T* m_alert; + }; + + template + class alert_dont_post_exception: public std::exception { + const char* what() const noexcept override { + return std::string(boost::typeindex::type_id().pretty_name() + " ignored").c_str(); + } + }; + + #undef TORRENT_DEFINE_ALERT_IMPL #undef TORRENT_DEFINE_ALERT #undef TORRENT_DEFINE_ALERT_PRIO From 8b1e19a979e3e671ddf1ccb0b3ecf44b69475ad4 Mon Sep 17 00:00:00 2001 From: Joris CARRIER Date: Thu, 16 Nov 2023 16:57:33 +0100 Subject: [PATCH 2/5] alert manager return alert on emplace --- include/libtorrent/aux_/alert_manager.hpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/include/libtorrent/aux_/alert_manager.hpp b/include/libtorrent/aux_/alert_manager.hpp index d5f6237e1e0..42d589d4542 100644 --- a/include/libtorrent/aux_/alert_manager.hpp +++ b/include/libtorrent/aux_/alert_manager.hpp @@ -69,7 +69,7 @@ namespace aux { ~alert_manager(); template - void emplace_alert(Args&&... args) try + const T* emplace_alert(Args&&... args) try { std::unique_lock lock(m_mutex); @@ -82,13 +82,15 @@ namespace aux { { // record that we dropped an alert of this type m_dropped.set(T::alert_type); - return; + return nullptr; } T& alert = queue.emplace_back( m_allocations[m_generation], std::forward(args)...); maybe_notify(&alert); + + return &alert; } catch (std::bad_alloc const&) { From 0a6e2a227409aab79aef76154856044b82317c4c Mon Sep 17 00:00:00 2001 From: Joris CARRIER Date: Thu, 16 Nov 2023 16:57:56 +0100 Subject: [PATCH 3/5] add async_flush_cache --- include/libtorrent/torrent.hpp | 7 +++++-- include/libtorrent/torrent_handle.hpp | 6 ++++++ src/torrent.cpp | 16 ++++++++++------ src/torrent_handle.cpp | 9 ++++++++- 4 files changed, 29 insertions(+), 9 deletions(-) diff --git a/include/libtorrent/torrent.hpp b/include/libtorrent/torrent.hpp index ecca52f34ea..17db9d47f14 100644 --- a/include/libtorrent/torrent.hpp +++ b/include/libtorrent/torrent.hpp @@ -48,6 +48,7 @@ POSSIBILITY OF SUCH DAMAGE. #include #include #include +#include #include #include // for numeric_limits #include // for unique_ptr @@ -563,7 +564,8 @@ namespace libtorrent { bool has_error() const { return !!m_error; } error_code error() const { return m_error; } - void flush_cache(); + + void flush_cache(std::shared_ptr> promise = nullptr); void pause(pause_flags_t flags = {}); void resume(); @@ -1307,7 +1309,8 @@ namespace libtorrent { void on_file_renamed(std::string const& filename , file_index_t file_idx , storage_error const& error); - void on_cache_flushed(bool manually_triggered); + + void on_cache_flushed(bool manually_triggered, std::shared_ptr> promise = nullptr); // this is used when a torrent is being removed.It synchronizes with the // disk thread diff --git a/include/libtorrent/torrent_handle.hpp b/include/libtorrent/torrent_handle.hpp index 6ffd49fb9f0..910c2919219 100644 --- a/include/libtorrent/torrent_handle.hpp +++ b/include/libtorrent/torrent_handle.hpp @@ -47,6 +47,7 @@ POSSIBILITY OF SUCH DAMAGE. #include #include #include +#include #include "libtorrent/aux_/disable_warnings_push.hpp" #if TORRENT_ABI_VERSION == 1 @@ -81,6 +82,9 @@ namespace aux { struct torrent; struct client_data_t; + struct cache_flushed_alert; + struct read_piece_alert; + #ifndef BOOST_NO_EXCEPTIONS [[noreturn]] void throw_invalid_handle(); #endif @@ -310,6 +314,7 @@ namespace aux { // Note that if you read multiple pieces, the read operations are not // guaranteed to finish in the same order as you initiated them. void read_piece(piece_index_t piece) const; + std::future async_read_piece(piece_index_t piece); // Returns true if this piece has been completely downloaded and written // to disk, and false otherwise. @@ -659,6 +664,7 @@ namespace aux { // data libtorrent had by the time you called // ``torrent_handle::flush_cache()`` has been written to disk. void flush_cache() const; + std::future async_flush_cache(); // ``force_recheck`` puts the torrent back in a state where it assumes to // have no resume data. All peers will be disconnected and the torrent diff --git a/src/torrent.cpp b/src/torrent.cpp index 43e2cddea18..a35da276edb 100644 --- a/src/torrent.cpp +++ b/src/torrent.cpp @@ -8397,7 +8397,7 @@ namespace { { // we need to keep the object alive during this operation m_ses.disk_thread().async_release_files(m_storage - , std::bind(&torrent::on_cache_flushed, shared_from_this(), false)); + , std::bind(&torrent::on_cache_flushed, shared_from_this(), false, nullptr)); m_ses.deferred_submit_jobs(); } @@ -9462,7 +9462,7 @@ namespace { && !m_session_paused; } - void torrent::flush_cache() + void torrent::flush_cache(std::shared_ptr> promise) { TORRENT_ASSERT(is_single_thread()); @@ -9473,18 +9473,22 @@ namespace { return; } m_ses.disk_thread().async_release_files(m_storage - , std::bind(&torrent::on_cache_flushed, shared_from_this(), true)); + , std::bind(&torrent::on_cache_flushed, shared_from_this(), true, promise)); m_ses.deferred_submit_jobs(); } - void torrent::on_cache_flushed(bool const manually_triggered) try + void torrent::on_cache_flushed(bool const manually_triggered, std::shared_ptr> promise) try { TORRENT_ASSERT(is_single_thread()); if (m_ses.is_aborted()) return; - if (manually_triggered || alerts().should_post()) - alerts().emplace_alert(get_handle()); + if (manually_triggered || alerts().should_post()) { + auto* a = alerts().emplace_alert(get_handle()); + if (promise) promise->set_value(a); + } else { + if (promise) promise->set_exception(std::make_exception_ptr(alert_dont_post_exception())); + } } catch (...) { handle_exception(); } diff --git a/src/torrent_handle.cpp b/src/torrent_handle.cpp index c255e7a7282..6c44385a450 100644 --- a/src/torrent_handle.cpp +++ b/src/torrent_handle.cpp @@ -358,7 +358,14 @@ namespace libtorrent { void torrent_handle::flush_cache() const { - async_call(&torrent::flush_cache); + async_call(&torrent::flush_cache, nullptr); + } + + std::future torrent_handle::async_flush_cache() + { + auto promise = std::make_shared>(); + async_call(&torrent::flush_cache, promise); + return promise->get_future(); } void torrent_handle::set_ssl_certificate( From e8c91570592460455df6981503d4db9fddb507d6 Mon Sep 17 00:00:00 2001 From: Joris CARRIER Date: Fri, 17 Nov 2023 07:33:30 +0100 Subject: [PATCH 4/5] add async_read_piece --- include/libtorrent/torrent.hpp | 5 ++++- src/torrent.cpp | 18 ++++++++++++------ src/torrent_handle.cpp | 9 ++++++++- 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/include/libtorrent/torrent.hpp b/include/libtorrent/torrent.hpp index 17db9d47f14..e7096824a82 100644 --- a/include/libtorrent/torrent.hpp +++ b/include/libtorrent/torrent.hpp @@ -486,8 +486,11 @@ namespace libtorrent { int blocks_left; bool fail; error_code error; + std::shared_ptr> promise; }; - void read_piece(piece_index_t); + + void read_piece(piece_index_t const piece, std::shared_ptr> promise = nullptr); + void on_disk_read_complete(disk_buffer_holder, storage_error const& , peer_request const&, std::shared_ptr); diff --git a/src/torrent.cpp b/src/torrent.cpp index a35da276edb..3f0502f8b4a 100644 --- a/src/torrent.cpp +++ b/src/torrent.cpp @@ -743,7 +743,7 @@ bool is_downloading_state(int const st) m_ses.close_connection(p); } - void torrent::read_piece(piece_index_t const piece) + void torrent::read_piece(piece_index_t const piece, std::shared_ptr> promise) { error_code ec; if (m_abort || m_deleted) @@ -761,7 +761,8 @@ bool is_downloading_state(int const st) if (ec) { - m_ses.alerts().emplace_alert(get_handle(), piece, ec); + auto* a = m_ses.alerts().emplace_alert(get_handle(), piece, ec); + if (promise) promise->set_value(a); return; } @@ -775,8 +776,9 @@ bool is_downloading_state(int const st) { // this shouldn't actually happen boost::shared_array buf; - m_ses.alerts().emplace_alert( + auto* a = m_ses.alerts().emplace_alert( get_handle(), piece, buf, 0); + if (promise) promise->set_value(a); return; } @@ -784,12 +786,14 @@ bool is_downloading_state(int const st) rp->piece_data.reset(new (std::nothrow) char[std::size_t(piece_size)]); if (!rp->piece_data) { - m_ses.alerts().emplace_alert( + auto* a = m_ses.alerts().emplace_alert( get_handle(), piece, error_code(boost::system::errc::not_enough_memory, generic_category())); + if (promise) promise->set_value(a); return; } rp->blocks_left = blocks_in_piece; rp->fail = false; + rp->promise = promise; disk_job_flags_t flags{}; auto const read_mode = settings().get_int(settings_pack::disk_io_read_mode); @@ -1215,13 +1219,15 @@ bool is_downloading_state(int const st) int size = m_torrent_file->piece_size(r.piece); if (rp->fail) { - m_ses.alerts().emplace_alert( + const auto* a = m_ses.alerts().emplace_alert( get_handle(), r.piece, rp->error); + if (rp->promise) rp->promise->set_value(a); } else { - m_ses.alerts().emplace_alert( + const auto* a = m_ses.alerts().emplace_alert( get_handle(), r.piece, rp->piece_data, size); + if (rp->promise) rp->promise->set_value(a); } } } diff --git a/src/torrent_handle.cpp b/src/torrent_handle.cpp index 6c44385a450..ae4f5bd3a43 100644 --- a/src/torrent_handle.cpp +++ b/src/torrent_handle.cpp @@ -751,7 +751,14 @@ namespace libtorrent { void torrent_handle::read_piece(piece_index_t piece) const { - async_call(&torrent::read_piece, piece); + async_call(&torrent::read_piece, piece, nullptr); + } + + std::future torrent_handle::async_read_piece(piece_index_t piece) + { + auto promise = std::make_shared>(); + async_call(&torrent::read_piece, piece, promise); + return promise->get_future(); } bool torrent_handle::have_piece(piece_index_t piece) const From 533c0a6688a15e9a092b5a0b180a2af3ac528ac6 Mon Sep 17 00:00:00 2001 From: Joris CARRIER Date: Fri, 17 Nov 2023 08:27:12 +0100 Subject: [PATCH 5/5] add async_move_storage --- include/libtorrent/torrent.hpp | 4 +-- include/libtorrent/torrent_handle.hpp | 4 +++ src/torrent.cpp | 47 +++++++++++++++++++-------- src/torrent_handle.cpp | 12 +++++-- 4 files changed, 50 insertions(+), 17 deletions(-) diff --git a/include/libtorrent/torrent.hpp b/include/libtorrent/torrent.hpp index e7096824a82..63f4a9558be 100644 --- a/include/libtorrent/torrent.hpp +++ b/include/libtorrent/torrent.hpp @@ -1165,7 +1165,7 @@ namespace libtorrent { // RESOURCE MANAGEMENT // flags are defined in storage.hpp - void move_storage(std::string const& save_path, move_flags_t flags); + void move_storage(std::string const& save_path, move_flags_t flags, std::shared_ptr> promise = nullptr); // renames the file with the given index to the new name // the name may include a directory path @@ -1308,7 +1308,7 @@ namespace libtorrent { void on_files_deleted(storage_error const& error); void on_torrent_paused(); void on_storage_moved(status_t status, std::string const& path - , storage_error const& error); + , storage_error const& error, std::shared_ptr> promise = nullptr); void on_file_renamed(std::string const& filename , file_index_t file_idx , storage_error const& error); diff --git a/include/libtorrent/torrent_handle.hpp b/include/libtorrent/torrent_handle.hpp index 910c2919219..a4e16ad3f5b 100644 --- a/include/libtorrent/torrent_handle.hpp +++ b/include/libtorrent/torrent_handle.hpp @@ -84,6 +84,7 @@ namespace aux { struct cache_flushed_alert; struct read_piece_alert; + struct storage_moved_alert; #ifndef BOOST_NO_EXCEPTIONS [[noreturn]] void throw_invalid_handle(); @@ -1335,6 +1336,9 @@ namespace aux { , move_flags_t flags = move_flags_t::always_replace_files ) const; + std::future async_move_storage(std::string const& save_path + , move_flags_t flags = move_flags_t::always_replace_files); + #if TORRENT_ABI_VERSION == 1 // deprecated in 1.2 TORRENT_DEPRECATED diff --git a/src/torrent.cpp b/src/torrent.cpp index 3f0502f8b4a..81a06643011 100644 --- a/src/torrent.cpp +++ b/src/torrent.cpp @@ -8690,17 +8690,22 @@ namespace { m_ses.deferred_submit_jobs(); } - void torrent::move_storage(std::string const& save_path, move_flags_t const flags) + void torrent::move_storage(std::string const& save_path, move_flags_t const flags, std::shared_ptr> promise) { TORRENT_ASSERT(is_single_thread()); INVARIANT_CHECK; if (m_abort) { - if (alerts().should_post()) - alerts().emplace_alert(get_handle() + if (alerts().should_post()) { + auto* a = alerts().emplace_alert(get_handle() , boost::asio::error::operation_aborted , "", operation_t::unknown); + + if (promise) promise->set_exception(std::make_exception_ptr(alert_exception(a))); + } else { + if (promise) promise->set_exception(std::make_exception_ptr(alert_dont_post_exception())); + } return; } @@ -8708,8 +8713,12 @@ namespace { // structure and we have to assume we don't have any file. if (!valid_metadata()) { - if (alerts().should_post()) - alerts().emplace_alert(get_handle(), save_path, m_save_path); + if (alerts().should_post()) { + auto* a = alerts().emplace_alert(get_handle(), save_path, m_save_path); + if (promise) promise->set_value(a); + } else { + if (promise) promise->set_exception(std::make_exception_ptr(alert_dont_post_exception())); + } #if TORRENT_USE_UNC_PATHS std::string path = canonicalize_path(save_path); #else @@ -8728,14 +8737,18 @@ namespace { std::string path = save_path; #endif m_ses.disk_thread().async_move_storage(m_storage, std::move(path), flags - , std::bind(&torrent::on_storage_moved, shared_from_this(), _1, _2, _3)); + , std::bind(&torrent::on_storage_moved, shared_from_this(), _1, _2, _3, promise)); m_moving_storage = true; m_ses.deferred_submit_jobs(); } else { - if (alerts().should_post()) - alerts().emplace_alert(get_handle(), save_path, m_save_path); + if (alerts().should_post()) { + auto* a = alerts().emplace_alert(get_handle(), save_path, m_save_path); + if (promise) promise->set_value(a); + } else { + if (promise) promise->set_exception(std::make_exception_ptr(alert_dont_post_exception())); + } #if TORRENT_USE_UNC_PATHS m_save_path = canonicalize_path(save_path); @@ -8748,7 +8761,7 @@ namespace { } void torrent::on_storage_moved(status_t const status, std::string const& path - , storage_error const& error) try + , storage_error const& error, std::shared_ptr> promise) try { TORRENT_ASSERT(is_single_thread()); @@ -8756,8 +8769,12 @@ namespace { if (status == status_t::no_error || status == status_t::need_full_check) { - if (alerts().should_post()) - alerts().emplace_alert(get_handle(), path, m_save_path); + if (alerts().should_post()) { + auto* a = alerts().emplace_alert(get_handle(), path, m_save_path); + if (promise) promise->set_value(a); + } else { + if (promise) promise->set_exception(std::make_exception_ptr(alert_dont_post_exception())); + } m_save_path = path; set_need_save_resume(torrent_handle::if_config_changed); if (status == status_t::need_full_check) @@ -8765,9 +8782,13 @@ namespace { } else { - if (alerts().should_post()) - alerts().emplace_alert(get_handle(), error.ec + if (alerts().should_post()) { + auto* a = alerts().emplace_alert(get_handle(), error.ec , resolve_filename(error.file()), error.operation); + if (promise) promise->set_exception(std::make_exception_ptr(alert_exception(a))); + } else { + if (promise) promise->set_exception(std::make_exception_ptr(alert_dont_post_exception())); + } } } catch (...) { handle_exception(); } diff --git a/src/torrent_handle.cpp b/src/torrent_handle.cpp index ae4f5bd3a43..257ca2703c3 100644 --- a/src/torrent_handle.cpp +++ b/src/torrent_handle.cpp @@ -270,14 +270,22 @@ namespace libtorrent { void torrent_handle::move_storage(std::string const& save_path, move_flags_t flags) const { - async_call(&torrent::move_storage, save_path, flags); + async_call(&torrent::move_storage, save_path, flags, nullptr); + } + + std::future torrent_handle::async_move_storage(std::string const& save_path + , move_flags_t flags) + { + auto promise = std::make_shared>(); + async_call(&torrent::move_storage, save_path, flags, promise); + return promise->get_future(); } #if TORRENT_ABI_VERSION == 1 void torrent_handle::move_storage( std::string const& save_path, int const flags) const { - async_call(&torrent::move_storage, save_path, static_cast(flags)); + async_call(&torrent::move_storage, save_path, static_cast(flags), nullptr); } #endif // TORRENT_ABI_VERSION