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 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&) { diff --git a/include/libtorrent/torrent.hpp b/include/libtorrent/torrent.hpp index ecca52f34ea..63f4a9558be 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 @@ -485,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); @@ -563,7 +567,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(); @@ -1160,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 @@ -1303,11 +1308,12 @@ 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); - 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..a4e16ad3f5b 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,10 @@ namespace aux { struct torrent; struct client_data_t; + struct cache_flushed_alert; + struct read_piece_alert; + struct storage_moved_alert; + #ifndef BOOST_NO_EXCEPTIONS [[noreturn]] void throw_invalid_handle(); #endif @@ -310,6 +315,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 +665,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 @@ -1329,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 43e2cddea18..81a06643011 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); } } } @@ -8397,7 +8403,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(); } @@ -8684,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; } @@ -8702,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 @@ -8722,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); @@ -8742,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()); @@ -8750,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) @@ -8759,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(); } @@ -9462,7 +9489,7 @@ namespace { && !m_session_paused; } - void torrent::flush_cache() + void torrent::flush_cache(std::shared_ptr> promise) { TORRENT_ASSERT(is_single_thread()); @@ -9473,18 +9500,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..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 @@ -358,7 +366,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( @@ -744,7 +759,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