diff --git a/glade/preferences_dialog.glade b/glade/preferences_dialog.glade index fc8815df5..c80dee3cc 100644 --- a/glade/preferences_dialog.glade +++ b/glade/preferences_dialog.glade @@ -315,108 +315,24 @@ 4 2 - + True - True - The maximum upload rate for all torrents. Set -1 for unlimited. - 1 - -1 -1 9000 1 10 10 - 1 + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 10 + + + True + 0 + Maximum Upload Rate (KB/s): + + - 1 - 2 3 4 GTK_FILL - - - True - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - The maximum download rate for all torrents. Set -1 for unlimited. - 1 - -1 -1 9000 1 10 10 - 1 - - - 1 - 2 - 2 - 3 - GTK_FILL - - - - - True - True - The maximum number of upload slots. Set -1 for unlimited. - 1 - -1 -1 1000 1 10 10 - 1 - - - 1 - 2 - 1 - 2 - GTK_FILL - - - - - True - True - The maximum number of connections allowed. Set -1 for unlimited. - 1 - -1 -1 1000 1 10 10 - 1 - - - 1 - 2 - GTK_FILL - - - - - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - 10 - - - True - 0 - Maximum Connections: - - - - - GTK_FILL - - - - - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - 10 - - - True - 0 - Upload Slots: - - - - - 1 - 2 - GTK_FILL - - True @@ -437,19 +353,103 @@ - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 10 - + True 0 - Maximum Upload Rate (KB/s): + Upload Slots: + 1 + 2 + GTK_FILL + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 10 + + + True + 0 + Maximum Connections: + + + + + GTK_FILL + + + + + True + True + The maximum number of connections allowed. Set -1 for unlimited. + 1 + -1 -1 1000 1 10 10 + 1 + + + 1 + 2 + GTK_FILL + + + + + True + True + The maximum number of upload slots. Set -1 for unlimited. + 1 + -1 -1 1000 1 10 10 + 1 + + + 1 + 2 + 1 + 2 + GTK_FILL + + + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + The maximum download rate for all torrents. Set -1 for unlimited. + 1 + -1 -1 9000 1 10 10 + 1 + + + 1 + 2 + 2 + 3 + GTK_FILL + + + + + True + True + The maximum upload rate for all torrents. Set -1 for unlimited. + 1 + -1 -1 9000 1 10 10 + 1 + + + 1 + 2 3 4 GTK_FILL @@ -758,7 +758,7 @@ True - False + True UPnP True 0 @@ -772,7 +772,7 @@ True - False + True NAT-PMP True 0 @@ -787,7 +787,7 @@ True - False + True UT PeX True 0 @@ -806,7 +806,7 @@ True - <b>Network Extras</b> - Always on + <b>Network Extras</b> True diff --git a/libtorrent/include/libtorrent/alert_types.hpp b/libtorrent/include/libtorrent/alert_types.hpp index e3e23ad05..7b32d2502 100755 --- a/libtorrent/include/libtorrent/alert_types.hpp +++ b/libtorrent/include/libtorrent/alert_types.hpp @@ -81,8 +81,6 @@ namespace libtorrent { return std::auto_ptr(new tracker_warning_alert(*this)); } }; - - struct TORRENT_EXPORT tracker_reply_alert: torrent_alert { tracker_reply_alert(torrent_handle const& h @@ -186,6 +184,26 @@ namespace libtorrent { return std::auto_ptr(new torrent_finished_alert(*this)); } }; + struct TORRENT_EXPORT storage_moved_alert: torrent_alert + { + storage_moved_alert(torrent_handle const& h, std::string const& path) + : torrent_alert(h, alert::warning, path) + {} + + virtual std::auto_ptr clone() const + { return std::auto_ptr(new storage_moved_alert(*this)); } + }; + + struct TORRENT_EXPORT torrent_paused_alert: torrent_alert + { + torrent_paused_alert(torrent_handle const& h, std::string const& msg) + : torrent_alert(h, alert::warning, msg) + {} + + virtual std::auto_ptr clone() const + { return std::auto_ptr(new torrent_paused_alert(*this)); } + }; + struct TORRENT_EXPORT url_seed_alert: torrent_alert { url_seed_alert( diff --git a/libtorrent/include/libtorrent/asio.hpp b/libtorrent/include/libtorrent/asio.hpp index dfad4a6d5..ae6455bdb 100644 --- a/libtorrent/include/libtorrent/asio.hpp +++ b/libtorrent/include/libtorrent/asio.hpp @@ -68,6 +68,7 @@ #include "asio/system_error.hpp" #include "asio/thread.hpp" #include "asio/time_traits.hpp" +#include "asio/version.hpp" #include "asio/write.hpp" #endif // ASIO_HPP diff --git a/libtorrent/include/libtorrent/asio/basic_deadline_timer.hpp b/libtorrent/include/libtorrent/asio/basic_deadline_timer.hpp index a630c67bc..d881f6643 100644 --- a/libtorrent/include/libtorrent/asio/basic_deadline_timer.hpp +++ b/libtorrent/include/libtorrent/asio/basic_deadline_timer.hpp @@ -40,8 +40,6 @@ namespace asio { * @e Distinct @e objects: Safe.@n * @e Shared @e objects: Unsafe. * - * @sa @ref deadline_timer_reset - * * @par Examples * Performing a blocking wait: * @code @@ -75,6 +73,45 @@ namespace asio { * // Start an asynchronous wait. * timer.async_wait(handler); * @endcode + * + * @par Changing an active deadline_timer's expiry time + * + * Changing the expiry time of a timer while there are pending asynchronous + * waits causes those wait operations to be cancelled. To ensure that the action + * associated with the timer is performed only once, use something like this: + * used: + * + * @code + * void on_some_event() + * { + * if (my_timer.expires_from_now(seconds(5)) > 0) + * { + * // We managed to cancel the timer. Start new asynchronous wait. + * my_timer.async_wait(on_timeout); + * } + * else + * { + * // Too late, timer has already expired! + * } + * } + * + * void on_timeout(const asio::error_code& e) + * { + * if (e != asio::error::operation_aborted) + * { + * // Timer was not cancelled, take necessary action. + * } + * } + * @endcode + * + * @li The asio::basic_deadline_timer::expires_from_now() function + * cancels any pending asynchronous waits, and returns the number of + * asynchronous waits that were cancelled. If it returns 0 then you were too + * late and the wait handler has already been executed, or will soon be + * executed. If it returns 1 then the wait handler was successfully cancelled. + * + * @li If a wait handler is cancelled, the asio::error_code passed to + * it contains the value asio::error::operation_aborted. */ template , @@ -197,9 +234,6 @@ public: * operations will be cancelled. The handler for each cancelled operation will * be invoked with the asio::error::operation_aborted error code. * - * See @ref deadline_timer_reset for more information on altering the expiry - * time of an active timer. - * * @param expiry_time The expiry time to be used for the timer. * * @return The number of asynchronous operations that were cancelled. @@ -221,9 +255,6 @@ public: * operations will be cancelled. The handler for each cancelled operation will * be invoked with the asio::error::operation_aborted error code. * - * See @ref deadline_timer_reset for more information on altering the expiry - * time of an active timer. - * * @param expiry_time The expiry time to be used for the timer. * * @param ec Set to indicate what error occurred, if any. @@ -252,9 +283,6 @@ public: * operations will be cancelled. The handler for each cancelled operation will * be invoked with the asio::error::operation_aborted error code. * - * See @ref deadline_timer_reset for more information on altering the expiry - * time of an active timer. - * * @param expiry_time The expiry time to be used for the timer. * * @return The number of asynchronous operations that were cancelled. @@ -276,9 +304,6 @@ public: * operations will be cancelled. The handler for each cancelled operation will * be invoked with the asio::error::operation_aborted error code. * - * See @ref deadline_timer_reset for more information on altering the expiry - * time of an active timer. - * * @param expiry_time The expiry time to be used for the timer. * * @param ec Set to indicate what error occurred, if any. @@ -349,49 +374,6 @@ public: } }; -/** - * @page deadline_timer_reset Changing an active deadline_timer's expiry time - * - * Changing the expiry time of a timer while there are pending asynchronous - * waits causes those wait operations to be cancelled. To ensure that the action - * associated with the timer is performed only once, use something like this: - * used: - * - * @code - * void on_some_event() - * { - * if (my_timer.expires_from_now(seconds(5)) > 0) - * { - * // We managed to cancel the timer. Start new asynchronous wait. - * my_timer.async_wait(on_timeout); - * } - * else - * { - * // Too late, timer has already expired! - * } - * } - * - * void on_timeout(const asio::error_code& e) - * { - * if (e != asio::error::operation_aborted) - * { - * // Timer was not cancelled, take necessary action. - * } - * } - * @endcode - * - * @li The asio::basic_deadline_timer::expires_from_now() function - * cancels any pending asynchronous waits, and returns the number of - * asynchronous waits that were cancelled. If it returns 0 then you were too - * late and the wait handler has already been executed, or will soon be - * executed. If it returns 1 then the wait handler was successfully cancelled. - * - * @li If a wait handler is cancelled, the asio::error_code passed to - * it contains the value asio::error::operation_aborted. - * - * @sa asio::basic_deadline_timer - */ - } // namespace asio #include "asio/detail/pop_options.hpp" diff --git a/libtorrent/include/libtorrent/asio/buffer.hpp b/libtorrent/include/libtorrent/asio/buffer.hpp index 69214fdb1..384e8d70a 100644 --- a/libtorrent/include/libtorrent/asio/buffer.hpp +++ b/libtorrent/include/libtorrent/asio/buffer.hpp @@ -34,6 +34,14 @@ # endif // defined(_HAS_ITERATOR_DEBUGGING) #endif // defined(BOOST_MSVC) +#if defined(__GNUC__) +# if defined(_GLIBCXX_DEBUG) +# if !defined(ASIO_DISABLE_BUFFER_DEBUGGING) +# define ASIO_ENABLE_BUFFER_DEBUGGING +# endif // !defined(ASIO_DISABLE_BUFFER_DEBUGGING) +# endif // defined(_GLIBCXX_DEBUG) +#endif // defined(__GNUC__) + #if defined(ASIO_ENABLE_BUFFER_DEBUGGING) # include "asio/detail/push_options.hpp" # include diff --git a/libtorrent/include/libtorrent/asio/detail/epoll_reactor_fwd.hpp b/libtorrent/include/libtorrent/asio/detail/epoll_reactor_fwd.hpp index 720d7ca71..87fad6325 100644 --- a/libtorrent/include/libtorrent/asio/detail/epoll_reactor_fwd.hpp +++ b/libtorrent/include/libtorrent/asio/detail/epoll_reactor_fwd.hpp @@ -24,6 +24,8 @@ #include #include "asio/detail/pop_options.hpp" +#if LINUX_VERSION_CODE >= KERNEL_VERSION (2,5,45) // Only kernels >= 2.5.45. + // Define this to indicate that epoll is supported on the target platform. #define ASIO_HAS_EPOLL 1 @@ -36,6 +38,7 @@ class epoll_reactor; } // namespace detail } // namespace asio +#endif // LINUX_VERSION_CODE >= KERNEL_VERSION (2,5,45) #endif // defined(__linux__) #endif // !defined(ASIO_DISABLE_EPOLL) diff --git a/libtorrent/include/libtorrent/asio/detail/old_win_sdk_compat.hpp b/libtorrent/include/libtorrent/asio/detail/old_win_sdk_compat.hpp index da78f956d..2fb957aa9 100644 --- a/libtorrent/include/libtorrent/asio/detail/old_win_sdk_compat.hpp +++ b/libtorrent/include/libtorrent/asio/detail/old_win_sdk_compat.hpp @@ -164,6 +164,10 @@ struct addrinfo_emulation # define IPPROTO_IPV6 41 #endif +#if !defined(IPV6_UNICAST_HOPS) +# define IPV6_UNICAST_HOPS 4 +#endif + #if !defined(IPV6_MULTICAST_IF) # define IPV6_MULTICAST_IF 9 #endif diff --git a/libtorrent/include/libtorrent/asio/detail/reactive_socket_service.hpp b/libtorrent/include/libtorrent/asio/detail/reactive_socket_service.hpp index b58f62781..d5b8e4fc8 100644 --- a/libtorrent/include/libtorrent/asio/detail/reactive_socket_service.hpp +++ b/libtorrent/include/libtorrent/asio/detail/reactive_socket_service.hpp @@ -350,6 +350,22 @@ public: socket_ops::setsockopt(impl.socket_, option.level(impl.protocol_), option.name(impl.protocol_), option.data(impl.protocol_), option.size(impl.protocol_), ec); + +#if defined(__MACH__) && defined(__APPLE__) \ +|| defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__) + // To implement portable behaviour for SO_REUSEADDR with UDP sockets we + // need to also set SO_REUSEPORT on BSD-based platforms. + if (!ec && impl.protocol_.type() == SOCK_DGRAM + && option.level(impl.protocol_) == SOL_SOCKET + && option.name(impl.protocol_) == SO_REUSEADDR) + { + asio::error_code ignored_ec; + socket_ops::setsockopt(impl.socket_, SOL_SOCKET, SO_REUSEPORT, + option.data(impl.protocol_), option.size(impl.protocol_), + ignored_ec); + } +#endif + return ec; } } @@ -506,6 +522,18 @@ public: return 0; } + // Make socket non-blocking if user wants non-blocking. + if (impl.flags_ & implementation_type::user_set_non_blocking) + { + if (!(impl.flags_ & implementation_type::internal_non_blocking)) + { + ioctl_arg_type non_blocking = 1; + if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec)) + return 0; + impl.flags_ |= implementation_type::internal_non_blocking; + } + } + // Send the data. for (;;) { @@ -668,6 +696,18 @@ public: asio::buffer_size(buffer)); } + // Make socket non-blocking if user wants non-blocking. + if (impl.flags_ & implementation_type::user_set_non_blocking) + { + if (!(impl.flags_ & implementation_type::internal_non_blocking)) + { + ioctl_arg_type non_blocking = 1; + if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec)) + return 0; + impl.flags_ |= implementation_type::internal_non_blocking; + } + } + // Send the data. for (;;) { @@ -823,6 +863,18 @@ public: return 0; } + // Make socket non-blocking if user wants non-blocking. + if (impl.flags_ & implementation_type::user_set_non_blocking) + { + if (!(impl.flags_ & implementation_type::internal_non_blocking)) + { + ioctl_arg_type non_blocking = 1; + if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec)) + return 0; + impl.flags_ |= implementation_type::internal_non_blocking; + } + } + // Receive some data. for (;;) { @@ -1005,6 +1057,18 @@ public: asio::buffer_size(buffer)); } + // Make socket non-blocking if user wants non-blocking. + if (impl.flags_ & implementation_type::user_set_non_blocking) + { + if (!(impl.flags_ & implementation_type::internal_non_blocking)) + { + ioctl_arg_type non_blocking = 1; + if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec)) + return 0; + impl.flags_ |= implementation_type::internal_non_blocking; + } + } + // Receive some data. for (;;) { @@ -1160,6 +1224,18 @@ public: return ec; } + // Make socket non-blocking if user wants non-blocking. + if (impl.flags_ & implementation_type::user_set_non_blocking) + { + if (!(impl.flags_ & implementation_type::internal_non_blocking)) + { + ioctl_arg_type non_blocking = 1; + if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec)) + return ec; + impl.flags_ |= implementation_type::internal_non_blocking; + } + } + // Accept a socket. for (;;) { @@ -1203,6 +1279,14 @@ public: return ec; // Fall through to retry operation. } +#if defined(EPROTO) + else if (ec.value() == EPROTO) + { + if (impl.flags_ & implementation_type::enable_connection_aborted) + return ec; + // Fall through to retry operation. + } +#endif // defined(EPROTO) else return ec; @@ -1262,6 +1346,10 @@ public: if (ec == asio::error::connection_aborted && !enable_connection_aborted_) return false; +#if defined(EPROTO) + if (ec.value() == EPROTO && !enable_connection_aborted_) + return false; +#endif // defined(EPROTO) // Transfer ownership of the new socket to the peer object. if (!ec) diff --git a/libtorrent/include/libtorrent/asio/detail/socket_ops.hpp b/libtorrent/include/libtorrent/asio/detail/socket_ops.hpp index 166319bb1..4b38c6ee8 100644 --- a/libtorrent/include/libtorrent/asio/detail/socket_ops.hpp +++ b/libtorrent/include/libtorrent/asio/detail/socket_ops.hpp @@ -63,7 +63,7 @@ inline socket_type accept(socket_type s, socket_addr_type* addr, socket_addr_len_type* addrlen, asio::error_code& ec) { clear_error(ec); -#if defined(__MACH__) && defined(__APPLE__) +#if defined(__MACH__) && defined(__APPLE__) || defined(__FreeBSD__) socket_type new_s = error_wrapper(::accept(s, addr, addrlen), ec); if (new_s == invalid_socket) return new_s; @@ -295,7 +295,7 @@ inline socket_type socket(int af, int type, int protocol, } return s; -#elif defined(__MACH__) && defined(__APPLE__) +#elif defined(__MACH__) && defined(__APPLE__) || defined(__FreeBSD__) socket_type s = error_wrapper(::socket(af, type, protocol), ec); if (s == invalid_socket) return s; @@ -318,11 +318,35 @@ inline socket_type socket(int af, int type, int protocol, inline int setsockopt(socket_type s, int level, int optname, const void* optval, size_t optlen, asio::error_code& ec) { + if (level == custom_socket_option_level && optname == always_fail_option) + { + ec = asio::error::invalid_argument; + return -1; + } + +#if defined(__BORLANDC__) + // Mysteriously, using the getsockopt and setsockopt functions directly with + // Borland C++ results in incorrect values being set and read. The bug can be + // worked around by using function addresses resolved with GetProcAddress. + if (HMODULE winsock_module = ::GetModuleHandleA("ws2_32")) + { + typedef int (WSAAPI *sso_t)(SOCKET, int, int, const char*, int); + if (sso_t sso = (sso_t)::GetProcAddress(winsock_module, "setsockopt")) + { + clear_error(ec); + return error_wrapper(sso(s, level, optname, + reinterpret_cast(optval), + static_cast(optlen)), ec); + } + } + ec = asio::error::fault; + return -1; +#elif defined(BOOST_WINDOWS) || defined(__CYGWIN__) clear_error(ec); -#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) return error_wrapper(::setsockopt(s, level, optname, reinterpret_cast(optval), static_cast(optlen)), ec); #else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + clear_error(ec); return error_wrapper(::setsockopt(s, level, optname, optval, static_cast(optlen)), ec); #endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) @@ -331,8 +355,44 @@ inline int setsockopt(socket_type s, int level, int optname, inline int getsockopt(socket_type s, int level, int optname, void* optval, size_t* optlen, asio::error_code& ec) { + if (level == custom_socket_option_level && optname == always_fail_option) + { + ec = asio::error::invalid_argument; + return -1; + } + +#if defined(__BORLANDC__) + // Mysteriously, using the getsockopt and setsockopt functions directly with + // Borland C++ results in incorrect values being set and read. The bug can be + // worked around by using function addresses resolved with GetProcAddress. + if (HMODULE winsock_module = ::GetModuleHandleA("ws2_32")) + { + typedef int (WSAAPI *gso_t)(SOCKET, int, int, char*, int*); + if (gso_t gso = (gso_t)::GetProcAddress(winsock_module, "getsockopt")) + { + clear_error(ec); + int tmp_optlen = static_cast(*optlen); + int result = error_wrapper(gso(s, level, optname, + reinterpret_cast(optval), &tmp_optlen), ec); + *optlen = static_cast(tmp_optlen); + if (result != 0 && level == IPPROTO_IPV6 && optname == IPV6_V6ONLY + && ec.value() == WSAENOPROTOOPT && *optlen == sizeof(DWORD)) + { + // Dual-stack IPv4/v6 sockets, and the IPV6_V6ONLY socket option, are + // only supported on Windows Vista and later. To simplify program logic + // we will fake success of getting this option and specify that the + // value is non-zero (i.e. true). This corresponds to the behavior of + // IPv6 sockets on Windows platforms pre-Vista. + *static_cast(optval) = 1; + clear_error(ec); + } + return result; + } + } + ec = asio::error::fault; + return -1; +#elif defined(BOOST_WINDOWS) || defined(__CYGWIN__) clear_error(ec); -#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) int tmp_optlen = static_cast(*optlen); int result = error_wrapper(::getsockopt(s, level, optname, reinterpret_cast(optval), &tmp_optlen), ec); @@ -350,6 +410,7 @@ inline int getsockopt(socket_type s, int level, int optname, void* optval, } return result; #else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + clear_error(ec); socklen_t tmp_optlen = static_cast(*optlen); int result = error_wrapper(::getsockopt(s, level, optname, optval, &tmp_optlen), ec); diff --git a/libtorrent/include/libtorrent/asio/detail/socket_select_interrupter.hpp b/libtorrent/include/libtorrent/asio/detail/socket_select_interrupter.hpp index 6117d309d..ba62b12d6 100644 --- a/libtorrent/include/libtorrent/asio/detail/socket_select_interrupter.hpp +++ b/libtorrent/include/libtorrent/asio/detail/socket_select_interrupter.hpp @@ -18,6 +18,7 @@ #include "asio/detail/push_options.hpp" #include "asio/detail/push_options.hpp" +#include #include #include "asio/detail/pop_options.hpp" @@ -49,8 +50,10 @@ public: socket_ops::setsockopt(acceptor.get(), SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt), ec); + using namespace std; // For memset. sockaddr_in4_type addr; socket_addr_len_type addr_len = sizeof(addr); + memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = inet_addr("127.0.0.1"); addr.sin_port = 0; diff --git a/libtorrent/include/libtorrent/asio/detail/socket_types.hpp b/libtorrent/include/libtorrent/asio/detail/socket_types.hpp index b2a03f2f1..49d1c7fc2 100644 --- a/libtorrent/include/libtorrent/asio/detail/socket_types.hpp +++ b/libtorrent/include/libtorrent/asio/detail/socket_types.hpp @@ -169,6 +169,7 @@ const int message_do_not_route = MSG_DONTROUTE; #endif const int custom_socket_option_level = 0xA5100000; const int enable_connection_aborted_option = 1; +const int always_fail_option = 2; } // namespace detail } // namespace asio diff --git a/libtorrent/include/libtorrent/asio/detail/timer_queue.hpp b/libtorrent/include/libtorrent/asio/detail/timer_queue.hpp index ab6eac263..af1e36bd5 100644 --- a/libtorrent/include/libtorrent/asio/detail/timer_queue.hpp +++ b/libtorrent/include/libtorrent/asio/detail/timer_queue.hpp @@ -309,7 +309,8 @@ private: swap_heap(index, heap_.size() - 1); heap_.pop_back(); size_t parent = (index - 1) / 2; - if (index > 0 && Time_Traits::less_than(t->time_, heap_[parent]->time_)) + if (index > 0 && Time_Traits::less_than( + heap_[index]->time_, heap_[parent]->time_)) up_heap(index); else down_heap(index); diff --git a/libtorrent/include/libtorrent/asio/detail/win_iocp_socket_service.hpp b/libtorrent/include/libtorrent/asio/detail/win_iocp_socket_service.hpp index 667553960..007286e8d 100644 --- a/libtorrent/include/libtorrent/asio/detail/win_iocp_socket_service.hpp +++ b/libtorrent/include/libtorrent/asio/detail/win_iocp_socket_service.hpp @@ -137,7 +137,8 @@ public: enum { enable_connection_aborted = 1, // User wants connection_aborted errors. - user_set_linger = 2 // The user set the linger option. + user_set_linger = 2, // The user set the linger option. + user_set_non_blocking = 4 // The user wants a non-blocking socket. }; // Flags indicating the current state of the socket. @@ -200,6 +201,7 @@ public: void construct(implementation_type& impl) { impl.socket_ = invalid_socket; + impl.flags_ = 0; impl.cancel_token_.reset(); impl.safe_cancellation_thread_id_ = 0; @@ -239,6 +241,7 @@ public: asio::error_code ignored_ec; socket_ops::close(impl.socket_, ignored_ec); impl.socket_ = invalid_socket; + impl.flags_ = 0; impl.cancel_token_.reset(); impl.safe_cancellation_thread_id_ = 0; } @@ -274,6 +277,7 @@ public: iocp_service_.register_handle(sock_as_handle); impl.socket_ = sock.release(); + impl.flags_ = 0; impl.cancel_token_.reset(static_cast(0), noop_deleter()); impl.protocol_ = protocol; ec = asio::error_code(); @@ -294,6 +298,7 @@ public: iocp_service_.register_handle(native_socket.as_handle()); impl.socket_ = native_socket; + impl.flags_ = 0; impl.cancel_token_.reset(static_cast(0), noop_deleter()); impl.protocol_ = protocol; ec = asio::error_code(); @@ -325,6 +330,7 @@ public: return ec; impl.socket_ = invalid_socket; + impl.flags_ = 0; impl.cancel_token_.reset(); impl.safe_cancellation_thread_id_ = 0; } @@ -534,6 +540,15 @@ public: socket_ops::ioctl(impl.socket_, command.name(), static_cast(command.data()), ec); + + if (!ec && command.name() == static_cast(FIONBIO)) + { + if (command.get()) + impl.flags_ |= implementation_type::user_set_non_blocking; + else + impl.flags_ &= ~implementation_type::user_set_non_blocking; + } + return ec; } @@ -1774,11 +1789,12 @@ public: class connect_handler { public: - connect_handler(socket_type socket, + connect_handler(socket_type socket, bool user_set_non_blocking, boost::shared_ptr completed, asio::io_service& io_service, reactor_type& reactor, Handler handler) : socket_(socket), + user_set_non_blocking_(user_set_non_blocking), completed_(completed), io_service_(io_service), reactor_(reactor), @@ -1825,12 +1841,15 @@ public: return true; } - // Make the socket blocking again (the default). - ioctl_arg_type non_blocking = 0; - if (socket_ops::ioctl(socket_, FIONBIO, &non_blocking, ec)) + // Revert socket to blocking mode unless the user requested otherwise. + if (!user_set_non_blocking_) { - io_service_.post(bind_handler(handler_, ec)); - return true; + ioctl_arg_type non_blocking = 0; + if (socket_ops::ioctl(socket_, FIONBIO, &non_blocking, ec)) + { + io_service_.post(bind_handler(handler_, ec)); + return true; + } } // Post the result of the successful connection operation. @@ -1841,6 +1860,7 @@ public: private: socket_type socket_; + bool user_set_non_blocking_; boost::shared_ptr completed_; asio::io_service& io_service_; reactor_type& reactor_; @@ -1891,6 +1911,13 @@ public: if (socket_ops::connect(impl.socket_, peer_endpoint.data(), peer_endpoint.size(), ec) == 0) { + // Revert socket to blocking mode unless the user requested otherwise. + if (!(impl.flags_ & implementation_type::user_set_non_blocking)) + { + non_blocking = 0; + socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec); + } + // The connect operation has finished successfully so we need to post the // handler immediately. this->io_service().post(bind_handler(handler, ec)); @@ -1903,10 +1930,20 @@ public: boost::shared_ptr completed(new bool(false)); reactor->start_write_and_except_ops(impl.socket_, connect_handler( - impl.socket_, completed, this->io_service(), *reactor, handler)); + impl.socket_, + (impl.flags_ & implementation_type::user_set_non_blocking) != 0, + completed, this->io_service(), *reactor, handler)); } else { + // Revert socket to blocking mode unless the user requested otherwise. + if (!(impl.flags_ & implementation_type::user_set_non_blocking)) + { + non_blocking = 0; + asio::error_code ignored_ec; + socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ignored_ec); + } + // The connect operation has failed, so post the handler immediately. this->io_service().post(bind_handler(handler, ec)); } diff --git a/libtorrent/include/libtorrent/asio/io_service.hpp b/libtorrent/include/libtorrent/asio/io_service.hpp index 731a7ac9a..b694545db 100644 --- a/libtorrent/include/libtorrent/asio/io_service.hpp +++ b/libtorrent/include/libtorrent/asio/io_service.hpp @@ -60,7 +60,40 @@ namespace asio { * @par Concepts: * Dispatcher. * - * @sa @ref io_service_handler_exception + * @par Effect of exceptions thrown from handlers + * + * If an exception is thrown from a handler, the exception is allowed to + * propagate through the throwing thread's invocation of + * asio::io_service::run(), asio::io_service::run_one(), + * asio::io_service::poll() or asio::io_service::poll_one(). + * No other threads that are calling any of these functions are affected. It is + * then the responsibility of the application to catch the exception. + * + * After the exception has been caught, the + * asio::io_service::run(), asio::io_service::run_one(), + * asio::io_service::poll() or asio::io_service::poll_one() + * call may be restarted @em without the need for an intervening call to + * asio::io_service::reset(). This allows the thread to rejoin the + * io_service's thread pool without impacting any other threads in the pool. + * + * For example: + * + * @code + * asio::io_service io_service; + * ... + * for (;;) + * { + * try + * { + * io_service.run(); + * break; // run() exited normally + * } + * catch (my_exception& e) + * { + * // Deal with exception as appropriate. + * } + * } + * @endcode */ class io_service : private noncopyable @@ -108,7 +141,9 @@ public: * more handlers to be dispatched, or until the io_service has been stopped. * * Multiple threads may call the run() function to set up a pool of threads - * from which the io_service may execute handlers. + * from which the io_service may execute handlers. All threads that are + * waiting in the pool are equivalent and the io_service may choose any one + * of them to invoke a handler. * * The run() function may be safely called again once it has completed only * after a call to reset(). @@ -125,7 +160,9 @@ public: * more handlers to be dispatched, or until the io_service has been stopped. * * Multiple threads may call the run() function to set up a pool of threads - * from which the io_service may execute handlers. + * from which the io_service may execute handlers. All threads that are + * waiting in the pool are equivalent and the io_service may choose any one + * of them to invoke a handler. * * The run() function may be safely called again once it has completed only * after a call to reset(). @@ -456,42 +493,6 @@ public: } }; -/** - * @page io_service_handler_exception Effect of exceptions thrown from handlers - * - * If an exception is thrown from a handler, the exception is allowed to - * propagate through the throwing thread's invocation of - * asio::io_service::run(), asio::io_service::run_one(), - * asio::io_service::poll() or asio::io_service::poll_one(). - * No other threads that are calling any of these functions are affected. It is - * then the responsibility of the application to catch the exception. - * - * After the exception has been caught, the - * asio::io_service::run(), asio::io_service::run_one(), - * asio::io_service::poll() or asio::io_service::poll_one() - * call may be restarted @em without the need for an intervening call to - * asio::io_service::reset(). This allows the thread to rejoin the - * io_service's thread pool without impacting any other threads in the pool. - * - * @par Example - * @code - * asio::io_service io_service; - * ... - * for (;;) - * { - * try - * { - * io_service.run(); - * break; // run() exited normally - * } - * catch (my_exception& e) - * { - * // Deal with exception as appropriate. - * } - * } - * @endcode - */ - } // namespace asio #include "asio/impl/io_service.ipp" diff --git a/libtorrent/include/libtorrent/asio/ip/basic_resolver_iterator.hpp b/libtorrent/include/libtorrent/asio/ip/basic_resolver_iterator.hpp index 9edec2b0a..686e4446e 100644 --- a/libtorrent/include/libtorrent/asio/ip/basic_resolver_iterator.hpp +++ b/libtorrent/include/libtorrent/asio/ip/basic_resolver_iterator.hpp @@ -19,6 +19,7 @@ #include "asio/detail/push_options.hpp" #include +#include #include #include #include @@ -118,12 +119,12 @@ private: void increment() { - if (++iter_ == values_->end()) + if (++*iter_ == values_->end()) { // Reset state to match a default constructed end iterator. values_.reset(); typedef typename values_type::const_iterator values_iterator_type; - iter_ = values_iterator_type(); + iter_.reset(); } } @@ -133,17 +134,18 @@ private: return true; if (values_ != other.values_) return false; - return iter_ == other.iter_; + return *iter_ == *other.iter_; } const basic_resolver_entry& dereference() const { - return *iter_; + return **iter_; } typedef std::vector > values_type; + typedef typename values_type::const_iterator values_iter_type; boost::shared_ptr values_; - typename values_type::const_iterator iter_; + boost::optional iter_; }; } // namespace ip diff --git a/libtorrent/include/libtorrent/asio/ip/detail/socket_option.hpp b/libtorrent/include/libtorrent/asio/ip/detail/socket_option.hpp index db7f8edc4..a86307077 100644 --- a/libtorrent/include/libtorrent/asio/ip/detail/socket_option.hpp +++ b/libtorrent/include/libtorrent/asio/ip/detail/socket_option.hpp @@ -37,6 +37,12 @@ template class boolean { public: +#if defined(__sun) + typedef unsigned char value_type; +#else + typedef int value_type; +#endif + // Default constructor. boolean() : value_(0) @@ -94,14 +100,14 @@ public: // Get the address of the boolean data. template - int* data(const Protocol&) + value_type* data(const Protocol&) { return &value_; } // Get the address of the boolean data. template - const int* data(const Protocol&) const + const value_type* data(const Protocol&) const { return &value_; } @@ -122,7 +128,7 @@ public: } private: - int value_; + value_type value_; }; // Helper template for implementing unicast hops options. diff --git a/libtorrent/include/libtorrent/asio/ip/tcp.hpp b/libtorrent/include/libtorrent/asio/ip/tcp.hpp index 447210caf..e31844c4d 100644 --- a/libtorrent/include/libtorrent/asio/ip/tcp.hpp +++ b/libtorrent/include/libtorrent/asio/ip/tcp.hpp @@ -59,7 +59,7 @@ public: return tcp(PF_INET); } - /// Construct to represent the IPv4 TCP protocol. + /// Construct to represent the IPv6 TCP protocol. static tcp v6() { return tcp(PF_INET6); diff --git a/libtorrent/include/libtorrent/asio/ip/udp.hpp b/libtorrent/include/libtorrent/asio/ip/udp.hpp index ed3bb8536..e434f74d9 100644 --- a/libtorrent/include/libtorrent/asio/ip/udp.hpp +++ b/libtorrent/include/libtorrent/asio/ip/udp.hpp @@ -56,7 +56,7 @@ public: return udp(PF_INET); } - /// Construct to represent the IPv4 UDP protocol. + /// Construct to represent the IPv6 UDP protocol. static udp v6() { return udp(PF_INET6); @@ -80,7 +80,7 @@ public: return family_; } - /// The IPv4 UDP socket type. + /// The UDP socket type. typedef basic_datagram_socket socket; /// The UDP resolver type. diff --git a/libtorrent/include/libtorrent/asio/strand.hpp b/libtorrent/include/libtorrent/asio/strand.hpp index 948921e10..d0869d95d 100644 --- a/libtorrent/include/libtorrent/asio/strand.hpp +++ b/libtorrent/include/libtorrent/asio/strand.hpp @@ -54,6 +54,12 @@ public: } /// Destructor. + /** + * Destroys a strand. + * + * Handlers posted through the strand that have not yet been invoked will + * still be dispatched in a way that meets the guarantee of non-concurrency. + */ ~strand() { service_.destroy(impl_); diff --git a/libtorrent/include/libtorrent/aux_/session_impl.hpp b/libtorrent/include/libtorrent/aux_/session_impl.hpp index fd24ef8f7..c270d9ffc 100644 --- a/libtorrent/include/libtorrent/aux_/session_impl.hpp +++ b/libtorrent/include/libtorrent/aux_/session_impl.hpp @@ -82,10 +82,13 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/lsd.hpp" #include "libtorrent/socket_type.hpp" #include "libtorrent/connection_queue.hpp" +#include "libtorrent/disk_io_thread.hpp" namespace libtorrent { + namespace fs = boost::filesystem; + namespace aux { struct session_impl; @@ -98,7 +101,7 @@ namespace libtorrent : processing(false), progress(0.f), abort(false) {} boost::shared_ptr torrent_ptr; - boost::filesystem::path save_path; + fs::path save_path; sha1_hash info_hash; @@ -229,6 +232,7 @@ namespace libtorrent bool is_aborted() const { return m_abort; } void set_ip_filter(ip_filter const& f); + void set_port_filter(port_filter const& f); bool listen_on( std::pair const& port_range @@ -237,7 +241,7 @@ namespace libtorrent torrent_handle add_torrent( torrent_info const& ti - , boost::filesystem::path const& save_path + , fs::path const& save_path , entry const& resume_data , bool compact_mode , int block_size @@ -247,7 +251,7 @@ namespace libtorrent char const* tracker_url , sha1_hash const& info_hash , char const* name - , boost::filesystem::path const& save_path + , fs::path const& save_path , entry const& resume_data , bool compact_mode , int block_size @@ -304,6 +308,14 @@ namespace libtorrent { return m_dht_proxy; } #endif + void start_lsd(); + void start_natpmp(); + void start_upnp(); + + void stop_lsd(); + void stop_natpmp(); + void stop_upnp(); + // handles delayed alerts alert_manager m_alerts; @@ -325,6 +337,9 @@ namespace libtorrent // when they are destructed. file_pool m_files; + // handles disk io requests asynchronously + disk_io_thread m_disk_thread; + // this is a list of half-open tcp connections // (only outgoing connections) // this has to be one of the last @@ -348,6 +363,9 @@ namespace libtorrent // filters incoming connections ip_filter m_ip_filter; + + // filters outgoing connections + port_filter m_port_filter; // the peer id that is generated at the start of the session peer_id m_peer_id; @@ -427,9 +445,9 @@ namespace libtorrent pe_settings m_pe_settings; #endif - natpmp m_natpmp; - upnp m_upnp; - lsd m_lsd; + boost::shared_ptr m_natpmp; + boost::shared_ptr m_upnp; + boost::shared_ptr m_lsd; // the timer used to fire the second_tick deadline_timer m_timer; diff --git a/libtorrent/include/libtorrent/bt_peer_connection.hpp b/libtorrent/include/libtorrent/bt_peer_connection.hpp index 2a5000011..0f5e58e9d 100755 --- a/libtorrent/include/libtorrent/bt_peer_connection.hpp +++ b/libtorrent/include/libtorrent/bt_peer_connection.hpp @@ -192,7 +192,7 @@ namespace libtorrent void write_cancel(peer_request const& r); void write_bitfield(std::vector const& bitfield); void write_have(int index); - void write_piece(peer_request const& r); + void write_piece(peer_request const& r, char const* buffer); void write_handshake(); #ifndef TORRENT_DISABLE_EXTENSIONS void write_extensions(); diff --git a/libtorrent/include/libtorrent/debug.hpp b/libtorrent/include/libtorrent/debug.hpp index 39c4b0222..436b695f6 100755 --- a/libtorrent/include/libtorrent/debug.hpp +++ b/libtorrent/include/libtorrent/debug.hpp @@ -53,15 +53,16 @@ POSSIBILITY OF SUCH DAMAGE. namespace libtorrent { // DEBUG API + + namespace fs = boost::filesystem; struct logger { - logger(boost::filesystem::path const& filename, int instance, bool append = true) + logger(fs::path const& filename, int instance, bool append = true) { - using namespace boost::filesystem; - path dir(complete("libtorrent_logs" + boost::lexical_cast(instance))); - if (!exists(dir)) create_directories(dir); - m_file.open(dir / filename, std::ios_base::out | (append ? std::ios_base::app : std::ios_base::out)); + fs::path dir(fs::complete("libtorrent_logs" + boost::lexical_cast(instance))); + if (!fs::exists(dir)) fs::create_directories(dir); + m_file.open((dir / filename).string().c_str(), std::ios_base::out | (append ? std::ios_base::app : std::ios_base::out)); *this << "\n\n\n*** starting log ***\n"; } @@ -73,7 +74,7 @@ namespace libtorrent return *this; } - boost::filesystem::ofstream m_file; + std::ofstream m_file; }; } diff --git a/libtorrent/include/libtorrent/entry.hpp b/libtorrent/include/libtorrent/entry.hpp index 31a78b972..a1eba5324 100755 --- a/libtorrent/include/libtorrent/entry.hpp +++ b/libtorrent/include/libtorrent/entry.hpp @@ -155,6 +155,8 @@ namespace libtorrent dictionary_type& dict(); const dictionary_type& dict() const; + void swap(entry& e); + // these functions requires that the entry // is a dictionary, otherwise they will throw entry& operator[](char const* key); diff --git a/libtorrent/include/libtorrent/file.hpp b/libtorrent/include/libtorrent/file.hpp index d11496b28..bd0d03539 100755 --- a/libtorrent/include/libtorrent/file.hpp +++ b/libtorrent/include/libtorrent/file.hpp @@ -52,6 +52,7 @@ POSSIBILITY OF SUCH DAMAGE. namespace libtorrent { + namespace fs = boost::filesystem; struct TORRENT_EXPORT file_error: std::runtime_error { @@ -105,10 +106,10 @@ namespace libtorrent static const open_mode out; file(); - file(boost::filesystem::path const& p, open_mode m); + file(fs::path const& p, open_mode m); ~file(); - void open(boost::filesystem::path const& p, open_mode m); + void open(fs::path const& p, open_mode m); void close(); void set_size(size_type size); diff --git a/libtorrent/include/libtorrent/hasher.hpp b/libtorrent/include/libtorrent/hasher.hpp index 3333edf07..990cbefd8 100755 --- a/libtorrent/include/libtorrent/hasher.hpp +++ b/libtorrent/include/libtorrent/hasher.hpp @@ -104,3 +104,4 @@ namespace libtorrent } #endif // TORRENT_HASHER_HPP_INCLUDED + diff --git a/libtorrent/include/libtorrent/http_stream.hpp b/libtorrent/include/libtorrent/http_stream.hpp index 2bd124b43..041b7c84f 100644 --- a/libtorrent/include/libtorrent/http_stream.hpp +++ b/libtorrent/include/libtorrent/http_stream.hpp @@ -1,36 +1,53 @@ -#include "libtorrent/io.hpp" -#include "libtorrent/socket.hpp" -#include -#include -#include -#include -#include +/* +Copyright (c) 2007, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_HTTP_STREAM_HPP_INCLUDED +#define TORRENT_HTTP_STREAM_HPP_INCLUDED + +#include "libtorrent/proxy_base.hpp" namespace libtorrent { -class http_stream : boost::noncopyable +class http_stream : public proxy_base { public: - typedef stream_socket::lowest_layer_type lowest_layer_type; - typedef stream_socket::endpoint_type endpoint_type; - typedef stream_socket::protocol_type protocol_type; - explicit http_stream(asio::io_service& io_service) - : m_sock(io_service) - , m_resolver(io_service) + : proxy_base(io_service) , m_no_connect(false) {} void set_no_connect(bool c) { m_no_connect = c; } - void set_proxy(std::string hostname, int port) - { - m_hostname = hostname; - m_port = port; - } - void set_username(std::string const& user , std::string const& password) { @@ -38,108 +55,6 @@ public: m_password = password; } - template - void async_read_some(Mutable_Buffers const& buffers, Handler const& handler) - { - m_sock.async_read_some(buffers, handler); - } - - template - std::size_t read_some(Mutable_Buffers const& buffers, asio::error_code& ec) - { - return m_sock.read_some(buffers, ec); - } - - template - std::size_t read_some(Mutable_Buffers const& buffers) - { - return m_sock.read_some(buffers); - } - - template - void io_control(IO_Control_Command& ioc) - { - m_sock.io_control(ioc); - } - - template - void io_control(IO_Control_Command& ioc, asio::error_code& ec) - { - m_sock.io_control(ioc, ec); - } - - template - void async_write_some(Const_Buffers const& buffers, Handler const& handler) - { - m_sock.async_write_some(buffers, handler); - } - - void bind(endpoint_type const& endpoint) - { - m_sock.bind(endpoint); - } - - template - void bind(endpoint_type const& endpoint, Error_Handler const& error_handler) - { - m_sock.bind(endpoint, error_handler); - } - - void open(protocol_type const& p) - { - m_sock.open(p); - } - - template - void open(protocol_type const& p, Error_Handler const& error_handler) - { - m_sock.open(p, error_handler); - } - - void close() - { - m_remote_endpoint = endpoint_type(); - m_sock.close(); - } - - template - void close(Error_Handler const& error_handler) - { - m_sock.close(error_handler); - } - - endpoint_type remote_endpoint() - { - return m_remote_endpoint; - } - - template - endpoint_type remote_endpoint(Error_Handler const& error_handler) - { - return m_remote_endpoint; - } - - endpoint_type local_endpoint() - { - return m_sock.local_endpoint(); - } - - template - endpoint_type local_endpoint(Error_Handler const& error_handler) - { - return m_sock.local_endpoint(error_handler); - } - - asio::io_service& io_service() - { - return m_sock.io_service(); - } - - lowest_layer_type& lowest_layer() - { - return m_sock.lowest_layer(); - } - typedef boost::function handler_type; template @@ -171,19 +86,12 @@ private: void handshake1(asio::error_code const& e, boost::shared_ptr h); void handshake2(asio::error_code const& e, boost::shared_ptr h); - stream_socket m_sock; - // the http proxy - std::string m_hostname; - int m_port; // send and receive buffer std::vector m_buffer; // proxy authentication std::string m_user; std::string m_password; - endpoint_type m_remote_endpoint; - - tcp::resolver m_resolver; // this is true if the connection is HTTP based and // want to talk directly to the proxy bool m_no_connect; @@ -191,3 +99,4 @@ private: } +#endif diff --git a/libtorrent/include/libtorrent/ip_filter.hpp b/libtorrent/include/libtorrent/ip_filter.hpp index 1c62c553b..8b1793c3a 100644 --- a/libtorrent/include/libtorrent/ip_filter.hpp +++ b/libtorrent/include/libtorrent/ip_filter.hpp @@ -71,20 +71,82 @@ struct ip_range namespace detail { + template + Addr zero() + { + typename Addr::bytes_type zero; + std::fill(zero.begin(), zero.end(), 0); + return Addr(zero); + } + + template<> + inline boost::uint16_t zero() { return 0; } + + template + Addr plus_one(Addr const& a) + { + typename Addr::bytes_type tmp(a.to_bytes()); + typedef typename Addr::bytes_type::reverse_iterator iter; + for (iter i = tmp.rbegin() + , end(tmp.rend()); i != end; ++i) + { + if (*i < (std::numeric_limits::max)()) + { + *i += 1; + break; + } + *i = 0; + } + return Addr(tmp); + } + + inline boost::uint16_t plus_one(boost::uint16_t val) { return val + 1; } + + template + Addr minus_one(Addr const& a) + { + typename Addr::bytes_type tmp(a.to_bytes()); + typedef typename Addr::bytes_type::reverse_iterator iter; + for (iter i = tmp.rbegin() + , end(tmp.rend()); i != end; ++i) + { + if (*i > 0) + { + *i -= 1; + break; + } + *i = (std::numeric_limits::max)(); + } + return Addr(tmp); + } + + inline boost::uint16_t minus_one(boost::uint16_t val) { return val - 1; } + + template + Addr max_addr() + { + typename Addr::bytes_type tmp; + std::fill(tmp.begin(), tmp.end() + , (std::numeric_limits::max)()); + return Addr(tmp); + } + + template<> + inline boost::uint16_t max_addr() + { return (std::numeric_limits::max)(); } + // this is the generic implementation of // a filter for a specific address type. // it works with IPv4 and IPv6 template - class TORRENT_EXPORT filter_impl + class filter_impl { public: filter_impl() { - typename Addr::bytes_type zero; - std::fill(zero.begin(), zero.end(), 0); // make the entire ip-range non-blocked - m_access_list.insert(range(Addr(zero), 0)); + m_access_list.insert(range(zero(), 0)); } void add_rule(Addr first, Addr last, int flags) @@ -134,7 +196,7 @@ namespace detail if ((j != m_access_list.end() && minus_one(j->start) != last) || (j == m_access_list.end() - && last != max_addr())) + && last != max_addr())) { assert(j == m_access_list.end() || last < minus_one(j->start)); if (last_access != flags) @@ -170,7 +232,7 @@ namespace detail ++i; if (i == end) - r.last = max_addr(); + r.last = max_addr(); else r.last = minus_one(i->start); @@ -181,48 +243,6 @@ namespace detail private: - Addr plus_one(Addr const& a) const - { - typename Addr::bytes_type tmp(a.to_bytes()); - typedef typename Addr::bytes_type::reverse_iterator iter; - for (iter i = tmp.rbegin() - , end(tmp.rend()); i != end; ++i) - { - if (*i < (std::numeric_limits::max)()) - { - *i += 1; - break; - } - *i = 0; - } - return Addr(tmp); - } - - Addr minus_one(Addr const& a) const - { - typename Addr::bytes_type tmp(a.to_bytes()); - typedef typename Addr::bytes_type::reverse_iterator iter; - for (iter i = tmp.rbegin() - , end(tmp.rend()); i != end; ++i) - { - if (*i > 0) - { - *i -= 1; - break; - } - *i = (std::numeric_limits::max)(); - } - return Addr(tmp); - } - - Addr max_addr() const - { - typename Addr::bytes_type tmp; - std::fill(tmp.begin(), tmp.end() - , (std::numeric_limits::max)()); - return Addr(tmp); - } - struct range { range(Addr addr, int access = 0): start(addr), access(access) {} @@ -270,6 +290,24 @@ private: detail::filter_impl m_filter6; }; +class TORRENT_EXPORT port_filter +{ +public: + + enum access_flags + { + blocked = 1 + }; + + void add_rule(boost::uint16_t first, boost::uint16_t last, int flags); + int access(boost::uint16_t port) const; + +private: + + detail::filter_impl m_filter; + +}; + } #endif diff --git a/libtorrent/include/libtorrent/peer_connection.hpp b/libtorrent/include/libtorrent/peer_connection.hpp index 9514874fb..b25503a9a 100755 --- a/libtorrent/include/libtorrent/peer_connection.hpp +++ b/libtorrent/include/libtorrent/peer_connection.hpp @@ -51,7 +51,6 @@ POSSIBILITY OF SUCH DAMAGE. #include #include #include -#include #ifdef _MSC_VER #pragma warning(pop) @@ -73,6 +72,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/bandwidth_manager.hpp" #include "libtorrent/policy.hpp" #include "libtorrent/socket_type.hpp" +#include "libtorrent/intrusive_ptr_base.hpp" // TODO: each time a block is 'taken over' // from another peer. That peer must be given @@ -88,20 +88,16 @@ namespace libtorrent struct session_impl; } - TORRENT_EXPORT void intrusive_ptr_add_ref(peer_connection const*); - TORRENT_EXPORT void intrusive_ptr_release(peer_connection const*); - struct TORRENT_EXPORT protocol_error: std::runtime_error { protocol_error(const std::string& msg): std::runtime_error(msg) {}; }; class TORRENT_EXPORT peer_connection - : public boost::noncopyable + : public intrusive_ptr_base + , public boost::noncopyable { friend class invariant_access; - friend TORRENT_EXPORT void intrusive_ptr_add_ref(peer_connection const*); - friend TORRENT_EXPORT void intrusive_ptr_release(peer_connection const*); public: enum channels @@ -378,7 +374,7 @@ namespace libtorrent virtual void write_cancel(peer_request const& r) = 0; virtual void write_have(int index) = 0; virtual void write_keepalive() = 0; - virtual void write_piece(peer_request const& r) = 0; + virtual void write_piece(peer_request const& r, char const* buffer) = 0; virtual void on_connected() = 0; virtual void on_tick() {} @@ -474,6 +470,9 @@ namespace libtorrent private: void fill_send_buffer(); + void on_disk_read_complete(int ret, disk_io_job const& j, peer_request r); + void on_disk_write_complete(int ret, disk_io_job const& j + , peer_request r, boost::shared_ptr t); // the timeout in seconds int m_timeout; @@ -503,6 +502,11 @@ namespace libtorrent // (m_current_send_buffer + 1) % 2 is the // buffer we're currently waiting for. int m_current_send_buffer; + + // the number of bytes we are currently reading + // from disk, that will be added to the send + // buffer as soon as they complete + int m_reading_bytes; // if the sending buffer doesn't finish in one send // operation, this is the position within that buffer @@ -660,9 +664,6 @@ namespace libtorrent // the left-over bandwidth (suitable for web seeds). bool m_non_prioritized; - // reference counter for intrusive_ptr - mutable boost::detail::atomic_count m_refs; - int m_upload_limit; int m_download_limit; @@ -681,6 +682,19 @@ namespace libtorrent // so that it can be removed from the queue // once the connection completes int m_connection_ticket; + + // bytes downloaded since last second + // timer timeout; used for determining + // approx download rate + int m_remote_bytes_dled; + + // approximate peer download rate + int m_remote_dl_rate; + + // a timestamp when the remote download rate + // was last updated + ptime m_remote_dl_update; + #ifndef NDEBUG public: bool m_in_constructor; diff --git a/libtorrent/include/libtorrent/peer_id.hpp b/libtorrent/include/libtorrent/peer_id.hpp index 23a5eb463..b66c1d4bc 100755 --- a/libtorrent/include/libtorrent/peer_id.hpp +++ b/libtorrent/include/libtorrent/peer_id.hpp @@ -58,6 +58,7 @@ namespace libtorrent big_number(std::string const& s) { + assert(s.size() >= 20); int sl = int(s.size()) < size ? int(s.size()) : size; std::memcpy(m_number, &s[0], sl); } diff --git a/libtorrent/include/libtorrent/peer_info.hpp b/libtorrent/include/libtorrent/peer_info.hpp index 0f398284b..f8ee2feb6 100755 --- a/libtorrent/include/libtorrent/peer_info.hpp +++ b/libtorrent/include/libtorrent/peer_info.hpp @@ -58,8 +58,8 @@ namespace libtorrent on_parole = 0x200, seed = 0x400 #ifndef TORRENT_DISABLE_ENCRYPTION - , rc4_encrypted = 0x200, - plaintext_encrypted = 0x400 + , rc4_encrypted = 0x800, + plaintext_encrypted = 0x1000 #endif }; @@ -141,6 +141,9 @@ namespace libtorrent web_seed = 1 }; int connection_type; + + // approximate peer download rate + int remote_dl_rate; }; } diff --git a/libtorrent/include/libtorrent/piece_picker.hpp b/libtorrent/include/libtorrent/piece_picker.hpp index 7b8612909..c52521d0a 100755 --- a/libtorrent/include/libtorrent/piece_picker.hpp +++ b/libtorrent/include/libtorrent/piece_picker.hpp @@ -90,14 +90,14 @@ namespace libtorrent struct block_info { - block_info(): num_downloads(0), requested(0), finished(0) {} + block_info(): num_downloads(0), state(state_none) {} // the peer this block was requested or // downloaded from tcp::endpoint peer; // the number of times this block has been downloaded unsigned num_downloads:14; - unsigned requested:1; - unsigned finished:1; + enum { state_none, state_requested, state_writing, state_finished }; + unsigned state:2; }; // the peers that are downloading this piece @@ -109,7 +109,7 @@ namespace libtorrent struct downloading_piece { - downloading_piece(): finished(0), requested(0) {} + downloading_piece(): finished(0), writing(0), requested(0) {} piece_state_t state; // the index of the piece @@ -118,20 +118,27 @@ namespace libtorrent // this is a pointer into the m_block_info // vector owned by the piece_picker block_info* info; - boost::uint16_t finished; - boost::uint16_t requested; + // the number of blocks in the finished state + boost::int16_t finished; + // the number of blocks in the writing state + boost::int16_t writing; + // the number of blocks in the requested state + boost::int16_t requested; }; piece_picker(int blocks_per_piece , int total_num_blocks); + void get_availability(std::vector& avail) const; + void set_sequenced_download_threshold(int sequenced_download_threshold); // the vector tells which pieces we already have // and which we don't have. void files_checked( - const std::vector& pieces - , const std::vector& unfinished); + std::vector const& pieces + , std::vector const& unfinished + , std::vector& verify_pieces); // increases the peer count for the given piece // (is used when a HAVE or BITFIELD message is received) @@ -188,12 +195,16 @@ namespace libtorrent // returns true if any client is currently downloading this // piece-block, or if it's queued for downloading by some client // or if it already has been successfully downloaded - bool is_downloading(piece_block block) const; + bool is_requested(piece_block block) const; + // returns true if the block has been downloaded + bool is_downloaded(piece_block block) const; + // returns true if the block has been downloaded and written to disk bool is_finished(piece_block block) const; // marks this piece-block as queued for downloading void mark_as_downloading(piece_block block, tcp::endpoint const& peer , piece_state_t s); + void mark_as_writing(piece_block block, tcp::endpoint const& peer); void mark_as_finished(piece_block block, tcp::endpoint const& peer); // if a piece had a hash-failure, it must be restored and diff --git a/libtorrent/include/libtorrent/policy.hpp b/libtorrent/include/libtorrent/policy.hpp index 9404aa095..fffc3bfa2 100755 --- a/libtorrent/include/libtorrent/policy.hpp +++ b/libtorrent/include/libtorrent/policy.hpp @@ -99,11 +99,11 @@ namespace libtorrent void piece_finished(int index, bool successfully_verified); - void block_finished(peer_connection& c, piece_block b); - // the peer choked us void choked(peer_connection& c); + int count_choked() const; + // the peer unchoked us void unchoked(peer_connection& c); diff --git a/libtorrent/include/libtorrent/proxy_base.hpp b/libtorrent/include/libtorrent/proxy_base.hpp index 4abca18c7..021802dd0 100644 --- a/libtorrent/include/libtorrent/proxy_base.hpp +++ b/libtorrent/include/libtorrent/proxy_base.hpp @@ -181,4 +181,3 @@ protected: #endif - diff --git a/libtorrent/include/libtorrent/session.hpp b/libtorrent/include/libtorrent/session.hpp index 98424690a..52ec62cdb 100755 --- a/libtorrent/include/libtorrent/session.hpp +++ b/libtorrent/include/libtorrent/session.hpp @@ -72,8 +72,11 @@ namespace libtorrent struct torrent_plugin; class torrent; class ip_filter; + class port_filter; class connection_queue; + namespace fs = boost::filesystem; + namespace aux { // workaround for microsofts @@ -135,7 +138,7 @@ namespace libtorrent // all torrent_handles must be destructed before the session is destructed! torrent_handle add_torrent( torrent_info const& ti - , boost::filesystem::path const& save_path + , fs::path const& save_path , entry const& resume_data = entry() , bool compact_mode = true , int block_size = 16 * 1024 @@ -144,7 +147,7 @@ namespace libtorrent // TODO: deprecated, this is for backwards compatibility only torrent_handle add_torrent( entry const& e - , boost::filesystem::path const& save_path + , fs::path const& save_path , entry const& resume_data = entry() , bool compact_mode = true , int block_size = 16 * 1024 @@ -158,7 +161,7 @@ namespace libtorrent char const* tracker_url , sha1_hash const& info_hash , char const* name - , boost::filesystem::path const& save_path + , fs::path const& save_path , entry const& resume_data = entry() , bool compact_mode = true , int block_size = 16 * 1024 @@ -187,8 +190,10 @@ namespace libtorrent #endif void set_ip_filter(ip_filter const& f); + void set_port_filter(port_filter const& f); void set_peer_id(peer_id const& pid); void set_key(int key); + peer_id id() const; bool is_listening() const; @@ -249,6 +254,16 @@ namespace libtorrent connection_queue& get_connection_queue(); + // starts/stops UPnP, NATPMP or LSD port mappers + // they are stopped by default + void start_lsd(); + void start_natpmp(); + void start_upnp(); + + void stop_lsd(); + void stop_natpmp(); + void stop_upnp(); + // Resource management used for global limits. resource_request m_ul_bandwidth_quota; resource_request m_dl_bandwidth_quota; diff --git a/libtorrent/include/libtorrent/session_settings.hpp b/libtorrent/include/libtorrent/session_settings.hpp index c41fca1b7..a8b2091f6 100644 --- a/libtorrent/include/libtorrent/session_settings.hpp +++ b/libtorrent/include/libtorrent/session_settings.hpp @@ -54,6 +54,8 @@ namespace libtorrent // a plain tcp socket is used, and // the other settings are ignored. none, + // socks4 server, requires username. + socks4, // the hostname and port settings are // used to connect to the proxy. No // username or password is sent. @@ -102,6 +104,8 @@ namespace libtorrent , connection_speed(20) , send_redundant_have(false) , lazy_bitfields(true) + , inactivity_timeout(600) + , unchoke_interval(20) #ifndef TORRENT_DISABLE_DHT , use_dht_as_fallback(true) #endif @@ -226,6 +230,18 @@ namespace libtorrent // from stopping people from seeding. bool lazy_bitfields; + // if a peer is uninteresting and uninterested for longer + // than this number of seconds, it will be disconnected. + // default is 10 minutes + int inactivity_timeout; + + // the number of seconds between chokes/unchokes + int unchoke_interval; + + // if this is set, this IP will be reported do the + // tracker in the ip= parameter. + address announce_ip; + #ifndef TORRENT_DISABLE_DHT // while this is true, the dht will note be used unless the // tracker is online diff --git a/libtorrent/include/libtorrent/socket_type.hpp b/libtorrent/include/libtorrent/socket_type.hpp index f81d12342..9ed8f9a4b 100644 --- a/libtorrent/include/libtorrent/socket_type.hpp +++ b/libtorrent/include/libtorrent/socket_type.hpp @@ -34,12 +34,17 @@ POSSIBILITY OF SUCH DAMAGE. #define TORRENT_SOCKET_TYPE #include "libtorrent/socks5_stream.hpp" +#include "libtorrent/socks4_stream.hpp" #include "libtorrent/http_stream.hpp" #include "libtorrent/variant_stream.hpp" namespace libtorrent { - typedef variant_stream socket_type; + typedef variant_stream< + stream_socket + , socks5_stream + , socks4_stream + , http_stream> socket_type; } #endif diff --git a/libtorrent/include/libtorrent/socks5_stream.hpp b/libtorrent/include/libtorrent/socks5_stream.hpp index 9e8a0d04b..8bfc74c59 100644 --- a/libtorrent/include/libtorrent/socks5_stream.hpp +++ b/libtorrent/include/libtorrent/socks5_stream.hpp @@ -1,33 +1,50 @@ -#include "libtorrent/io.hpp" -#include "libtorrent/socket.hpp" -#include -#include -#include -#include -#include +/* +Copyright (c) 2007, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_SOCKS5_STREAM_HPP_INCLUDED +#define TORRENT_SOCKS5_STREAM_HPP_INCLUDED + +#include "libtorrent/proxy_base.hpp" namespace libtorrent { -class socks5_stream : boost::noncopyable +class socks5_stream : public proxy_base { public: - typedef stream_socket::lowest_layer_type lowest_layer_type; - typedef stream_socket::endpoint_type endpoint_type; - typedef stream_socket::protocol_type protocol_type; - explicit socks5_stream(asio::io_service& io_service) - : m_sock(io_service) - , m_resolver(io_service) + : proxy_base(io_service) {} - void set_proxy(std::string hostname, int port) - { - m_hostname = hostname; - m_port = port; - } - void set_username(std::string const& user , std::string const& password) { @@ -35,108 +52,6 @@ public: m_password = password; } - template - void async_read_some(Mutable_Buffers const& buffers, Handler const& handler) - { - m_sock.async_read_some(buffers, handler); - } - - template - std::size_t read_some(Mutable_Buffers const& buffers, asio::error_code& ec) - { - return m_sock.read_some(buffers, ec); - } - - template - std::size_t read_some(Mutable_Buffers const& buffers) - { - return m_sock.read_some(buffers); - } - - template - void io_control(IO_Control_Command& ioc) - { - m_sock.io_control(ioc); - } - - template - void io_control(IO_Control_Command& ioc, asio::error_code& ec) - { - m_sock.io_control(ioc, ec); - } - - template - void async_write_some(Const_Buffers const& buffers, Handler const& handler) - { - m_sock.async_write_some(buffers, handler); - } - - void bind(endpoint_type const& endpoint) - { - m_sock.bind(endpoint); - } - - template - void bind(endpoint_type const& endpoint, Error_Handler const& error_handler) - { - m_sock.bind(endpoint, error_handler); - } - - void open(protocol_type const& p) - { - m_sock.open(p); - } - - template - void open(protocol_type const& p, Error_Handler const& error_handler) - { - m_sock.open(p, error_handler); - } - - void close() - { - m_remote_endpoint = endpoint_type(); - m_sock.close(); - } - - template - void close(Error_Handler const& error_handler) - { - m_sock.close(error_handler); - } - - endpoint_type remote_endpoint() - { - return m_remote_endpoint; - } - - template - endpoint_type remote_endpoint(Error_Handler const& error_handler) - { - return m_remote_endpoint; - } - - endpoint_type local_endpoint() - { - return m_sock.local_endpoint(); - } - - template - endpoint_type local_endpoint(Error_Handler const& error_handler) - { - return m_sock.local_endpoint(error_handler); - } - - asio::io_service& io_service() - { - return m_sock.io_service(); - } - - lowest_layer_type& lowest_layer() - { - return m_sock.lowest_layer(); - } - typedef boost::function handler_type; template @@ -176,20 +91,13 @@ private: void connect2(asio::error_code const& e, boost::shared_ptr h); void connect3(asio::error_code const& e, boost::shared_ptr h); - stream_socket m_sock; - // the socks5 proxy - std::string m_hostname; - int m_port; // send and receive buffer std::vector m_buffer; // proxy authentication std::string m_user; std::string m_password; - - endpoint_type m_remote_endpoint; - - tcp::resolver m_resolver; }; } +#endif diff --git a/libtorrent/include/libtorrent/storage.hpp b/libtorrent/include/libtorrent/storage.hpp index ccb17cb71..552b6e89f 100755 --- a/libtorrent/include/libtorrent/storage.hpp +++ b/libtorrent/include/libtorrent/storage.hpp @@ -41,9 +41,9 @@ POSSIBILITY OF SUCH DAMAGE. #endif #include -#include #include #include +#include #ifdef _MSC_VER #pragma warning(pop) @@ -52,6 +52,9 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/torrent_info.hpp" #include "libtorrent/piece_picker.hpp" +#include "libtorrent/intrusive_ptr_base.hpp" +#include "libtorrent/peer_request.hpp" +#include "libtorrent/hasher.hpp" #include "libtorrent/config.hpp" namespace libtorrent @@ -61,8 +64,11 @@ namespace libtorrent struct piece_checker_data; } + namespace fs = boost::filesystem; + class session; struct file_pool; + struct disk_io_job; #if defined(_WIN32) && defined(UNICODE) @@ -72,11 +78,11 @@ namespace libtorrent TORRENT_EXPORT std::vector > get_filesizes( torrent_info const& t - , boost::filesystem::path p); + , fs::path p); TORRENT_EXPORT bool match_filesizes( torrent_info const& t - , boost::filesystem::path p + , fs::path p , std::vector > const& sizes , bool compact_mode , std::string* error = 0); @@ -89,6 +95,15 @@ namespace libtorrent std::string m_msg; }; + struct TORRENT_EXPORT partial_hash + { + partial_hash(): offset(0) {} + // the number of bytes in the piece that has been hashed + int offset; + // the sha-1 context + hasher h; + }; + struct TORRENT_EXPORT storage_interface { // create directories and set file sizes @@ -103,7 +118,7 @@ namespace libtorrent // may throw file_error if storage for slot hasn't been allocated virtual void write(const char* buf, int slot, int offset, int size) = 0; - virtual bool move_storage(boost::filesystem::path save_path) = 0; + virtual bool move_storage(fs::path save_path) = 0; // verify storage dependent fast resume entries virtual bool verify_resume_data(entry& rd, std::string& error) = 0; @@ -121,6 +136,9 @@ namespace libtorrent // in slot3 and the data in slot3 in slot1 virtual void swap_slots3(int slot1, int slot2, int slot3) = 0; + // returns the sha1-hash for the data at the given slot + virtual sha1_hash hash_for_slot(int slot, partial_hash& h, int piece_size) = 0; + // this will close all open files that are opened for // writing. This is called when a torrent has finished // downloading. @@ -129,24 +147,32 @@ namespace libtorrent }; typedef storage_interface* (&storage_constructor_type)( - torrent_info const&, boost::filesystem::path const& + torrent_info const&, fs::path const& , file_pool&); TORRENT_EXPORT storage_interface* default_storage_constructor(torrent_info const& ti - , boost::filesystem::path const& path, file_pool& fp); + , fs::path const& path, file_pool& fp); // returns true if the filesystem the path relies on supports // sparse files or automatic zero filling of files. - TORRENT_EXPORT bool supports_sparse_files(boost::filesystem::path const& p); + TORRENT_EXPORT bool supports_sparse_files(fs::path const& p); - class TORRENT_EXPORT piece_manager : boost::noncopyable + struct disk_io_thread; + + class TORRENT_EXPORT piece_manager + : public intrusive_ptr_base + , boost::noncopyable { + friend class invariant_access; + friend struct disk_io_thread; public: piece_manager( - const torrent_info& info - , const boost::filesystem::path& path + boost::shared_ptr const& torrent + , torrent_info const& ti + , fs::path const& path , file_pool& fp + , disk_io_thread& io , storage_constructor_type sc); ~piece_manager(); @@ -156,35 +182,38 @@ namespace libtorrent std::pair check_files(std::vector& pieces , int& num_pieces, boost::recursive_mutex& mutex); - void release_files(); - void write_resume_data(entry& rd) const; bool verify_resume_data(entry& rd, std::string& error); - bool is_allocating() const; - bool allocate_slots(int num_slots, bool abort_on_disk = false); + bool is_allocating() const + { return m_state == state_allocating; } + void mark_failed(int index); unsigned long piece_crc( int slot_index , int block_size , piece_picker::block_info const* bi); + int slot_for_piece(int piece_index) const; - size_type read( - char* buf - , int piece_index - , int offset - , int size); + void async_read( + peer_request const& r + , boost::function const& handler); - void write( - const char* buf - , int piece_index - , int offset - , int size); + void async_write( + peer_request const& r + , char const* buffer + , boost::function const& f); - boost::filesystem::path const& save_path() const; - bool move_storage(boost::filesystem::path const&); + void async_hash(int piece, boost::function const& f); + + fs::path save_path() const; + + void async_release_files( + boost::function const& handler); + void async_move_storage(fs::path const& p + , boost::function const& handler); // fills the vector that maps all allocated // slots to the piece that is stored (or @@ -192,11 +221,134 @@ namespace libtorrent // of unassigned pieces and -1 is unallocated void export_piece_map(std::vector& pieces) const; - bool compact_allocation() const; + bool compact_allocation() const + { return m_compact_mode; } +#ifndef NDEBUG + std::string name() const { return m_info.name(); } +#endif + private: - class impl; - std::auto_ptr m_pimpl; + + bool allocate_slots(int num_slots, bool abort_on_disk = false); + + int identify_data( + const std::vector& piece_data + , int current_slot + , std::vector& have_pieces + , int& num_pieces + , const std::multimap& hash_to_piece + , boost::recursive_mutex& mutex); + + size_type read_impl( + char* buf + , int piece_index + , int offset + , int size); + + void write_impl( + const char* buf + , int piece_index + , int offset + , int size); + + sha1_hash hash_for_piece_impl(int piece); + + void release_files_impl(); + + bool move_storage_impl(fs::path const& save_path); + + int allocate_slot_for_piece(int piece_index); +#ifndef NDEBUG + void check_invariant() const; +#ifdef TORRENT_STORAGE_DEBUG + void debug_log() const; +#endif +#endif + boost::scoped_ptr m_storage; + + // if this is true, pieces are always allocated at the + // lowest possible slot index. If it is false, pieces + // are always written to their final place immediately + bool m_compact_mode; + + // if this is true, pieces that haven't been downloaded + // will be filled with zeroes. Not filling with zeroes + // will not work in some cases (where a seek cannot pass + // the end of the file). + bool m_fill_mode; + + // a bitmask representing the pieces we have + std::vector m_have_piece; + + torrent_info const& m_info; + + // slots that haven't had any file storage allocated + std::vector m_unallocated_slots; + // slots that have file storage, but isn't assigned to a piece + std::vector m_free_slots; + + enum + { + has_no_slot = -3 // the piece has no storage + }; + + // maps piece indices to slots. If a piece doesn't + // have any storage, it is set to 'has_no_slot' + std::vector m_piece_to_slot; + + enum + { + unallocated = -1, // the slot is unallocated + unassigned = -2 // the slot is allocated but not assigned to a piece + }; + + // maps slots to piece indices, if a slot doesn't have a piece + // it can either be 'unassigned' or 'unallocated' + std::vector m_slot_to_piece; + + fs::path m_save_path; + + mutable boost::recursive_mutex m_mutex; + + bool m_allocating; + boost::mutex m_allocating_monitor; + boost::condition m_allocating_condition; + + // these states are used while checking/allocating the torrent + + enum { + // the default initial state + state_none, + // the file checking is complete + state_finished, + // creating the directories + state_create_files, + // checking the files + state_full_check, + // allocating files (in non-compact mode) + state_allocating + } m_state; + int m_current_slot; + + std::vector m_piece_data; + + // this maps a piece hash to piece index. It will be + // build the first time it is used (to save time if it + // isn't needed) + std::multimap m_hash_to_piece; + + std::map m_piece_hasher; + + disk_io_thread& m_io_thread; + + // the reason for this to be a void pointer + // is to avoid creating a dependency on the + // torrent. This shared_ptr is here only + // to keep the torrent object alive until + // the piece_manager destructs. This is because + // the torrent_info object is owned by the torrent. + boost::shared_ptr m_torrent; }; } diff --git a/libtorrent/include/libtorrent/time.hpp b/libtorrent/include/libtorrent/time.hpp index 2470522c0..3c51bdc00 100644 --- a/libtorrent/include/libtorrent/time.hpp +++ b/libtorrent/include/libtorrent/time.hpp @@ -51,7 +51,8 @@ namespace libtorrent } } -#if (!defined (__MACH__) && !defined (_WIN32) && !defined(_POSIX_MONOTONIC_CLOCK) || _POSIX_MONOTONIC_CLOCK < 0) || defined (TORRENT_USE_BOOST_DATE_TIME) +#if (!defined (__MACH__) && !defined (_WIN32) && (!defined(_POSIX_MONOTONIC_CLOCK) \ + || _POSIX_MONOTONIC_CLOCK < 0)) || defined (TORRENT_USE_BOOST_DATE_TIME) #include @@ -99,8 +100,12 @@ namespace libtorrent inline bool is_negative(time_duration dt) { return dt.diff < 0; } inline bool operator<(time_duration lhs, time_duration rhs) { return lhs.diff < rhs.diff; } + inline bool operator<=(time_duration lhs, time_duration rhs) + { return lhs.diff <= rhs.diff; } inline bool operator>(time_duration lhs, time_duration rhs) { return lhs.diff > rhs.diff; } + inline bool operator>=(time_duration lhs, time_duration rhs) + { return lhs.diff >= rhs.diff; } // libtorrent time type struct ptime @@ -381,3 +386,4 @@ namespace libtorrent #endif #endif + diff --git a/libtorrent/include/libtorrent/torrent.hpp b/libtorrent/include/libtorrent/torrent.hpp index 8943b0db9..9c9a5d034 100755 --- a/libtorrent/include/libtorrent/torrent.hpp +++ b/libtorrent/include/libtorrent/torrent.hpp @@ -68,6 +68,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/escape_string.hpp" #include "libtorrent/bandwidth_manager.hpp" #include "libtorrent/storage.hpp" +#include "libtorrent/hasher.hpp" namespace libtorrent { @@ -84,6 +85,8 @@ namespace libtorrent struct piece_checker_data; } + namespace fs = boost::filesystem; + // a torrent is a class that holds information // for a specific download. It updates itself against // the tracker @@ -96,7 +99,7 @@ namespace libtorrent aux::session_impl& ses , aux::checker_impl& checker , torrent_info const& tf - , boost::filesystem::path const& save_path + , fs::path const& save_path , tcp::endpoint const& net_interface , bool compact_mode , int block_size @@ -111,7 +114,7 @@ namespace libtorrent , char const* tracker_url , sha1_hash const& info_hash , char const* name - , boost::filesystem::path const& save_path + , fs::path const& save_path , tcp::endpoint const& net_interface , bool compact_mode , int block_size @@ -184,6 +187,8 @@ namespace libtorrent void filter_files(std::vector const& files); // ============ end deprecation ============= + void piece_availability(std::vector& avail) const; + void set_piece_priority(int index, int priority); int piece_priority(int index) const; @@ -422,11 +427,12 @@ namespace libtorrent // completed() is called immediately after it. void finished(); - bool verify_piece(int piece_index); + void async_verify_piece(int piece_index, boost::function const&); // this is called from the peer_connection // each time a piece has failed the hash // test + void piece_finished(int index, bool passed_hash_check); void piece_failed(int index); void received_redundant_data(int num_bytes) { assert(num_bytes > 0); m_total_redundant_bytes += num_bytes; } @@ -446,17 +452,15 @@ namespace libtorrent - m_num_pieces - m_picker->num_filtered() == 0; } - boost::filesystem::path save_path() const; + fs::path save_path() const; alert_manager& alerts() const; piece_picker& picker() { - assert(!is_seed()); assert(m_picker.get()); return *m_picker; } bool has_picker() const { - assert((valid_metadata() && !is_seed()) == bool(m_picker.get() != 0)); return m_picker.get() != 0; } policy& get_policy() @@ -503,14 +507,14 @@ namespace libtorrent void set_max_uploads(int limit); void set_max_connections(int limit); - bool move_storage(boost::filesystem::path const& save_path); + void move_storage(fs::path const& save_path); // unless this returns true, new connections must wait // with their initialization. bool ready_for_connections() const { return m_connections_initialized; } bool valid_metadata() const - { return m_storage.get() != 0; } + { return m_torrent_file.is_valid(); } // parses the info section from the given // bencoded tree and moves the torrent @@ -519,6 +523,12 @@ namespace libtorrent void set_metadata(entry const&); private: + + void on_files_released(int ret, disk_io_job const& j); + void on_storage_moved(int ret, disk_io_job const& j); + + void on_piece_verified(int ret, disk_io_job const& j + , boost::function f); void try_next_tracker(); int prioritize_tracker(int tracker_index); @@ -552,7 +562,27 @@ namespace libtorrent // if this pointer is 0, the torrent is in // a state where the metadata hasn't been // received yet. - boost::scoped_ptr m_storage; + // the piece_manager keeps the torrent object + // alive by holding a shared_ptr to it and + // the torrent keeps the piece manager alive + // with this intrusive_ptr. This cycle is + // broken when torrent::abort() is called + // Then the torrent releases the piece_manager + // and when the piece_manager is complete with all + // outstanding disk io jobs (that keeps + // the piece_manager alive) it will destruct + // and release the torrent file. The reason for + // this is that the torrent_info is used by + // the piece_manager, and stored in the + // torrent, so the torrent cannot destruct + // before the piece_manager. + boost::intrusive_ptr m_owning_storage; + + // this is a weak (non owninig) pointer to + // the piece_manager. This is used after the torrent + // has been aborted, and it can no longer own + // the object. + piece_manager* m_storage; // the time of next tracker request ptime m_next_request; @@ -689,7 +719,7 @@ namespace libtorrent // are opened through tcp::endpoint m_net_interface; - boost::filesystem::path m_save_path; + fs::path m_save_path; // determines the storage state for this torrent. const bool m_compact_mode; diff --git a/libtorrent/include/libtorrent/torrent_handle.hpp b/libtorrent/include/libtorrent/torrent_handle.hpp index cb4b892d2..b5e6bdc17 100755 --- a/libtorrent/include/libtorrent/torrent_handle.hpp +++ b/libtorrent/include/libtorrent/torrent_handle.hpp @@ -54,6 +54,8 @@ POSSIBILITY OF SUCH DAMAGE. namespace libtorrent { + namespace fs = boost::filesystem; + namespace aux { struct session_impl; @@ -202,15 +204,22 @@ namespace libtorrent int block_size; }; + struct TORRENT_EXPORT block_info + { + enum block_state_t + { none, requested, writing, finished }; + + tcp::endpoint peer; + unsigned state:2; + unsigned num_downloads:14; + }; + struct TORRENT_EXPORT partial_piece_info { enum { max_blocks_per_piece = 256 }; int piece_index; int blocks_in_piece; - std::bitset requested_blocks; - std::bitset finished_blocks; - tcp::endpoint peer[max_blocks_per_piece]; - int num_downloads[max_blocks_per_piece]; + block_info blocks[max_blocks_per_piece]; enum state_t { none, slow, medium, fast }; state_t piece_state; }; @@ -269,6 +278,8 @@ namespace libtorrent // ================ end deprecation ============ + void piece_availability(std::vector& avail) const; + // priority must be within the range [0, 7] void piece_priority(int index, int priority) const; int piece_priority(int index) const; @@ -321,7 +332,7 @@ namespace libtorrent // the ratio is uploaded / downloaded. less than 1 is not allowed void set_ratio(float up_down_ratio) const; - boost::filesystem::path save_path() const; + fs::path save_path() const; // -1 means unlimited unchokes void set_max_uploads(int max_uploads) const; @@ -333,7 +344,7 @@ namespace libtorrent , std::string const& password) const; // post condition: save_path() == save_path if true is returned - bool move_storage(boost::filesystem::path const& save_path) const; + void move_storage(fs::path const& save_path) const; const sha1_hash& info_hash() const { return m_info_hash; } diff --git a/libtorrent/include/libtorrent/torrent_info.hpp b/libtorrent/include/libtorrent/torrent_info.hpp index d26f9f1f2..e52024255 100755 --- a/libtorrent/include/libtorrent/torrent_info.hpp +++ b/libtorrent/include/libtorrent/torrent_info.hpp @@ -62,16 +62,18 @@ namespace libtorrent namespace pt = boost::posix_time; namespace gr = boost::gregorian; + namespace fs = boost::filesystem; + struct TORRENT_EXPORT file_entry { - boost::filesystem::path path; + fs::path path; size_type offset; // the offset of this file inside the torrent size_type size; // the size of this file // if the path was incorrectly encoded, this is // the origianal corrupt encoded string. It is // preserved in order to be able to reproduce // the correct info-hash - boost::shared_ptr orig_path; + boost::shared_ptr orig_path; }; struct TORRENT_EXPORT file_slice @@ -109,7 +111,7 @@ namespace libtorrent void set_piece_size(int size); void set_hash(int index, sha1_hash const& h); void add_tracker(std::string const& url, int tier = 0); - void add_file(boost::filesystem::path file, size_type size); + void add_file(fs::path file, size_type size); void add_url_seed(std::string const& url); std::vector map_block(int piece, size_type offset, int size) const; @@ -192,6 +194,8 @@ namespace libtorrent // used by seeds void seed_free(); + void swap(torrent_info& ti); + private: void read_torrent_info(const entry& libtorrent); diff --git a/libtorrent/include/libtorrent/tracker_manager.hpp b/libtorrent/include/libtorrent/tracker_manager.hpp index a4d24f751..1435ceda6 100755 --- a/libtorrent/include/libtorrent/tracker_manager.hpp +++ b/libtorrent/include/libtorrent/tracker_manager.hpp @@ -62,6 +62,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/config.hpp" #include "libtorrent/time.hpp" #include "libtorrent/connection_queue.hpp" +#include "libtorrent/intrusive_ptr_base.hpp" namespace libtorrent { @@ -148,15 +149,10 @@ namespace libtorrent , request_callback* requester , int maximum_tracker_response_length); - TORRENT_EXPORT void intrusive_ptr_add_ref(timeout_handler const*); - TORRENT_EXPORT void intrusive_ptr_release(timeout_handler const*); - struct TORRENT_EXPORT timeout_handler - : boost::noncopyable + : intrusive_ptr_base + , boost::noncopyable { - friend TORRENT_EXPORT void intrusive_ptr_add_ref(timeout_handler const*); - friend TORRENT_EXPORT void intrusive_ptr_release(timeout_handler const*); - timeout_handler(asio::strand& str); void set_timeout(int completion_timeout, int read_timeout); @@ -187,7 +183,6 @@ namespace libtorrent typedef boost::mutex mutex_t; mutable mutex_t m_mutex; - mutable int m_refs; }; struct TORRENT_EXPORT tracker_connection diff --git a/libtorrent/include/libtorrent/web_peer_connection.hpp b/libtorrent/include/libtorrent/web_peer_connection.hpp index a84f2e6ff..ba7450c0a 100755 --- a/libtorrent/include/libtorrent/web_peer_connection.hpp +++ b/libtorrent/include/libtorrent/web_peer_connection.hpp @@ -123,7 +123,7 @@ namespace libtorrent void write_request(peer_request const& r); void write_cancel(peer_request const& r) {} void write_have(int index) {} - void write_piece(peer_request const& r) {} + void write_piece(peer_request const& r, char const* buffer) { assert(false); } void write_keepalive() {} void on_connected(); diff --git a/libtorrent/src/Makefile.am b/libtorrent/src/Makefile.am index 4c04b2863..97e74ee25 100644 --- a/libtorrent/src/Makefile.am +++ b/libtorrent/src/Makefile.am @@ -21,7 +21,9 @@ torrent_info.cpp tracker_manager.cpp http_connection.cpp \ http_tracker_connection.cpp udp_tracker_connection.cpp \ alert.cpp identify_client.cpp ip_filter.cpp file.cpp metadata_transfer.cpp \ logger.cpp file_pool.cpp ut_pex.cpp lsd.cpp upnp.cpp instantiate_connection.cpp \ -socks5_stream.cpp http_stream.cpp connection_queue.cpp $(kademlia_sources) +socks5_stream.cpp socks4_stream.cpp http_stream.cpp connection_queue.cpp \ +disk_io_thread.cpp \ +$(kademlia_sources) noinst_HEADERS = \ $(top_srcdir)/include/libtorrent/alert.hpp \ @@ -34,6 +36,7 @@ $(top_srcdir)/include/libtorrent/bencode.hpp \ $(top_srcdir)/include/libtorrent/buffer.hpp \ $(top_srcdir)/include/libtorrent/connection_queue.hpp \ $(top_srcdir)/include/libtorrent/debug.hpp \ +$(top_srcdir)/include/libtorrent/disk_io_thread.hpp \ $(top_srcdir)/include/libtorrent/entry.hpp \ $(top_srcdir)/include/libtorrent/escape_string.hpp \ $(top_srcdir)/include/libtorrent/extensions.hpp \ @@ -50,6 +53,7 @@ $(top_srcdir)/include/libtorrent/session_settings.hpp \ $(top_srcdir)/include/libtorrent/http_tracker_connection.hpp \ $(top_srcdir)/include/libtorrent/identify_client.hpp \ $(top_srcdir)/include/libtorrent/instantiate_connection.hpp \ +$(top_srcdir)/include/libtorrent/intrusive_ptr_base.hpp \ $(top_srcdir)/include/libtorrent/invariant_check.hpp \ $(top_srcdir)/include/libtorrent/io.hpp \ $(top_srcdir)/include/libtorrent/ip_filter.hpp \ @@ -72,6 +76,7 @@ $(top_srcdir)/include/libtorrent/session.hpp \ $(top_srcdir)/include/libtorrent/size_type.hpp \ $(top_srcdir)/include/libtorrent/socket.hpp \ $(top_srcdir)/include/libtorrent/socket_type.hpp \ +$(top_srcdir)/include/libtorrent/socks4_stream.hpp \ $(top_srcdir)/include/libtorrent/socks5_stream.hpp \ $(top_srcdir)/include/libtorrent/stat.hpp \ $(top_srcdir)/include/libtorrent/storage.hpp \ diff --git a/libtorrent/src/bt_peer_connection.cpp b/libtorrent/src/bt_peer_connection.cpp index e2e2fe537..55eec74c2 100755 --- a/libtorrent/src/bt_peer_connection.cpp +++ b/libtorrent/src/bt_peer_connection.cpp @@ -277,6 +277,8 @@ namespace libtorrent void bt_peer_connection::write_pe1_2_dhkey() { + INVARIANT_CHECK; + assert(!m_encrypted); assert(!m_rc4_encrypted); assert(!m_DH_key_exchange.get()); @@ -305,6 +307,8 @@ namespace libtorrent void bt_peer_connection::write_pe3_sync() { + INVARIANT_CHECK; + assert (!m_encrypted); assert (!m_rc4_encrypted); assert (is_local()); @@ -383,6 +387,8 @@ namespace libtorrent void bt_peer_connection::write_pe4_sync(int crypto_select) { + INVARIANT_CHECK; + assert(!is_local()); assert(!m_encrypted); assert(!m_rc4_encrypted); @@ -415,6 +421,8 @@ namespace libtorrent void bt_peer_connection::write_pe_vc_cryptofield(buffer::interval& write_buf, int crypto_field, int pad_size) { + INVARIANT_CHECK; + assert(crypto_field <= 0x03 && crypto_field > 0); assert(pad_size == 0); // pad not used yet // vc,crypto_field,len(pad),pad, (len(ia)) @@ -444,6 +452,8 @@ namespace libtorrent void bt_peer_connection::init_pe_RC4_handler(char const* secret, sha1_hash const& stream_key) { + INVARIANT_CHECK; + assert(secret); hasher h; @@ -483,8 +493,9 @@ namespace libtorrent assert (begin); assert (end); assert (end > begin); + assert (!m_rc4_encrypted || m_encrypted); - if (m_rc4_encrypted && m_encrypted) + if (m_rc4_encrypted) m_RC4_handler->encrypt(begin, end - begin); peer_connection::send_buffer(begin, end); @@ -492,7 +503,9 @@ namespace libtorrent buffer::interval bt_peer_connection::allocate_send_buffer(int size) { - if (m_rc4_encrypted && m_encrypted) + assert(!m_rc4_encrypted || m_encrypted); + + if (m_rc4_encrypted) { m_enc_send_buffer = peer_connection::allocate_send_buffer(size); return m_enc_send_buffer; @@ -506,7 +519,9 @@ namespace libtorrent void bt_peer_connection::setup_send() { - if (m_rc4_encrypted && m_encrypted) + assert(!m_rc4_encrypted || m_encrypted); + + if (m_rc4_encrypted) { assert (m_enc_send_buffer.begin); assert (m_enc_send_buffer.end); @@ -1129,6 +1144,7 @@ namespace libtorrent { INVARIANT_CHECK; + assert(!in_handshake()); boost::shared_ptr t = associated_torrent().lock(); assert(t); assert(m_sent_bitfield == false); @@ -1319,7 +1335,7 @@ namespace libtorrent send_buffer(msg, msg + packet_size); } - void bt_peer_connection::write_piece(peer_request const& r) + void bt_peer_connection::write_piece(peer_request const& r, char const* buffer) { INVARIANT_CHECK; @@ -1334,9 +1350,7 @@ namespace libtorrent detail::write_uint8(msg_piece, i.begin); detail::write_int32(r.piece, i.begin); detail::write_int32(r.start, i.begin); - - t->filesystem().read( - i.begin, r.piece, r.start, r.length); + std::memcpy(i.begin, buffer, r.length); assert(i.begin + r.length == i.end); @@ -1357,10 +1371,11 @@ namespace libtorrent return p.connection != m_pc && p.connection && p.connection->pid() == m_id - && !p.connection->pid().is_all_zeros(); + && !p.connection->pid().is_all_zeros() + && p.ip.address() == m_pc->remote().address(); } - peer_id m_id; + peer_id const& m_id; peer_connection const* m_pc; }; } @@ -1382,6 +1397,7 @@ namespace libtorrent m_statistics.received_bytes(0, bytes_transferred); #ifndef TORRENT_DISABLE_ENCRYPTION + assert(in_handshake() || !m_rc4_encrypted || m_encrypted); if (m_rc4_encrypted && m_encrypted) { buffer::interval wr_buf = wr_recv_buffer(); @@ -1781,6 +1797,9 @@ namespace libtorrent { recv_buffer.begin += pad_size; int len_ia = detail::read_int16(recv_buffer.begin); + + if (len_ia < 0) throw protocol_error("invalid len_ia in handshake"); + #ifdef TORRENT_VERBOSE_LOGGING (*m_logger) << " len(IA) : " << len_ia << "\n"; #endif @@ -1972,8 +1991,6 @@ namespace libtorrent , (char*)info_hash.begin()); attach_to_torrent(info_hash); - t = associated_torrent().lock(); - assert(t); } else { @@ -1990,17 +2007,15 @@ namespace libtorrent #ifdef TORRENT_VERBOSE_LOGGING (*m_logger) << " info_hash received\n"; #endif - t = associated_torrent().lock(); - assert(t); } - // Respond with handshake and bitfield + t = associated_torrent().lock(); + assert(t); + if (!is_local()) { write_handshake(); } - if (t->valid_metadata()) - write_bitfield(t->pieces()); assert(t->get_policy().has_connection(this)); @@ -2130,6 +2145,8 @@ namespace libtorrent m_state = read_packet_size; reset_recv_buffer(4); + if (t->valid_metadata()) + write_bitfield(t->pieces()); assert(!packet_finished()); return; @@ -2239,8 +2256,10 @@ namespace libtorrent void bt_peer_connection::check_invariant() const { #ifndef TORRENT_DISABLE_ENCRYPTION - assert(bool(m_state == read_pe_dhkey) == bool(m_DH_key_exchange) - || !is_local()); + assert( (bool(m_state != read_pe_dhkey) || m_DH_key_exchange.get()) + || !is_local()); + + assert(!m_rc4_encrypted || m_RC4_handler.get()); #endif if (!m_in_constructor) peer_connection::check_invariant(); @@ -2258,4 +2277,3 @@ namespace libtorrent } - diff --git a/libtorrent/src/entry.cpp b/libtorrent/src/entry.cpp index 6506ed4c2..16dffc275 100755 --- a/libtorrent/src/entry.cpp +++ b/libtorrent/src/entry.cpp @@ -278,6 +278,12 @@ namespace libtorrent } } + void entry::swap(entry& e) + { + // not implemented + assert(false); + } + void entry::print(std::ostream& os, int indent) const { assert(indent >= 0); diff --git a/libtorrent/src/file.cpp b/libtorrent/src/file.cpp index 71bc795a4..515406a46 100755 --- a/libtorrent/src/file.cpp +++ b/libtorrent/src/file.cpp @@ -82,8 +82,6 @@ BOOST_STATIC_ASSERT(sizeof(lseek(0, 0, 0)) >= 8); #endif -namespace fs = boost::filesystem; - namespace { enum { mode_in = 1, mode_out = 2 }; @@ -130,6 +128,8 @@ namespace namespace libtorrent { + namespace fs = boost::filesystem; + const file::open_mode file::in(mode_in); const file::open_mode file::out(mode_out); @@ -248,15 +248,15 @@ namespace libtorrent void set_size(size_type s) { size_type pos = tell(); - seek(1, 0); + seek(s - 1); char dummy = 0; read(&dummy, 1); - seek(1, 0); + seek(s - 1); write(&dummy, 1); - seek(pos, 1); + seek(pos); } - size_type seek(size_type offset, int m) + size_type seek(size_type offset, int m = 1) { assert(m_open_mode); assert(m_fd != -1); @@ -303,13 +303,13 @@ namespace libtorrent file::file() : m_impl(new impl()) {} - file::file(boost::filesystem::path const& p, file::open_mode m) + file::file(fs::path const& p, file::open_mode m) : m_impl(new impl(p, m.m_mask)) {} file::~file() {} - void file::open(boost::filesystem::path const& p, file::open_mode m) + void file::open(fs::path const& p, file::open_mode m) { m_impl->open(p, m.m_mask); } diff --git a/libtorrent/src/http_connection.cpp b/libtorrent/src/http_connection.cpp index ba1592898..53798cae1 100644 --- a/libtorrent/src/http_connection.cpp +++ b/libtorrent/src/http_connection.cpp @@ -237,7 +237,7 @@ void http_connection::on_read(asio::error_code const& e data = m_parser.get_body().begin; size = m_parser.get_body().left(); } - m_handler(asio::error_code(), m_parser, data, size); + m_handler(e, m_parser, data, size); return; } diff --git a/libtorrent/src/http_tracker_connection.cpp b/libtorrent/src/http_tracker_connection.cpp index cc039f198..936f8292a 100755 --- a/libtorrent/src/http_tracker_connection.cpp +++ b/libtorrent/src/http_tracker_connection.cpp @@ -411,6 +411,12 @@ namespace libtorrent std::min(req.num_want, 999)); m_send_buffer += '&'; } + if (m_settings.announce_ip != address() && !url_has_argument(request, "ip")) + { + m_send_buffer += "ip="; + m_send_buffer += m_settings.announce_ip.to_string(); + m_send_buffer += '&'; + } #ifndef TORRENT_DISABLE_ENCRYPTION m_send_buffer += "supportcrypto=1&"; diff --git a/libtorrent/src/identify_client.cpp b/libtorrent/src/identify_client.cpp index cf837a05b..44be2d60f 100755 --- a/libtorrent/src/identify_client.cpp +++ b/libtorrent/src/identify_client.cpp @@ -197,7 +197,7 @@ namespace , {"XT", "XanTorrent"} , {"XX", "Xtorrent"} , {"ZT", "ZipTorrent"} - , {"lt", "libTorrent (libtorrent.rakshasa.no/}"} + , {"lt", "rTorrent"} , {"pX", "pHoeniX"} , {"qB", "qBittorrent"} }; diff --git a/libtorrent/src/instantiate_connection.cpp b/libtorrent/src/instantiate_connection.cpp index ff4efbc59..43b70f40d 100644 --- a/libtorrent/src/instantiate_connection.cpp +++ b/libtorrent/src/instantiate_connection.cpp @@ -67,6 +67,12 @@ namespace libtorrent if (ps.type == proxy_settings::socks5_pw) s->get().set_username(ps.username, ps.password); } + else if (ps.type == proxy_settings::socks4) + { + s->instantiate(); + s->get().set_proxy(ps.hostname, ps.port); + s->get().set_username(ps.username); + } else { throw std::runtime_error("unsupported proxy type"); diff --git a/libtorrent/src/ip_filter.cpp b/libtorrent/src/ip_filter.cpp index 92ea711c7..cf368c4d1 100644 --- a/libtorrent/src/ip_filter.cpp +++ b/libtorrent/src/ip_filter.cpp @@ -69,6 +69,15 @@ namespace libtorrent , m_filter6.export_filter()); } + void port_filter::add_rule(boost::uint16_t first, boost::uint16_t last, int flags) + { + m_filter.add_rule(first, last, flags); + } + + int port_filter::access(boost::uint16_t port) const + { + return m_filter.access(port); + } /* void ip_filter::print() const { diff --git a/libtorrent/src/logger.cpp b/libtorrent/src/logger.cpp index 6881c5e7b..b33816a59 100644 --- a/libtorrent/src/logger.cpp +++ b/libtorrent/src/logger.cpp @@ -53,17 +53,20 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/peer_request.hpp" #include "libtorrent/peer_connection.hpp" -namespace libtorrent { namespace +namespace libtorrent { + +namespace fs = boost::filesystem; + +namespace { struct logger_peer_plugin : peer_plugin { logger_peer_plugin(std::string const& filename) { - using namespace boost::filesystem; - path dir(complete("libtorrent_ext_logs")); - if (!exists(dir)) create_directories(dir); - m_file.open(dir / filename, std::ios_base::out | std::ios_base::out); + fs::path dir(fs::complete("libtorrent_ext_logs")); + if (!fs::exists(dir)) fs::create_directories(dir); + m_file.open((dir / filename).string().c_str(), std::ios_base::out | std::ios_base::out); m_file << "\n\n\n"; log_timestamp(); m_file << "*** starting log ***\n"; @@ -201,7 +204,7 @@ namespace libtorrent { namespace } private: - boost::filesystem::ofstream m_file; + std::ofstream m_file; }; struct logger_plugin : torrent_plugin diff --git a/libtorrent/src/metadata_transfer.cpp b/libtorrent/src/metadata_transfer.cpp index fe308f926..97635cdb9 100644 --- a/libtorrent/src/metadata_transfer.cpp +++ b/libtorrent/src/metadata_transfer.cpp @@ -38,8 +38,6 @@ POSSIBILITY OF SUCH DAMAGE. #include #include -#include -#include #ifdef _MSC_VER #pragma warning(pop) @@ -56,6 +54,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/torrent.hpp" #include "libtorrent/extensions.hpp" #include "libtorrent/extensions/metadata_transfer.hpp" +#include "libtorrent/alert_types.hpp" namespace libtorrent { namespace { @@ -176,14 +175,13 @@ namespace libtorrent { namespace , false); m_metadata_progress = 0; m_metadata_size = 0; - // TODO: allow plugins to post alerts -/* - if (m_ses.m_alerts.should_post(alert::info)) + + if (m_torrent.alerts().should_post(alert::info)) { - m_ses.m_alerts.post_alert(metadata_failed_alert( - get_handle(), "invalid metadata received from swarm")); + m_torrent.alerts().post_alert(metadata_failed_alert( + m_torrent.get_handle(), "invalid metadata received from swarm")); } -*/ + return false; } diff --git a/libtorrent/src/peer_connection.cpp b/libtorrent/src/peer_connection.cpp index db70d2e1c..08f448fce 100755 --- a/libtorrent/src/peer_connection.cpp +++ b/libtorrent/src/peer_connection.cpp @@ -59,21 +59,6 @@ using libtorrent::aux::session_impl; namespace libtorrent { - void intrusive_ptr_add_ref(peer_connection const* c) - { - assert(c->m_refs >= 0); - assert(c != 0); - ++c->m_refs; - } - - void intrusive_ptr_release(peer_connection const* c) - { - assert(c->m_refs > 0); - assert(c != 0); - if (--c->m_refs == 0) - delete c; - } - // outbound connection peer_connection::peer_connection( session_impl& ses @@ -94,6 +79,7 @@ namespace libtorrent , m_packet_size(0) , m_recv_pos(0) , m_current_send_buffer(0) + , m_reading_bytes(0) , m_write_pos(0) , m_last_receive(time_now()) , m_last_sent(time_now()) @@ -122,12 +108,14 @@ namespace libtorrent , m_prefer_whole_pieces(false) , m_request_large_blocks(false) , m_non_prioritized(false) - , m_refs(0) , m_upload_limit(resource_request::inf) , m_download_limit(resource_request::inf) , m_peer_info(peerinfo) , m_speed(slow) , m_connection_ticket(-1) + , m_remote_bytes_dled(0) + , m_remote_dl_rate(0) + , m_remote_dl_update(time_now()) #ifndef NDEBUG , m_in_constructor(true) #endif @@ -167,6 +155,7 @@ namespace libtorrent , m_packet_size(0) , m_recv_pos(0) , m_current_send_buffer(0) + , m_reading_bytes(0) , m_write_pos(0) , m_last_receive(time_now()) , m_last_sent(time_now()) @@ -193,11 +182,13 @@ namespace libtorrent , m_prefer_whole_pieces(false) , m_request_large_blocks(false) , m_non_prioritized(false) - , m_refs(0) , m_upload_limit(resource_request::inf) , m_download_limit(resource_request::inf) , m_peer_info(peerinfo) , m_speed(slow) + , m_remote_bytes_dled(0) + , m_remote_dl_rate(0) + , m_remote_dl_update(time_now()) #ifndef NDEBUG , m_in_constructor(true) #endif @@ -742,8 +733,18 @@ namespace libtorrent && !is_interesting() && t->picker().piece_priority(index) != 0) t->get_policy().peer_is_interesting(*this); - } + // this will disregard all have messages we get within + // the first two seconds. Since some clients implements + // lazy bitfields, these will not be reliable to use + // for an estimated peer download rate. + if (!peer_info_struct() || time_now() - peer_info_struct()->connected > seconds(2)) + { + // update bytes downloaded since last timer + m_remote_bytes_dled += t->torrent_file().piece_size(index); + } + } + if (is_seed()) { assert(m_peer_info); @@ -1012,7 +1013,7 @@ namespace libtorrent for (std::vector::const_iterator i = dl_queue.begin(); i != dl_queue.end(); ++i) { - assert(i->finished < blocks_per_piece); + assert(i->finished <= blocks_per_piece); } } } @@ -1076,7 +1077,6 @@ namespace libtorrent piece_picker& picker = t->picker(); piece_manager& fs = t->filesystem(); - policy& pol = t->get_policy(); std::vector finished_blocks; piece_block block_finished(p.piece, p.start / t->block_size()); @@ -1125,13 +1125,12 @@ namespace libtorrent } else { - // cancel the block from the +/* // cancel the block from the // peer that has taken over it. boost::optional peer = t->picker().get_downloader(block_finished); - if (peer) + if (peer && t->picker().is_requested(block_finished)) { - assert(!t->picker().is_finished(block_finished)); peer_connection* pc = t->connection_for(*peer); if (pc && pc != this) { @@ -1140,7 +1139,7 @@ namespace libtorrent } } else - { +*/ { if (t->alerts().should_post(alert::debug)) { t->alerts().post_alert( @@ -1153,15 +1152,28 @@ namespace libtorrent (*m_logger) << " *** The block we just got was not in the " "request queue ***\n"; #endif + t->received_redundant_data(p.length); + if (!has_peer_choked()) + { + request_a_block(*t, *this); + send_block_requests(); + } + return; } } + assert(picker.is_requested(block_finished)); + // if the block we got is already finished, then ignore it - if (picker.is_finished(block_finished)) + if (picker.is_downloaded(block_finished)) { - t->received_redundant_data(t->block_size()); - pol.block_finished(*this, block_finished); - send_block_requests(); + t->received_redundant_data(p.length); + + if (!has_peer_choked()) + { + request_a_block(*t, *this); + send_block_requests(); + } if (request_peer && !request_peer->has_peer_choked() && !t->is_seed()) { @@ -1171,107 +1183,73 @@ namespace libtorrent return; } - fs.write(data, p.piece, p.start, p.length); - - picker.mark_as_finished(block_finished, m_remote); - - try - { - pol.block_finished(*this, block_finished); - send_block_requests(); - } - catch (std::exception const&) {} - + fs.async_write(p, data, bind(&peer_connection::on_disk_write_complete + , self(), _1, _2, p, t)); + picker.mark_as_writing(block_finished, m_remote); + if (request_peer && !request_peer->has_peer_choked() && !t->is_seed()) { request_a_block(*t, *request_peer); request_peer->send_block_requests(); } + } + + void peer_connection::on_disk_write_complete(int ret, disk_io_job const& j + , peer_request p, boost::shared_ptr t) + { + session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); + + if (ret == -1 || !t) + { + if (!t) + { + m_ses.connection_failed(m_socket, remote(), j.str.c_str()); + return; + } + + if (t->alerts().should_post(alert::fatal)) + { + std::string err = "torrent paused: disk write error, " + j.str; + t->alerts().post_alert(file_error_alert(t->get_handle(), err)); + } + t->pause(); + return; + } + + if (t->is_seed()) return; + + piece_picker& picker = t->picker(); + + assert(p.piece == j.piece); + assert(p.start == j.offset); + piece_block block_finished(p.piece, p.start / t->block_size()); + picker.mark_as_finished(block_finished, m_remote); + + if (!has_peer_choked() && !t->is_seed() && !m_torrent.expired()) + { + // this is a free function defined in policy.cpp + request_a_block(*t, *this); + try + { + send_block_requests(); + } + catch (std::exception const&) {} + } + #ifndef NDEBUG try { #endif - bool was_seed = t->is_seed(); - bool was_finished = picker.num_filtered() + t->num_pieces() - == t->torrent_file().num_pieces(); - // did we just finish the piece? if (picker.is_piece_finished(p.piece)) { #ifndef NDEBUG check_postcondition post_checker2_(t, false); #endif - bool verified = t->verify_piece(p.piece); - if (verified) - { - // the following call may cause picker to become invalid - // in case we just became a seed - t->announce_piece(p.piece); - assert(t->valid_metadata()); - // if we just became a seed, picker is now invalid, since it - // is deallocated by the torrent once it starts seeding - if (!was_finished - && (t->is_seed() - || picker.num_filtered() + t->num_pieces() - == t->torrent_file().num_pieces())) - { - // torrent finished - // i.e. all the pieces we're interested in have - // been downloaded. Release the files (they will open - // in read only mode if needed) - try { t->finished(); } - catch (std::exception& e) - { -#ifndef NDEBUG - std::cerr << e.what() << std::endl; - assert(false); -#endif - } - } - } - else - { - t->piece_failed(p.piece); - } - -#ifndef NDEBUG - try - { -#endif - - pol.piece_finished(p.piece, verified); - -#ifndef NDEBUG - } - catch (std::exception const& e) - { - std::cerr << e.what() << std::endl; - assert(false); - } -#endif - -#ifndef NDEBUG - try - { -#endif - - if (!was_seed && t->is_seed()) - { - assert(verified); - t->completed(); - } - -#ifndef NDEBUG - } - catch (std::exception const& e) - { - std::cerr << e.what() << std::endl; - assert(false); - } -#endif - + t->async_verify_piece(p.piece, bind(&torrent::piece_finished, t + , p.piece, _1)); } #ifndef NDEBUG @@ -1350,7 +1328,7 @@ namespace libtorrent assert(block.piece_index < t->torrent_file().num_pieces()); assert(block.block_index >= 0); assert(block.block_index < t->torrent_file().piece_size(block.piece_index)); - assert(!t->picker().is_downloading(block)); + assert(!t->picker().is_requested(block)); piece_picker::piece_state_t state; peer_speed_t speed = peer_speed(); @@ -1376,7 +1354,7 @@ namespace libtorrent assert(block.piece_index < t->torrent_file().num_pieces()); assert(block.block_index >= 0); assert(block.block_index < t->torrent_file().piece_size(block.piece_index)); - assert(t->picker().is_downloading(block)); + assert(t->picker().is_requested(block)); t->picker().abort_download(block); @@ -1603,6 +1581,8 @@ namespace libtorrent void peer_connection::disconnect() { + session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); + boost::intrusive_ptr me(this); INVARIANT_CHECK; @@ -1753,12 +1733,14 @@ namespace libtorrent p.failcount = peer_info_struct()->failcount; p.num_hashfails = peer_info_struct()->hashfails; p.flags |= peer_info_struct()->on_parole ? peer_info::on_parole : 0; + p.remote_dl_rate = m_remote_dl_rate; } else { p.source = 0; p.failcount = 0; p.num_hashfails = 0; + p.remote_dl_rate = 0; } p.send_buffer_size = send_buffer_size(); @@ -1868,8 +1850,11 @@ namespace libtorrent m_assume_fifo = true; - request_a_block(*t, *this); - send_block_requests(); + if (!has_peer_choked()) + { + request_a_block(*t, *this); + send_block_requests(); + } } } @@ -1915,6 +1900,21 @@ namespace libtorrent , m_upload_limit)); } + // update once every minute + if (now - m_remote_dl_update >= seconds(60)) + { + float factor = 0.6666666666667f; + + if (m_remote_dl_rate == 0) factor = 0.0f; + + m_remote_dl_rate = + (m_remote_dl_rate * factor) + + ((m_remote_bytes_dled * (1.0f-factor)) / 60.f); + + m_remote_bytes_dled = 0; + m_remote_dl_update = now; + } + fill_send_buffer(); /* size_type diff = share_diff(); @@ -1970,7 +1970,7 @@ namespace libtorrent else if (buffer_size_watermark > 80 * 1024) buffer_size_watermark = 80 * 1024; while (!m_requests.empty() - && (send_buffer_size() < buffer_size_watermark) + && (send_buffer_size() + m_reading_bytes < buffer_size_watermark) && !m_choked) { assert(t->valid_metadata()); @@ -1982,16 +1982,12 @@ namespace libtorrent assert(r.start + r.length <= t->torrent_file().piece_size(r.piece)); assert(r.length > 0 && r.start >= 0); - write_piece(r); - -#ifdef TORRENT_VERBOSE_LOGGING - (*m_logger) << time_now_string() - << " ==> PIECE [ piece: " << r.piece << " | s: " << r.start - << " | l: " << r.length << " ]\n"; -#endif + t->filesystem().async_read(r, bind(&peer_connection::on_disk_read_complete + , self(), _1, _2, r)); + m_reading_bytes += r.length; m_requests.erase(m_requests.begin()); - +/* if (m_requests.empty() && m_num_invalid_requests > 0 && is_peer_interested() @@ -2004,9 +2000,49 @@ namespace libtorrent send_choke(); send_unchoke(); } +*/ } } + void peer_connection::on_disk_read_complete(int ret, disk_io_job const& j, peer_request r) + { + session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); + + m_reading_bytes -= r.length; + + if (ret != r.length || m_torrent.expired()) + { + boost::shared_ptr t = m_torrent.lock(); + if (!t) + { + m_ses.connection_failed(m_socket, remote(), j.str.c_str()); + return; + } + + if (t->alerts().should_post(alert::fatal)) + { + std::string err = "torrent paused: disk read error"; + if (!j.str.empty()) + { + err += ", "; + err += j.str; + } + t->alerts().post_alert(file_error_alert(t->get_handle(), err)); + } + t->pause(); + return; + } + +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << time_now_string() + << " ==> PIECE [ piece: " << r.piece << " | s: " << r.start + << " | l: " << r.length << " ]\n"; +#endif + + write_piece(r, j.buffer); + setup_send(); + } + void peer_connection::assign_bandwidth(int channel, int amount) { session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); @@ -2599,11 +2635,13 @@ namespace libtorrent time_duration d2; d1 = now - m_became_uninterested; d2 = now - m_became_uninteresting; - // TODO: these timeouts should be user settable + time_duration time_limit = seconds( + m_ses.settings().inactivity_timeout); + if (!m_interesting && !m_peer_interested - && d1 > minutes(10) - && d2 > minutes(10)) + && d1 > time_limit + && d2 > time_limit) { return true; } @@ -2663,3 +2701,4 @@ namespace libtorrent } } + diff --git a/libtorrent/src/piece_picker.cpp b/libtorrent/src/piece_picker.cpp index cf6eb4a0e..bd568210f 100755 --- a/libtorrent/src/piece_picker.cpp +++ b/libtorrent/src/piece_picker.cpp @@ -88,8 +88,9 @@ namespace libtorrent // pieces is a bitmask with the pieces we have void piece_picker::files_checked( - const std::vector& pieces - , const std::vector& unfinished) + std::vector const& pieces + , std::vector const& unfinished + , std::vector& verify_pieces) { #ifndef NDEBUG m_files_checked_called = true; @@ -118,14 +119,12 @@ namespace libtorrent tcp::endpoint peer; for (int j = 0; j < m_blocks_per_piece; ++j) { - if (i->info[j].finished) + if (i->info[j].state == block_info::state_finished) mark_as_finished(piece_block(i->index, j), peer); } if (is_piece_finished(i->index)) { - // TODO: handle this case by verifying the - // piece and either accept it or discard it - assert(false); + verify_pieces.push_back(i->index); } } } @@ -212,8 +211,7 @@ namespace libtorrent for (int i = 0; i < m_blocks_per_piece; ++i) { ret.info[i].num_downloads = 0; - ret.info[i].requested = 0; - ret.info[i].finished = 0; + ret.info[i].state = block_info::state_none; ret.info[i].peer = tcp::endpoint(); } return ret; @@ -258,25 +256,28 @@ namespace libtorrent int num_blocks = blocks_in_piece(i->index); int num_requested = 0; int num_finished = 0; + int num_writing = 0; for (int k = 0; k < num_blocks; ++k) { - if (i->info[k].finished) + if (i->info[k].state == block_info::state_finished) { ++num_finished; - assert(i->info[k].requested); - ++num_requested; continue; } - if (i->info[k].requested) + if (i->info[k].state == block_info::state_requested) { ++num_requested; blocks_requested = true; } + if (i->info[k].state == block_info::state_writing) + { + ++num_writing; + } } assert(blocks_requested == (i->state != none)); assert(num_requested == i->requested); + assert(num_writing == i->writing); assert(num_finished == i->finished); - assert(num_finished <= num_requested); } @@ -1070,8 +1071,7 @@ namespace libtorrent for (int j = 0; j < num_blocks_in_piece; ++j) { piece_picker::block_info const& info = p.info[j]; - if ((info.finished == 1 - || info.requested == 1) + if (info.state != piece_picker::block_info::state_none && info.peer != peer && info.peer != tcp::endpoint()) { @@ -1089,6 +1089,9 @@ namespace libtorrent , int num_blocks, bool prefer_whole_pieces , tcp::endpoint peer, piece_state_t speed) const { + // if we have less than 1% of the pieces, ignore speed priorities and just try + // to finish any downloading piece + bool ignore_speed_categories = (m_num_have * 100 / m_piece_map.size()) < 1; for (std::vector::const_iterator i = piece_list.begin(); i != piece_list.end(); ++i) { @@ -1098,10 +1101,6 @@ namespace libtorrent // if the peer doesn't have the piece // skip it if (!pieces[*i]) continue; - - // if we have less than 1% of the pieces, ignore speed priorities and just try - // to finish any downloading piece - bool ignore_speed_categories = (m_num_have * 100 / m_piece_map.size()) < 1; int num_blocks_in_piece = blocks_in_piece(*i); @@ -1130,8 +1129,10 @@ namespace libtorrent for (int j = 0; j < num_blocks_in_piece; ++j) { block_info const& info = p->info[j]; - if (info.finished) continue; - if (info.requested + if (info.state == block_info::state_finished + || info.state == block_info::state_writing) + continue; + if (info.state == block_info::state_requested && info.peer == peer) continue; backup_blocks.push_back(piece_block(*i, j)); } @@ -1142,9 +1143,13 @@ namespace libtorrent { // ignore completed blocks block_info const& info = p->info[j]; - if (info.finished) continue; + if (info.state == block_info::state_finished + || info.state == block_info::state_writing) + continue; // ignore blocks requested from this peer already - if (info.requested && info.peer == peer) continue; + if (info.state == block_info::state_requested + && info.peer == peer) + continue; // if the piece is fast and the peer is slow, or vice versa, // add the block as a backup. // override this behavior if all the other blocks @@ -1169,7 +1174,7 @@ namespace libtorrent // blocks that have not been requested from any // other peer. interesting_blocks.push_back(piece_block(*i, j)); - if (p->info[j].requested == 0) + if (p->info[j].state == block_info::state_none) { // we have found a block that's free to download num_blocks--; @@ -1217,11 +1222,18 @@ namespace libtorrent int max_blocks = blocks_in_piece(index); if ((int)i->finished < max_blocks) return false; - assert((int)i->requested == max_blocks); +#ifndef NDEBUG + for (int k = 0; k < max_blocks; ++k) + { + assert(i->info[k].state == block_info::state_finished); + } +#endif + + assert((int)i->finished == max_blocks); return true; } - bool piece_picker::is_downloading(piece_block block) const + bool piece_picker::is_requested(piece_block block) const { assert(block.piece_index >= 0); assert(block.block_index >= 0); @@ -1235,7 +1247,22 @@ namespace libtorrent , has_index(block.piece_index)); assert(i != m_downloads.end()); - return i->info[block.block_index].requested; + return i->info[block.block_index].state == block_info::state_requested; + } + + bool piece_picker::is_downloaded(piece_block block) const + { + assert(block.piece_index >= 0); + assert(block.block_index >= 0); + assert(block.piece_index < (int)m_piece_map.size()); + + if (m_piece_map[block.piece_index].index == piece_pos::we_have_index) return true; + if (m_piece_map[block.piece_index].downloading == 0) return false; + std::vector::const_iterator i + = std::find_if(m_downloads.begin(), m_downloads.end(), has_index(block.piece_index)); + assert(i != m_downloads.end()); + return i->info[block.block_index].state == block_info::state_finished + || i->info[block.block_index].state == block_info::state_writing; } bool piece_picker::is_finished(piece_block block) const @@ -1249,7 +1276,7 @@ namespace libtorrent std::vector::const_iterator i = std::find_if(m_downloads.begin(), m_downloads.end(), has_index(block.piece_index)); assert(i != m_downloads.end()); - return i->info[block.block_index].finished; + return i->info[block.block_index].state == block_info::state_finished; } @@ -1274,7 +1301,7 @@ namespace libtorrent dp.state = state; dp.index = block.piece_index; block_info& info = dp.info[block.block_index]; - info.requested = 1; + info.state = block_info::state_requested; info.peer = peer; ++dp.requested; } @@ -1284,15 +1311,26 @@ namespace libtorrent = std::find_if(m_downloads.begin(), m_downloads.end(), has_index(block.piece_index)); assert(i != m_downloads.end()); block_info& info = i->info[block.block_index]; - assert(info.requested == 0); + assert(info.state == block_info::state_none); info.peer = peer; - info.requested = 1; + info.state = block_info::state_requested; ++i->requested; if (i->state == none) i->state = state; } } - void piece_picker::mark_as_finished(piece_block block, const tcp::endpoint& peer) + void piece_picker::get_availability(std::vector& avail) const + { + TORRENT_PIECE_PICKER_INVARIANT_CHECK; + + avail.resize(m_piece_map.size()); + std::vector::iterator j = avail.begin(); + for (std::vector::const_iterator i = m_piece_map.begin() + , end(m_piece_map.end()); i != end; ++i, ++j) + *j = i->peer_count; + } + + void piece_picker::mark_as_writing(piece_block block, tcp::endpoint const& peer) { TORRENT_PIECE_PICKER_INVARIANT_CHECK; @@ -1302,10 +1340,11 @@ namespace libtorrent assert(block.block_index < blocks_in_piece(block.piece_index)); piece_pos& p = m_piece_map[block.piece_index]; - int prio = p.priority(m_sequenced_download_threshold); + assert(p.downloading); - if (p.downloading == 0) +/* if (p.downloading == 0) { + int prio = p.priority(m_sequenced_download_threshold); p.downloading = 1; if (prio > 0) move(prio, p.index); else assert(p.priority(m_sequenced_download_threshold) == 0); @@ -1314,25 +1353,84 @@ namespace libtorrent dp.state = none; dp.index = block.piece_index; block_info& info = dp.info[block.block_index]; - info.requested = 1; - info.finished = 1; - ++dp.requested; - ++dp.finished; - dp.info[block.block_index].peer = peer; + info.peer = peer; + if (info.state == block_info::state_requested) --dp.requested; + assert(dp.requested >= 0); + assert (info.state != block_info::state_finished); + assert (info.state != block_info::state_writing); + if (info.state != block_info::state_requested) ++dp.writing; + info.state = block_info::state_writing; + } + else +*/ { + std::vector::iterator i + = std::find_if(m_downloads.begin(), m_downloads.end(), has_index(block.piece_index)); + assert(i != m_downloads.end()); + block_info& info = i->info[block.block_index]; + info.peer == peer; + assert(info.state == block_info::state_requested); + if (info.state == block_info::state_requested) --i->requested; + assert(i->requested >= 0); + assert (info.state != block_info::state_writing); + ++i->writing; + info.state = block_info::state_writing; + + if (i->requested == 0) + { + // there are no blocks requested in this piece. + // remove the fast/slow state from it + i->state = none; + } + } + } + + void piece_picker::mark_as_finished(piece_block block, tcp::endpoint const& peer) + { + assert(block.piece_index >= 0); + assert(block.block_index >= 0); + assert(block.piece_index < (int)m_piece_map.size()); + assert(block.block_index < blocks_in_piece(block.piece_index)); + + piece_pos& p = m_piece_map[block.piece_index]; + + if (p.downloading == 0) + { + TORRENT_PIECE_PICKER_INVARIANT_CHECK; + + assert(peer == tcp::endpoint()); + int prio = p.priority(m_sequenced_download_threshold); + p.downloading = 1; + if (prio > 0) move(prio, p.index); + else assert(p.priority(m_sequenced_download_threshold) == 0); + + downloading_piece& dp = add_download_piece(); + dp.state = none; + dp.index = block.piece_index; + block_info& info = dp.info[block.block_index]; + info.peer = peer; + assert(info.state == block_info::state_none); +// if (info.state == block_info::state_writing) --dp.writing; +// assert(dp.writing >= 0); + if (info.state != block_info::state_finished) ++dp.finished; + info.state = block_info::state_finished; } else { + TORRENT_PIECE_PICKER_INVARIANT_CHECK; + std::vector::iterator i = std::find_if(m_downloads.begin(), m_downloads.end(), has_index(block.piece_index)); assert(i != m_downloads.end()); block_info& info = i->info[block.block_index]; info.peer = peer; - if (!info.requested) ++i->requested; - info.requested = 1; - if (!info.finished) ++i->finished; - info.finished = 1; + assert(info.state == block_info::state_writing + || peer == tcp::endpoint()); + if (info.state == block_info::state_writing) --i->writing; + assert(i->writing >= 0); + ++i->finished; + info.state = block_info::state_finished; - if (i->requested == i->finished) + if (i->requested == 0) { // there are no blocks requested in this piece. // remove the fast/slow state from it @@ -1367,8 +1465,7 @@ namespace libtorrent assert(block.block_index >= 0); - if (i->info[block.block_index].requested == false - || i->info[block.block_index].requested == true) + if (i->info[block.block_index].state == block_info::state_none) return boost::optional(); return boost::optional(i->info[block.block_index].peer); @@ -1394,17 +1491,17 @@ namespace libtorrent , m_downloads.end(), has_index(block.piece_index)); assert(i != m_downloads.end()); - if (i->info[block.block_index].finished) + if (i->info[block.block_index].state == block_info::state_finished + || i->info[block.block_index].state == block_info::state_writing) { - assert(i->info[block.block_index].requested); return; } assert(block.block_index < blocks_in_piece(block.piece_index)); - assert(i->info[block.block_index].requested); + assert(i->info[block.block_index].state == block_info::state_requested); // clear this block as being downloaded - i->info[block.block_index].requested = false; + i->info[block.block_index].state = block_info::state_none; --i->requested; // clear the downloader of this block @@ -1412,7 +1509,7 @@ namespace libtorrent // if there are no other blocks in this piece // that's being downloaded, remove it from the list - if (i->requested == 0) + if (i->requested + i->finished + i->writing == 0) { erase_download_piece(i); piece_pos& p = m_piece_map[block.piece_index]; @@ -1423,7 +1520,7 @@ namespace libtorrent assert(std::find_if(m_downloads.begin(), m_downloads.end() , has_index(block.piece_index)) == m_downloads.end()); } - else if (i->requested == i->finished) + else if (i->requested == 0) { // there are no blocks requested in this piece. // remove the fast/slow state from it diff --git a/libtorrent/src/policy.cpp b/libtorrent/src/policy.cpp index 61bdd5748..eca5ba613 100755 --- a/libtorrent/src/policy.cpp +++ b/libtorrent/src/policy.cpp @@ -142,7 +142,19 @@ namespace bool operator()(policy::peer const& p) const { return p.ip.address() == m_ip.address(); } - tcp::endpoint m_ip; + tcp::endpoint const& m_ip; + }; + + struct match_peer_id + { + match_peer_id(peer_id const& id_) + : m_id(id_) + {} + + bool operator()(policy::peer const& p) const + { return p.connection && p.connection->pid() == m_id; } + + peer_id const& m_id; }; struct match_peer_connection @@ -152,9 +164,13 @@ namespace {} bool operator()(policy::peer const& p) const - { return p.connection == &m_conn; } + { + return p.connection == &m_conn + || (p.ip == m_conn.remote() + && p.type == policy::peer::connectable); + } - const peer_connection& m_conn; + peer_connection const& m_conn; }; @@ -230,7 +246,7 @@ namespace libtorrent for (std::vector::iterator i = interesting_pieces.begin(); i != interesting_pieces.end(); ++i) { - if (p.is_downloading(*i)) + if (p.is_requested(*i)) { busy_pieces.push_back(*i); continue; @@ -524,6 +540,8 @@ namespace libtorrent int max_failcount = m_torrent->settings().max_failcount; int min_reconnect_time = m_torrent->settings().min_reconnect_time; + aux::session_impl& ses = m_torrent->session(); + for (iterator i = m_peers.begin(); i != m_peers.end(); ++i) { if (i->connection) continue; @@ -533,6 +551,8 @@ namespace libtorrent if (i->failcount >= max_failcount) continue; if (now - i->connected < seconds(i->failcount * min_reconnect_time)) continue; + if (ses.m_port_filter.access(i->ip.port()) & port_filter::blocked) + continue; assert(i->connected <= now); @@ -846,12 +866,13 @@ namespace libtorrent --m_num_unchoked; } while (m_num_unchoked > m_torrent->m_uploads_quota.given); } - else + // this should prevent the choke/unchoke + // problem, since it will not unchoke unless + // there actually are any choked peers + else if (count_choked() > 0) { // optimistic unchoke. trade the 'worst' // unchoked peer with one of the choked - // TODO: This rotation should happen - // far less frequent than this! assert(m_num_unchoked <= m_torrent->num_peers()); iterator p = find_unchoke_candidate(); if (p != m_peers.end()) @@ -871,6 +892,22 @@ namespace libtorrent } } + int policy::count_choked() const + { + int ret = 0; + for (const_iterator i = m_peers.begin(); + i != m_peers.end(); ++i) + { + if (!i->connection + || i->connection->is_connecting() + || i->connection->is_disconnecting() + || !i->connection->is_peer_interested()) + continue; + if (i->connection->is_choked()) ++ret; + } + return ret; + } + void policy::new_connection(peer_connection& c) { assert(!c.is_local()); @@ -902,7 +939,10 @@ namespace libtorrent if (m_torrent->settings().allow_multiple_connections_per_ip) { - i = m_peers.end(); + i = std::find_if( + m_peers.begin() + , m_peers.end() + , match_peer_connection(c)); } else { @@ -968,13 +1008,29 @@ namespace libtorrent if(remote.address() == address() || remote.port() == 0) return; + aux::session_impl& ses = m_torrent->session(); + + port_filter const& pf = ses.m_port_filter; + if (pf.access(remote.port()) & port_filter::blocked) + { + if (ses.m_alerts.should_post(alert::info)) + { + ses.m_alerts.post_alert(peer_blocked_alert(remote.address() + , "outgoing port blocked, peer not added to peer list")); + } + return; + } + try { iterator i; if (m_torrent->settings().allow_multiple_connections_per_ip) { - i = m_peers.end(); + i = std::find_if( + m_peers.begin() + , m_peers.end() + , match_peer_id(pid)); } else { @@ -986,8 +1042,6 @@ namespace libtorrent if (i == m_peers.end()) { - aux::session_impl& ses = m_torrent->session(); - // if the IP is blocked, don't add it if (ses.m_ip_filter.access(remote.address()) & ip_filter::blocked) { @@ -1095,18 +1149,6 @@ namespace libtorrent } } - // TODO: we must be able to get interested - // in a peer again, if a piece fails that - // this peer has. - void policy::block_finished(peer_connection& c, piece_block) - { - INVARIANT_CHECK; - - // if the peer hasn't choked us, ask for another piece - if (!c.has_peer_choked() && !m_torrent->is_seed()) - request_a_block(*m_torrent, c); - } - // this is called when we are unchoked by a peer // i.e. a peer lets us know that we will receive // data from now on @@ -1167,6 +1209,7 @@ namespace libtorrent c.add_free_upload(-diff); } } +/* if (!c.is_choked()) { c.send_choke(); @@ -1175,6 +1218,7 @@ namespace libtorrent if (m_torrent->is_seed()) seed_unchoke_one_peer(); else unchoke_one_peer(); } +*/ } bool policy::unchoke_one_peer() diff --git a/libtorrent/src/session.cpp b/libtorrent/src/session.cpp index 45711b122..485c90d62 100755 --- a/libtorrent/src/session.cpp +++ b/libtorrent/src/session.cpp @@ -143,10 +143,20 @@ namespace libtorrent m_impl->set_ip_filter(f); } + void session::set_port_filter(port_filter const& f) + { + m_impl->set_port_filter(f); + } + void session::set_peer_id(peer_id const& id) { m_impl->set_peer_id(id); } + + peer_id session::id() const + { + return m_impl->get_peer_id(); + } void session::set_key(int key) { @@ -167,7 +177,7 @@ namespace libtorrent // if the torrent already exists, this will throw duplicate_torrent torrent_handle session::add_torrent( torrent_info const& ti - , boost::filesystem::path const& save_path + , fs::path const& save_path , entry const& resume_data , bool compact_mode , int block_size @@ -181,7 +191,7 @@ namespace libtorrent char const* tracker_url , sha1_hash const& info_hash , char const* name - , boost::filesystem::path const& save_path + , fs::path const& save_path , entry const& e , bool compact_mode , int block_size @@ -372,6 +382,36 @@ namespace libtorrent m_impl->set_severity_level(s); } + void session::start_lsd() + { + m_impl->start_lsd(); + } + + void session::start_natpmp() + { + m_impl->start_natpmp(); + } + + void session::start_upnp() + { + m_impl->start_upnp(); + } + + void session::stop_lsd() + { + m_impl->stop_lsd(); + } + + void session::stop_natpmp() + { + m_impl->stop_natpmp(); + } + + void session::stop_upnp() + { + m_impl->stop_upnp(); + } + connection_queue& session::get_connection_queue() { return m_impl->m_half_open; diff --git a/libtorrent/src/session_impl.cpp b/libtorrent/src/session_impl.cpp index 9244255bc..1a744ab44 100755 --- a/libtorrent/src/session_impl.cpp +++ b/libtorrent/src/session_impl.cpp @@ -81,7 +81,11 @@ using boost::bind; using boost::mutex; using libtorrent::aux::session_impl; -namespace libtorrent { namespace detail +namespace libtorrent { + +namespace fs = boost::filesystem; + +namespace detail { std::string generate_auth_string(std::string const& user @@ -412,7 +416,6 @@ namespace libtorrent { namespace detail for (std::deque >::iterator i = m_processing.begin(); i != m_processing.end(); ++i) { - if ((*i)->info_hash == info_hash) return i->get(); } @@ -494,13 +497,6 @@ namespace libtorrent { namespace detail , m_dht_same_port(true) , m_external_udp_port(0) #endif - , m_natpmp(m_io_service, m_listen_interface.address() - , bind(&session_impl::on_port_mapping, this, _1, _2, _3)) - , m_upnp(m_io_service, m_half_open, m_listen_interface.address() - , m_settings.user_agent - , bind(&session_impl::on_port_mapping, this, _1, _2, _3)) - , m_lsd(m_io_service, m_listen_interface.address() - , bind(&session_impl::on_lsd_peer, this, _1, _2)) , m_timer(m_io_service) , m_next_connect_torrent(0) , m_checker_impl(*this) @@ -589,6 +585,12 @@ namespace libtorrent { namespace detail m_checker_impl.m_abort = true; } + void session_impl::set_port_filter(port_filter const& f) + { + mutex_t::scoped_lock l(m_mutex); + m_port_filter = f; + } + void session_impl::set_ip_filter(ip_filter const& f) { mutex_t::scoped_lock l(m_mutex); @@ -600,13 +602,9 @@ namespace libtorrent { namespace detail = m_connections.begin(); i != m_connections.end();) { tcp::endpoint sender; - try { - sender = i->first->remote_endpoint(); - } catch (asio::system_error& e) { - i++; - continue; - } - if (m_ip_filter.access(sender.address()) & ip_filter::blocked) + try { sender = i->first->remote_endpoint(); } + catch (std::exception&) { sender = i->second->remote(); } + if (m_ip_filter.access(sender.address()) & ip_filter::blocked) { #if defined(TORRENT_VERBOSE_LOGGING) (*i->second->m_logger) << "*** CONNECTION FILTERED\n"; @@ -630,6 +628,9 @@ namespace libtorrent { namespace detail mutex_t::scoped_lock l(m_mutex); assert(s.connection_speed > 0); assert(s.file_pool_size > 0); + + // less than 5 seconds unchoke interval is insane + assert(s.unchoke_interval >= 5); m_settings = s; m_files.resize(m_settings.file_pool_size); // replace all occurances of '\n' with ' '. @@ -1087,8 +1088,10 @@ namespace libtorrent { namespace detail { session_impl::mutex_t::scoped_lock l(m_mutex); open_listen_port(); - m_natpmp.set_mappings(m_listen_interface.port(), 0); - m_upnp.set_mappings(m_listen_interface.port(), 0); + if (m_natpmp.get()) + m_natpmp->set_mappings(m_listen_interface.port(), 0); + if (m_upnp.get()) + m_upnp->set_mappings(m_listen_interface.port(), 0); } ptime timer = time_now(); @@ -1113,8 +1116,10 @@ namespace libtorrent { namespace detail deadline_timer tracker_timer(m_io_service); // this will remove the port mappings - m_natpmp.close(); - m_upnp.close(); + if (m_natpmp.get()) + m_natpmp->close(); + if (m_upnp.get()) + m_upnp->close(); #if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) (*m_logger) << time_now_string() << " locking mutex\n"; @@ -1276,7 +1281,7 @@ namespace libtorrent { namespace detail torrent_handle session_impl::add_torrent( torrent_info const& ti - , boost::filesystem::path const& save_path + , fs::path const& save_path , entry const& resume_data , bool compact_mode , int block_size @@ -1367,7 +1372,7 @@ namespace libtorrent { namespace detail char const* tracker_url , sha1_hash const& info_hash , char const* name - , boost::filesystem::path const& save_path + , fs::path const& save_path , entry const& , bool compact_mode , int block_size @@ -1524,13 +1529,18 @@ namespace libtorrent { namespace detail if (new_listen_address) { - m_natpmp.rebind(new_interface.address()); - m_upnp.rebind(new_interface.address()); - m_lsd.rebind(new_interface.address()); + if (m_natpmp.get()) + m_natpmp->rebind(new_interface.address()); + if (m_upnp.get()) + m_upnp->rebind(new_interface.address()); + if (m_lsd.get()) + m_lsd->rebind(new_interface.address()); } - m_natpmp.set_mappings(m_listen_interface.port(), 0); - m_upnp.set_mappings(m_listen_interface.port(), 0); + if (m_natpmp.get()) + m_natpmp->set_mappings(m_listen_interface.port(), 0); + if (m_upnp.get()) + m_upnp->set_mappings(m_listen_interface.port(), 0); #ifndef TORRENT_DISABLE_DHT if ((new_listen_address || m_dht_same_port) && m_dht) @@ -1540,8 +1550,10 @@ namespace libtorrent { namespace detail // the listen interface changed, rebind the dht listen socket as well m_dht->rebind(new_interface.address() , m_dht_settings.service_port); - m_natpmp.set_mappings(0, m_dht_settings.service_port); - m_upnp.set_mappings(0, m_dht_settings.service_port); + if (m_natpmp.get()) + m_natpmp->set_mappings(0, m_dht_settings.service_port); + if (m_upnp.get()) + m_upnp->set_mappings(0, m_dht_settings.service_port); } #endif @@ -1563,7 +1575,8 @@ namespace libtorrent { namespace detail { mutex_t::scoped_lock l(m_mutex); // use internal listen port for local peers - m_lsd.announce(ih, m_listen_interface.port()); + if (m_lsd.get()) + m_lsd->announce(ih, m_listen_interface.port()); } void session_impl::on_lsd_peer(tcp::endpoint peer, sha1_hash const& ih) @@ -1683,8 +1696,10 @@ namespace libtorrent { namespace detail m_dht_settings.service_port = m_listen_interface.port(); } m_external_udp_port = m_dht_settings.service_port; - m_natpmp.set_mappings(0, m_dht_settings.service_port); - m_upnp.set_mappings(0, m_dht_settings.service_port); + if (m_natpmp.get()) + m_natpmp->set_mappings(0, m_dht_settings.service_port); + if (m_upnp.get()) + m_upnp->set_mappings(0, m_dht_settings.service_port); m_dht = new dht::dht_tracker(m_io_service , m_dht_settings, m_listen_interface.address() , startup_state); @@ -1714,8 +1729,10 @@ namespace libtorrent { namespace detail { m_dht->rebind(m_listen_interface.address() , settings.service_port); - m_natpmp.set_mappings(0, m_dht_settings.service_port); - m_upnp.set_mappings(0, m_dht_settings.service_port); + if (m_natpmp.get()) + m_natpmp->set_mappings(0, m_dht_settings.service_port); + if (m_upnp.get()) + m_upnp->set_mappings(0, m_dht_settings.service_port); m_external_udp_port = settings.service_port; } m_dht_settings = settings; @@ -1896,6 +1913,69 @@ namespace libtorrent { namespace detail mutex_t::scoped_lock l(m_mutex); return m_dl_bandwidth_manager.throttle(); } + + + void session_impl::start_lsd() + { + mutex_t::scoped_lock l(m_mutex); + m_lsd.reset(new lsd(m_io_service + , m_listen_interface.address() + , bind(&session_impl::on_lsd_peer, this, _1, _2))); + } + + void session_impl::start_natpmp() + { + mutex_t::scoped_lock l(m_mutex); + m_natpmp.reset(new natpmp(m_io_service + , m_listen_interface.address() + , bind(&session_impl::on_port_mapping + , this, _1, _2, _3))); + + m_natpmp->set_mappings(m_listen_interface.port(), +#ifndef TORRENT_DISABLE_DHT + m_dht ? m_dht_settings.service_port : +#endif + 0); + } + + void session_impl::start_upnp() + { + mutex_t::scoped_lock l(m_mutex); + m_upnp.reset(new upnp(m_io_service, m_half_open + , m_listen_interface.address() + , m_settings.user_agent + , bind(&session_impl::on_port_mapping + , this, _1, _2, _3))); + + m_upnp->set_mappings(m_listen_interface.port(), +#ifndef TORRENT_DISABLE_DHT + m_dht ? m_dht_settings.service_port : +#endif + 0); + } + + void session_impl::stop_lsd() + { + mutex_t::scoped_lock l(m_mutex); + m_lsd.reset(); + } + + void session_impl::stop_natpmp() + { + mutex_t::scoped_lock l(m_mutex); + if (m_natpmp.get()) + m_natpmp->close(); + m_natpmp.reset(); + } + + void session_impl::stop_upnp() + { + mutex_t::scoped_lock l(m_mutex); + if (m_upnp.get()) + m_upnp->close(); + m_upnp.reset(); + } + #ifndef NDEBUG void session_impl::check_invariant(const char *place) @@ -2033,12 +2113,13 @@ namespace libtorrent { namespace detail for (int j = 0; j < num_bitmask_bytes; ++j) { unsigned char bits = bitmask[j]; - for (int k = 0; k < 8; ++k) + int num_bits = std::min(num_blocks_per_piece - j*8, 8); + for (int k = 0; k < num_bits; ++k) { const int bit = j * 8 + k; if (bits & (1 << k)) { - p.info[bit].finished = true; + p.info[bit].state = piece_picker::block_info::state_finished; ++p.finished; } } diff --git a/libtorrent/src/storage.cpp b/libtorrent/src/storage.cpp index 0623dfeb6..f0f2241c0 100755 --- a/libtorrent/src/storage.cpp +++ b/libtorrent/src/storage.cpp @@ -127,7 +127,7 @@ namespace using namespace boost::filesystem; // based on code from Boost.Fileystem - bool create_directories_win(const path& ph) + bool create_directories_win(const fs::path& ph) { if (ph.empty() || exists(ph)) { @@ -147,7 +147,7 @@ namespace return true; } - bool exists_win( const path & ph ) + bool exists_win( const fs::path & ph ) { std::wstring wpath(safe_convert(ph.string())); if(::GetFileAttributes( wpath.c_str() ) == 0xFFFFFFFF) @@ -167,7 +167,7 @@ namespace return true; } - boost::intmax_t file_size_win( const path & ph ) + boost::intmax_t file_size_win( const fs::path & ph ) { std::wstring wpath(safe_convert(ph.string())); // by now, intmax_t is 64-bits on all Windows compilers @@ -187,7 +187,7 @@ namespace + fad.nFileSizeLow; } - std::time_t last_write_time_win( const path & ph ) + std::time_t last_write_time_win( const fs::path & ph ) { struct _stat path_stat; std::wstring wph(safe_convert(ph.native_file_string())); @@ -198,8 +198,8 @@ namespace return path_stat.st_mtime; } - void rename_win( const path & old_path, - const path & new_path ) + void rename_win( const fs::path & old_path, + const fs::path & new_path ) { std::wstring wold_path(safe_convert(old_path.string())); std::wstring wnew_path(safe_convert(new_path.string())); @@ -214,14 +214,13 @@ namespace #endif #if BOOST_VERSION < 103200 -bool operator<(boost::filesystem::path const& lhs - , boost::filesystem::path const& rhs) +bool operator<(fs::path const& lhs, fs::path const& rhs) { return lhs.string() < rhs.string(); } #endif -using namespace boost::filesystem; +namespace fs = boost::filesystem; using boost::bind; using namespace ::boost::multi_index; using boost::multi_index::multi_index_container; @@ -244,7 +243,7 @@ namespace libtorrent { std::vector > get_filesizes( - torrent_info const& t, path p) + torrent_info const& t, fs::path p) { p = complete(p); std::vector > sizes; @@ -255,11 +254,16 @@ namespace libtorrent std::time_t time = 0; try { - path f = p / i->path; #if defined(_WIN32) && defined(UNICODE) && BOOST_VERSION < 103400 + fs::path f = p / i->path; size = file_size_win(f); time = last_write_time_win(f); +#elif defined(_WIN32) && defined(UNICODE) + fs::wpath f = safe_convert((p / i->path).string()); + size = file_size(f); + time = last_write_time(f); #else + fs::path f = p / i->path; size = file_size(f); time = last_write_time(f); #endif @@ -278,7 +282,7 @@ namespace libtorrent // still be a correct subset of the actual data on disk. bool match_filesizes( torrent_info const& t - , path p + , fs::path p , std::vector > const& sizes , bool compact_mode , std::string* error) @@ -299,17 +303,22 @@ namespace libtorrent std::time_t time = 0; try { - path f = p / i->path; #if defined(_WIN32) && defined(UNICODE) && BOOST_VERSION < 103400 + fs::path f = p / i->path; size = file_size_win(f); time = last_write_time_win(f); +#elif defined(_WIN32) && defined(UNICODE) + fs::wpath f = safe_convert((p / i->path).string()); + size = file_size(f); + time = last_write_time(f); #else + fs::path f = p / i->path; size = file_size(f); time = last_write_time(f); #endif } catch (std::exception&) {} - if (size != s->first + if ((compact_mode && size != s->first) || (!compact_mode && size < s->first)) { if (error) *error = "filesize mismatch for file '" @@ -319,7 +328,7 @@ namespace libtorrent + " bytes"; return false; } - if (time != s->second + if ((compact_mode && time != s->second) || (!compact_mode && time < s->second)) { if (error) *error = "timestamp mismatch for file '" @@ -371,19 +380,19 @@ namespace libtorrent class storage : public storage_interface, thread_safe_storage, boost::noncopyable { public: - storage(torrent_info const& info, path const& path, file_pool& fp) + storage(torrent_info const& info, fs::path const& path, file_pool& fp) : thread_safe_storage(info.num_pieces()) , m_info(info) , m_files(fp) { assert(info.begin_files() != info.end_files()); - m_save_path = complete(path); + m_save_path = fs::complete(path); assert(m_save_path.is_complete()); } void release_files(); void initialize(bool allocate_files); - bool move_storage(path save_path); + bool move_storage(fs::path save_path); size_type read(char* buf, int slot, int offset, int size); void write(const char* buf, int slot, int offset, int size); void move_slot(int src_slot, int dst_slot); @@ -391,6 +400,7 @@ namespace libtorrent void swap_slots3(int slot1, int slot2, int slot3); bool verify_resume_data(entry& rd, std::string& error); void write_resume_data(entry& rd) const; + sha1_hash hash_for_slot(int slot, partial_hash& ph, int piece_size); size_type read_impl(char* buf, int slot, int offset, int size, bool fill_zero); @@ -400,7 +410,7 @@ namespace libtorrent } torrent_info const& m_info; - path m_save_path; + fs::path m_save_path; // the file pool is typically stored in // the session, to make all storage // instances use the same pool @@ -410,23 +420,53 @@ namespace libtorrent std::vector m_scratch_buffer; }; + sha1_hash storage::hash_for_slot(int slot, partial_hash& ph, int piece_size) + { +#ifndef NDEBUG + hasher partial; + hasher whole; + int slot_size1 = piece_size; + m_scratch_buffer.resize(slot_size1); + read_impl(&m_scratch_buffer[0], slot, 0, slot_size1, true); + if (ph.offset > 0) + partial.update(&m_scratch_buffer[0], ph.offset); + whole.update(&m_scratch_buffer[0], slot_size1); + hasher partial_copy = ph.h; + assert(ph.offset == 0 || partial_copy.final() == partial.final()); +#endif + int slot_size = piece_size - ph.offset; + if (slot_size == 0) return ph.h.final(); + m_scratch_buffer.resize(slot_size); + read_impl(&m_scratch_buffer[0], slot, ph.offset, slot_size, true); + ph.h.update(&m_scratch_buffer[0], slot_size); + sha1_hash ret = ph.h.final(); + assert(whole.final() == ret); + return ret; + } + void storage::initialize(bool allocate_files) { // first, create all missing directories - path last_path; + fs::path last_path; for (torrent_info::file_iterator file_iter = m_info.begin_files(), end_iter = m_info.end_files(); file_iter != end_iter; ++file_iter) { - path dir = (m_save_path / file_iter->path).branch_path(); + fs::path dir = (m_save_path / file_iter->path).branch_path(); if (dir != last_path) { - last_path = dir; #if defined(_WIN32) && defined(UNICODE) && BOOST_VERSION < 103400 + last_path = dir; if (!exists_win(last_path)) create_directories_win(last_path); +#elif defined(_WIN32) && defined(UNICODE) + last_path = dir; + fs::wpath wp = safe_convert(last_path.string()); + if (!exists(wp)) + create_directories(wp); #else + last_path = dir; if (!exists(last_path)) create_directories(last_path); #endif @@ -537,23 +577,30 @@ namespace libtorrent } // returns true on success - bool storage::move_storage(path save_path) + bool storage::move_storage(fs::path save_path) { - path old_path; - path new_path; +#if defined(_WIN32) && defined(UNICODE) && BOOST_VERSION >= 103400 + fs::wpath old_path; + fs::wpath new_path; +#else + fs::path old_path; + fs::path new_path; +#endif save_path = complete(save_path); #if defined(_WIN32) && defined(UNICODE) && BOOST_VERSION < 103400 std::wstring wsave_path(safe_convert(save_path.native_file_string())); if (!exists_win(save_path)) - { CreateDirectory(wsave_path.c_str(), 0); - } else if ((GetFileAttributes(wsave_path.c_str()) & FILE_ATTRIBUTE_DIRECTORY) == 0) - { return false; - } +#elif defined(_WIN32) && defined(UNICODE) + fs::wpath wp = safe_convert(save_path.string()); + if (!exists(wp)) + create_directory(wp); + else if (!is_directory(wp)) + return false; #else if (!exists(save_path)) create_directory(save_path); @@ -563,13 +610,19 @@ namespace libtorrent m_files.release(this); +#if defined(_WIN32) && defined(UNICODE) && BOOST_VERSION >= 103400 + old_path = safe_convert((m_save_path / m_info.name()).string()); + new_path = safe_convert((save_path / m_info.name()).string()); +#else old_path = m_save_path / m_info.name(); new_path = save_path / m_info.name(); +#endif try { #if defined(_WIN32) && defined(UNICODE) && BOOST_VERSION < 103400 rename_win(old_path, new_path); + rename(old_path, new_path); #else rename(old_path, new_path); #endif @@ -772,7 +825,7 @@ namespace libtorrent // this file was empty, don't increment the slice counter if (read_bytes > 0) ++counter; #endif - path path = m_save_path / file_iter->path; + fs::path path = m_save_path / file_iter->path; file_offset = 0; in = m_files.open_file( @@ -820,7 +873,7 @@ namespace libtorrent assert(file_iter != m_info.end_files()); } - path p(m_save_path / file_iter->path); + fs::path p(m_save_path / file_iter->path); boost::shared_ptr out = m_files.open_file( this, p, file::out | file::in); @@ -884,13 +937,13 @@ namespace libtorrent if (left_to_write > 0) { - #ifndef NDEBUG +#ifndef NDEBUG if (write_bytes > 0) ++counter; - #endif +#endif ++file_iter; assert(file_iter != m_info.end_files()); - path p = m_save_path / file_iter->path; + fs::path p = m_save_path / file_iter->path; file_offset = 0; out = m_files.open_file( this, p, file::out | file::in); @@ -901,12 +954,12 @@ namespace libtorrent } storage_interface* default_storage_constructor(torrent_info const& ti - , boost::filesystem::path const& path, file_pool& fp) + , fs::path const& path, file_pool& fp) { return new storage(ti, path, fp); } - bool supports_sparse_files(path const& p) + bool supports_sparse_files(fs::path const& p) { assert(p.is_complete()); #if defined(_WIN32) @@ -930,7 +983,7 @@ namespace libtorrent #if defined(__APPLE__) || defined(__linux__) // find the last existing directory of the save path - path query_path = p; + fs::path query_path = p; while (!query_path.empty() && !exists(query_path)) query_path = query_path.branch_path(); #endif @@ -1004,215 +1057,141 @@ namespace libtorrent // -- piece_manager ----------------------------------------------------- - class piece_manager::impl - { - friend class invariant_access; - public: - - impl( - torrent_info const& info - , path const& path - , file_pool& fp - , storage_constructor_type sc); - - bool check_fastresume( - aux::piece_checker_data& d - , std::vector& pieces - , int& num_pieces - , bool compact_mode); - - std::pair check_files( - std::vector& pieces - , int& num_pieces, boost::recursive_mutex& mutex); - - void release_files(); - - bool allocate_slots(int num_slots, bool abort_on_disk = false); - void mark_failed(int index); - unsigned long piece_crc( - int slot_index - , int block_size - , piece_picker::block_info const* bi); - - int slot_for_piece(int piece_index) const; - - size_type read( - char* buf - , int piece_index - , int offset - , int size); - - void write( - const char* buf - , int piece_index - , int offset - , int size); - - path const& save_path() const - { return m_save_path; } - - bool move_storage(path save_path) - { - if (m_storage->move_storage(save_path)) - { - m_save_path = complete(save_path); - return true; - } - return false; - } - - void export_piece_map(std::vector& p) const; - - // returns the slot currently associated with the given - // piece or assigns the given piece_index to a free slot - - int identify_data( - const std::vector& piece_data - , int current_slot - , std::vector& have_pieces - , int& num_pieces - , const std::multimap& hash_to_piece - , boost::recursive_mutex& mutex); - - int allocate_slot_for_piece(int piece_index); -#ifndef NDEBUG - void check_invariant() const; -#ifdef TORRENT_STORAGE_DEBUG - void debug_log() const; -#endif -#endif - boost::scoped_ptr m_storage; - - // if this is true, pieces are always allocated at the - // lowest possible slot index. If it is false, pieces - // are always written to their final place immediately - bool m_compact_mode; - - // if this is true, pieces that haven't been downloaded - // will be filled with zeroes. Not filling with zeroes - // will not work in some cases (where a seek cannot pass - // the end of the file). - bool m_fill_mode; - - // a bitmask representing the pieces we have - std::vector m_have_piece; - - torrent_info const& m_info; - - // slots that haven't had any file storage allocated - std::vector m_unallocated_slots; - // slots that have file storage, but isn't assigned to a piece - std::vector m_free_slots; - - enum - { - has_no_slot = -3 // the piece has no storage - }; - - // maps piece indices to slots. If a piece doesn't - // have any storage, it is set to 'has_no_slot' - std::vector m_piece_to_slot; - - enum - { - unallocated = -1, // the slot is unallocated - unassigned = -2 // the slot is allocated but not assigned to a piece - }; - - // maps slots to piece indices, if a slot doesn't have a piece - // it can either be 'unassigned' or 'unallocated' - std::vector m_slot_to_piece; - - path m_save_path; - - mutable boost::recursive_mutex m_mutex; - - bool m_allocating; - boost::mutex m_allocating_monitor; - boost::condition m_allocating_condition; - - // these states are used while checking/allocating the torrent - - enum { - // the default initial state - state_none, - // the file checking is complete - state_finished, - // creating the directories - state_create_files, - // checking the files - state_full_check, - // allocating files (in non-compact mode) - state_allocating - } m_state; - int m_current_slot; - - std::vector m_piece_data; - - // this maps a piece hash to piece index. It will be - // build the first time it is used (to save time if it - // isn't needed) - std::multimap m_hash_to_piece; - }; - - piece_manager::impl::impl( - torrent_info const& info - , path const& save_path + piece_manager::piece_manager( + boost::shared_ptr const& torrent + , torrent_info const& ti + , fs::path const& save_path , file_pool& fp + , disk_io_thread& io , storage_constructor_type sc) - : m_storage(sc(info, save_path, fp)) + : m_storage(sc(ti, save_path, fp)) , m_compact_mode(false) , m_fill_mode(true) - , m_info(info) + , m_info(ti) , m_save_path(complete(save_path)) , m_allocating(false) + , m_io_thread(io) + , m_torrent(torrent) { m_fill_mode = !supports_sparse_files(save_path); } - piece_manager::piece_manager( - torrent_info const& info - , path const& save_path - , file_pool& fp - , storage_constructor_type sc) - : m_pimpl(new impl(info, save_path, fp, sc)) - { - } - piece_manager::~piece_manager() { } void piece_manager::write_resume_data(entry& rd) const { - m_pimpl->m_storage->write_resume_data(rd); + m_storage->write_resume_data(rd); } bool piece_manager::verify_resume_data(entry& rd, std::string& error) { - return m_pimpl->m_storage->verify_resume_data(rd, error); + return m_storage->verify_resume_data(rd, error); } - void piece_manager::release_files() + void piece_manager::async_release_files( + boost::function const& handler) { - m_pimpl->release_files(); + disk_io_job j; + j.storage = this; + j.action = disk_io_job::release_files; + m_io_thread.add_job(j, handler); } - void piece_manager::impl::release_files() + void piece_manager::async_move_storage(fs::path const& p + , boost::function const& handler) { - // synchronization ------------------------------------------------------ - boost::recursive_mutex::scoped_lock lock(m_mutex); - // ---------------------------------------------------------------------- + disk_io_job j; + j.storage = this; + j.action = disk_io_job::move_storage; + j.str = p.string(); + m_io_thread.add_job(j, handler); + } + void piece_manager::async_read( + peer_request const& r + , boost::function const& handler) + { + disk_io_job j; + j.storage = this; + j.action = disk_io_job::read; + j.piece = r.piece; + j.offset = r.start; + j.buffer_size = r.length; + m_io_thread.add_job(j, handler); + } + + void piece_manager::async_write( + peer_request const& r + , char const* buffer + , boost::function const& handler) + { + assert(r.length <= 16 * 1024); + + disk_io_job j; + j.storage = this; + j.action = disk_io_job::write; + j.piece = r.piece; + j.offset = r.start; + j.buffer_size = r.length; + j.buffer = m_io_thread.allocate_buffer(); + if (j.buffer == 0) throw file_error("out of memory"); + std::memcpy(j.buffer, buffer, j.buffer_size); + m_io_thread.add_job(j, handler); + } + + void piece_manager::async_hash(int piece + , boost::function const& handler) + { + disk_io_job j; + j.storage = this; + j.action = disk_io_job::hash; + j.piece = piece; + + m_io_thread.add_job(j, handler); + } + + fs::path piece_manager::save_path() const + { + boost::recursive_mutex::scoped_lock l(m_mutex); + return m_save_path; + } + + sha1_hash piece_manager::hash_for_piece_impl(int piece) + { + partial_hash ph; + + std::map::iterator i = m_piece_hasher.find(piece); + if (i != m_piece_hasher.end()) + { + ph = i->second; + m_piece_hasher.erase(i); + } + + int slot = m_piece_to_slot[piece]; + assert(slot != has_no_slot); + return m_storage->hash_for_slot(slot, ph, m_info.piece_size(piece)); + } + + void piece_manager::release_files_impl() + { m_storage->release_files(); } - void piece_manager::impl::export_piece_map( + bool piece_manager::move_storage_impl(fs::path const& save_path) + { + if (m_storage->move_storage(save_path)) + { + m_save_path = fs::complete(save_path); + return true; + } + return false; + } + void piece_manager::export_piece_map( std::vector& p) const { - // synchronization ------------------------------------------------------ boost::recursive_mutex::scoped_lock lock(m_mutex); - // ---------------------------------------------------------------------- INVARIANT_CHECK; @@ -1232,20 +1211,9 @@ namespace libtorrent } } - bool piece_manager::compact_allocation() const - { return m_pimpl->m_compact_mode; } - - void piece_manager::export_piece_map( - std::vector& p) const + void piece_manager::mark_failed(int piece_index) { - m_pimpl->export_piece_map(p); - } - - void piece_manager::impl::mark_failed(int piece_index) - { - // synchronization ------------------------------------------------------ boost::recursive_mutex::scoped_lock lock(m_mutex); - // ---------------------------------------------------------------------- INVARIANT_CHECK; @@ -1261,37 +1229,13 @@ namespace libtorrent m_free_slots.push_back(slot_index); } - void piece_manager::mark_failed(int index) - { - m_pimpl->mark_failed(index); - } - - bool piece_manager::is_allocating() const - { - return m_pimpl->m_state - == impl::state_allocating; - } - int piece_manager::slot_for_piece(int piece_index) const - { - return m_pimpl->slot_for_piece(piece_index); - } - - int piece_manager::impl::slot_for_piece(int piece_index) const { assert(piece_index >= 0 && piece_index < m_info.num_pieces()); return m_piece_to_slot[piece_index]; } unsigned long piece_manager::piece_crc( - int index - , int block_size - , piece_picker::block_info const* bi) - { - return m_pimpl->piece_crc(index, block_size, bi); - } - - unsigned long piece_manager::impl::piece_crc( int slot_index , int block_size , piece_picker::block_info const* bi) @@ -1309,7 +1253,7 @@ namespace libtorrent for (int i = 0; i < num_blocks-1; ++i) { - if (!bi[i].finished) continue; + if (!bi[i].state == piece_picker::block_info::state_finished) continue; m_storage->read( &buf[0] , slot_index @@ -1317,7 +1261,7 @@ namespace libtorrent , block_size); crc.update(&buf[0], block_size); } - if (bi[num_blocks - 1].finished) + if (bi[num_blocks - 1].state == piece_picker::block_info::state_finished) { m_storage->read( &buf[0] @@ -1333,7 +1277,7 @@ namespace libtorrent return 0; } - size_type piece_manager::impl::read( + size_type piece_manager::read_impl( char* buf , int piece_index , int offset @@ -1350,16 +1294,7 @@ namespace libtorrent return m_storage->read(buf, slot, offset, size); } - size_type piece_manager::read( - char* buf - , int piece_index - , int offset - , int size) - { - return m_pimpl->read(buf, piece_index, offset, size); - } - - void piece_manager::impl::write( + void piece_manager::write_impl( const char* buf , int piece_index , int offset @@ -1369,21 +1304,34 @@ namespace libtorrent assert(offset >= 0); assert(size > 0); assert(piece_index >= 0 && piece_index < (int)m_piece_to_slot.size()); + + if (offset == 0) + { + partial_hash& ph = m_piece_hasher[piece_index]; + assert(ph.offset == 0); + ph.offset = size; + ph.h.update(buf, size); + } + else + { + std::map::iterator i = m_piece_hasher.find(piece_index); + if (i != m_piece_hasher.end()) + { + assert(i->second.offset > 0); + if (offset == i->second.offset) + { + i->second.offset += size; + i->second.h.update(buf, size); + } + } + } + int slot = allocate_slot_for_piece(piece_index); assert(slot >= 0 && slot < (int)m_slot_to_piece.size()); m_storage->write(buf, slot, offset, size); } - void piece_manager::write( - const char* buf - , int piece_index - , int offset - , int size) - { - m_pimpl->write(buf, piece_index, offset, size); - } - - int piece_manager::impl::identify_data( + int piece_manager::identify_data( const std::vector& piece_data , int current_slot , std::vector& have_pieces @@ -1447,6 +1395,8 @@ namespace libtorrent , matching_pieces.end() , current_slot) != matching_pieces.end()) { + // the current slot is among the matching pieces, so + // we will assume that the piece is in the right place const int piece_index = current_slot; // lock because we're writing to have_pieces @@ -1542,18 +1492,17 @@ namespace libtorrent // if it is, use it and return true. If it // isn't return false and the full check // will be run - bool piece_manager::impl::check_fastresume( + bool piece_manager::check_fastresume( aux::piece_checker_data& data , std::vector& pieces , int& num_pieces, bool compact_mode) { - assert(m_info.piece_length() > 0); - // synchronization ------------------------------------------------------ boost::recursive_mutex::scoped_lock lock(m_mutex); - // ---------------------------------------------------------------------- INVARIANT_CHECK; + assert(m_info.piece_length() > 0); + m_compact_mode = compact_mode; // This will corrupt the storage @@ -1613,45 +1562,77 @@ namespace libtorrent m_unallocated_slots.push_back(i); } - if (!m_compact_mode && !m_unallocated_slots.empty()) + if (m_unallocated_slots.empty()) { - m_state = state_allocating; + m_state = state_create_files; return false; } - else + + if (m_compact_mode) { - m_state = state_finished; - return true; + m_state = state_create_files; + return false; } } - m_state = state_create_files; + m_state = state_full_check; return false; } +/* + state chart: + + check_fastresume() + + | | + | v + | +------------+ + | | full_check | + | +------------+ + | | + | v + | +------------+ + | | allocating | + | +------------+ + | | + | v + | +--------------+ + |->| create_files | + +--------------+ + | + v + +----------+ + | finished | + +----------+ +*/ + + // performs the full check and full allocation // (if necessary). returns true if finished and // false if it should be called again // the second return value is the progress the // file check is at. 0 is nothing done, and 1 // is finished - std::pair piece_manager::impl::check_files( + std::pair piece_manager::check_files( std::vector& pieces, int& num_pieces, boost::recursive_mutex& mutex) { assert(num_pieces == std::count(pieces.begin(), pieces.end(), true)); if (m_state == state_allocating) { - if (m_compact_mode) + if (m_compact_mode || m_unallocated_slots.empty()) { - m_state = state_finished; - return std::make_pair(true, 1.f); + m_state = state_create_files; + return std::make_pair(false, 1.f); } - if (m_unallocated_slots.empty()) + if (int(m_unallocated_slots.size()) == m_info.num_pieces() + && !m_fill_mode) { - m_state = state_finished; - return std::make_pair(true, 1.f); + // if there is not a single file on disk, just + // create the files + m_state = state_create_files; + return std::make_pair(false, 1.f); } // if we're not in compact mode, make sure the @@ -1682,10 +1663,18 @@ namespace libtorrent { m_storage->initialize(!m_fill_mode && !m_compact_mode); - m_current_slot = 0; - m_state = state_full_check; - m_piece_data.resize(int(m_info.piece_length())); - return std::make_pair(false, 0.f); + if (!m_unallocated_slots.empty() && !m_compact_mode) + { + assert(!m_fill_mode); + std::vector().swap(m_unallocated_slots); + std::fill(m_slot_to_piece.begin(), m_slot_to_piece.end(), int(unassigned)); + m_free_slots.resize(m_info.num_pieces()); + for (int i = 0; i < m_info.num_pieces(); ++i) + m_free_slots[i] = i; + } + + m_state = state_finished; + return std::make_pair(true, 1.f); } assert(m_state == state_full_check); @@ -1696,7 +1685,18 @@ namespace libtorrent try { + // initialization for the full check + if (m_hash_to_piece.empty()) + { + m_current_slot = 0; + for (int i = 0; i < m_info.num_pieces(); ++i) + { + m_hash_to_piece.insert(std::make_pair(m_info.hash_for_piece(i), i)); + } + std::fill(pieces.begin(), pieces.end(), false); + } + m_piece_data.resize(int(m_info.piece_length())); int piece_size = int(m_info.piece_size(m_current_slot)); int num_read = m_storage->read(&m_piece_data[0] , m_current_slot, 0, piece_size); @@ -1705,14 +1705,6 @@ namespace libtorrent if (num_read != piece_size) throw file_error(""); - if (m_hash_to_piece.empty()) - { - for (int i = 0; i < m_info.num_pieces(); ++i) - { - m_hash_to_piece.insert(std::make_pair(m_info.hash_for_piece(i), i)); - } - } - int piece_index = identify_data(m_piece_data, m_current_slot , pieces, num_pieces, m_hash_to_piece, mutex); @@ -1949,26 +1941,9 @@ namespace libtorrent return std::make_pair(false, (float)m_current_slot / m_info.num_pieces()); } - bool piece_manager::check_fastresume( - aux::piece_checker_data& d, std::vector& pieces - , int& num_pieces, bool compact_mode) + int piece_manager::allocate_slot_for_piece(int piece_index) { - return m_pimpl->check_fastresume(d, pieces, num_pieces, compact_mode); - } - - std::pair piece_manager::check_files( - std::vector& pieces - , int& num_pieces - , boost::recursive_mutex& mutex) - { - return m_pimpl->check_files(pieces, num_pieces, mutex); - } - - int piece_manager::impl::allocate_slot_for_piece(int piece_index) - { - // synchronization ------------------------------------------------------ boost::recursive_mutex::scoped_lock lock(m_mutex); - // ---------------------------------------------------------------------- // INVARIANT_CHECK; @@ -2075,56 +2050,11 @@ namespace libtorrent return slot_index; } - namespace - { - // this is used to notify potential other - // threads that the allocation-function has exited - struct allocation_syncronization - { - allocation_syncronization( - bool& flag - , boost::condition& cond - , boost::mutex& monitor) - : m_flag(flag) - , m_cond(cond) - , m_monitor(monitor) - { - boost::mutex::scoped_lock lock(m_monitor); - - while (m_flag) - m_cond.wait(lock); - - m_flag = true; - } - - ~allocation_syncronization() - { - boost::mutex::scoped_lock lock(m_monitor); - m_flag = false; - m_cond.notify_one(); - } - - bool& m_flag; - boost::condition& m_cond; - boost::mutex& m_monitor; - }; - - } - - bool piece_manager::impl::allocate_slots(int num_slots, bool abort_on_disk) + bool piece_manager::allocate_slots(int num_slots, bool abort_on_disk) { assert(num_slots > 0); - // this object will syncronize the allocation with - // potential other threads - allocation_syncronization sync_obj( - m_allocating - , m_allocating_condition - , m_allocating_monitor); - - // synchronization ------------------------------------------------------ boost::recursive_mutex::scoped_lock lock(m_mutex); - // ---------------------------------------------------------------------- // INVARIANT_CHECK; @@ -2175,27 +2105,10 @@ namespace libtorrent return written; } - bool piece_manager::allocate_slots(int num_slots, bool abort_on_disk) - { - return m_pimpl->allocate_slots(num_slots, abort_on_disk); - } - - path const& piece_manager::save_path() const - { - return m_pimpl->save_path(); - } - - bool piece_manager::move_storage(path const& save_path) - { - return m_pimpl->move_storage(save_path); - } - #ifndef NDEBUG - void piece_manager::impl::check_invariant() const + void piece_manager::check_invariant() const { - // synchronization ------------------------------------------------------ boost::recursive_mutex::scoped_lock lock(m_mutex); - // ---------------------------------------------------------------------- if (m_piece_to_slot.empty()) return; assert((int)m_piece_to_slot.size() == m_info.num_pieces()); @@ -2303,7 +2216,7 @@ namespace libtorrent } #ifdef TORRENT_STORAGE_DEBUG - void piece_manager::impl::debug_log() const + void piece_manager::debug_log() const { std::stringstream s; diff --git a/libtorrent/src/torrent.cpp b/libtorrent/src/torrent.cpp index f50ad0b01..2ac6d1895 100755 --- a/libtorrent/src/torrent.cpp +++ b/libtorrent/src/torrent.cpp @@ -77,7 +77,6 @@ using namespace libtorrent; using boost::tuples::tuple; using boost::tuples::get; using boost::tuples::make_tuple; -using boost::filesystem::complete; using boost::bind; using boost::mutex; using libtorrent::aux::session_impl; @@ -147,11 +146,12 @@ namespace namespace libtorrent { + torrent::torrent( session_impl& ses , aux::checker_impl& checker , torrent_info const& tf - , boost::filesystem::path const& save_path + , fs::path const& save_path , tcp::endpoint const& net_interface , bool compact_mode , int block_size @@ -174,7 +174,9 @@ namespace libtorrent , m_resolve_countries(false) #endif , m_announce_timer(ses.m_io_service) +#ifndef TORRENT_DISABLE_DHT , m_last_dht_announce(time_now() - minutes(15)) +#endif , m_policy() , m_ses(ses) , m_checker(checker) @@ -202,8 +204,6 @@ namespace libtorrent m_initial_done = 0; #endif - INVARIANT_CHECK; - m_uploads_quota.min = 2; m_connections_quota.min = 2; // this will be corrected the next time the main session @@ -212,7 +212,6 @@ namespace libtorrent m_uploads_quota.max = std::numeric_limits::max(); m_connections_quota.max = std::numeric_limits::max(); m_policy.reset(new policy(this)); - init(); } @@ -222,7 +221,7 @@ namespace libtorrent , char const* tracker_url , sha1_hash const& info_hash , char const* name - , boost::filesystem::path const& save_path + , fs::path const& save_path , tcp::endpoint const& net_interface , bool compact_mode , int block_size @@ -245,7 +244,9 @@ namespace libtorrent , m_resolve_countries(false) #endif , m_announce_timer(ses.m_io_service) +#ifndef TORRENT_DISABLE_DHT , m_last_dht_announce(time_now() - minutes(15)) +#endif , m_policy() , m_ses(ses) , m_checker(checker) @@ -295,6 +296,7 @@ namespace libtorrent void torrent::start() { boost::weak_ptr self(shared_from_this()); + if (m_torrent_file.is_valid()) init(); m_announce_timer.expires_from_now(seconds(1)); m_announce_timer.async_wait(m_ses.m_strand.wrap( bind(&torrent::on_announce_disp, self, _1))); @@ -327,8 +329,7 @@ namespace libtorrent INVARIANT_CHECK; - if (m_ses.is_aborted()) - m_abort = true; + assert(m_abort); if (!m_connections.empty()) disconnect_all(); } @@ -347,17 +348,20 @@ namespace libtorrent } #endif + // this may not be called from a constructor because of the call to + // shared_from_this() void torrent::init() { - INVARIANT_CHECK; - assert(m_torrent_file.is_valid()); assert(m_torrent_file.num_files() > 0); assert(m_torrent_file.total_size() >= 0); m_have_pieces.resize(m_torrent_file.num_pieces(), false); - m_storage.reset(new piece_manager(m_torrent_file, m_save_path - , m_ses.m_files, m_storage_constructor)); + // the shared_from_this() will create an intentional + // cycle of ownership, se the hpp file for description. + m_owning_storage = new piece_manager(shared_from_this(), m_torrent_file + , m_save_path, m_ses.m_files, m_ses.m_disk_thread, m_storage_constructor); + m_storage = m_owning_storage.get(); m_block_size = calculate_block_size(m_torrent_file, m_default_block_size); m_picker.reset(new piece_picker( static_cast(m_torrent_file.piece_length() / m_block_size) @@ -558,7 +562,6 @@ namespace libtorrent bind(&torrent::on_peer_name_lookup, shared_from_this(), _1, _2, i->pid))); } } - m_policy->pulse(); if (m_ses.m_alerts.should_post(alert::info)) { @@ -687,7 +690,7 @@ namespace libtorrent int corr = 0; int index = i->index; assert(!m_have_pieces[index]); - assert(i->finished < m_picker->blocks_in_piece(index)); + assert(i->finished <= m_picker->blocks_in_piece(index)); #ifndef NDEBUG for (std::vector::const_iterator j = boost::next(i); @@ -699,17 +702,17 @@ namespace libtorrent for (int j = 0; j < blocks_per_piece; ++j) { - assert(i->info[j].finished == 0 || i->info[j].finished == 1); - assert(m_picker->is_finished(piece_block(index, j)) == i->info[j].finished); - corr += i->info[j].finished * m_block_size; + assert(m_picker->is_finished(piece_block(index, j)) == (i->info[j].state == piece_picker::block_info::state_finished)); + corr += (i->info[j].state == piece_picker::block_info::state_finished) * m_block_size; assert(index != last_piece || j < m_picker->blocks_in_last_piece() - || i->info[j].finished == 0); + || i->info[j].state != piece_picker::block_info::state_finished); } // correction if this was the last piece // and if we have the last block if (i->index == last_piece - && i->info[m_picker->blocks_in_last_piece()-1].finished) + && i->info[m_picker->blocks_in_last_piece()-1].state + == piece_picker::block_info::state_finished) { corr -= m_block_size; corr += m_torrent_file.piece_size(last_piece) % m_block_size; @@ -719,8 +722,8 @@ namespace libtorrent wanted_done += corr; } - assert(total_done < m_torrent_file.total_size()); - assert(wanted_done < m_torrent_file.total_size()); + assert(total_done <= m_torrent_file.total_size()); + assert(wanted_done <= m_torrent_file.total_size()); std::map downloading_piece; for (const_peer_iterator i = begin(); i != end(); ++i) @@ -784,7 +787,7 @@ namespace libtorrent std::cerr << " " << i->index << " "; for (int j = 0; j < blocks_per_piece; ++j) { - std::cerr << i->info[j].finished; + std::cerr << (i->info[j].state == piece_picker::block_info::state_finished ? "1" : "0"); } std::cerr << std::endl; } @@ -800,8 +803,8 @@ namespace libtorrent } - assert(total_done < m_torrent_file.total_size()); - assert(wanted_done < m_torrent_file.total_size()); + assert(total_done <= m_torrent_file.total_size()); + assert(wanted_done <= m_torrent_file.total_size()); #endif @@ -809,6 +812,84 @@ namespace libtorrent return make_tuple(total_done, wanted_done); } + void torrent::piece_finished(int index, bool passed_hash_check) + { + session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); + + bool was_seed = is_seed(); + bool was_finished = m_picker->num_filtered() + num_pieces() + == torrent_file().num_pieces(); + + if (passed_hash_check) + { + // the following call may cause picker to become invalid + // in case we just became a seed + announce_piece(index); + assert(valid_metadata()); + // if we just became a seed, picker is now invalid, since it + // is deallocated by the torrent once it starts seeding + if (!was_finished + && (is_seed() + || m_picker->num_filtered() + num_pieces() + == torrent_file().num_pieces())) + { + // torrent finished + // i.e. all the pieces we're interested in have + // been downloaded. Release the files (they will open + // in read only mode if needed) + try { finished(); } + catch (std::exception& e) + { +#ifndef NDEBUG + std::cerr << e.what() << std::endl; + assert(false); +#endif + } + } + } + else + { + piece_failed(index); + } + +#ifndef NDEBUG + try + { +#endif + + m_policy->piece_finished(index, passed_hash_check); + +#ifndef NDEBUG + } + catch (std::exception const& e) + { + std::cerr << e.what() << std::endl; + assert(false); + } +#endif + +#ifndef NDEBUG + try + { +#endif + + if (!was_seed && is_seed()) + { + assert(passed_hash_check); + completed(); + } + +#ifndef NDEBUG + } + catch (std::exception const& e) + { + std::cerr << e.what() << std::endl; + assert(false); + } +#endif + + } + void torrent::piece_failed(int index) { // if the last piece fails the peer connection will still @@ -818,7 +899,8 @@ namespace libtorrent // (total_done == m_torrent_file.total_size()) => is_seed() // INVARIANT_CHECK; - assert(m_storage.get()); + assert(m_storage); + assert(m_storage->refcount() > 0); assert(m_picker.get()); assert(index >= 0); assert(index < m_torrent_file.num_pieces()); @@ -907,6 +989,7 @@ namespace libtorrent // start with redownloading the pieces that the client // that has sent the least number of pieces m_picker->restore_piece(index); + assert(m_storage); m_storage->mark_failed(index); assert(m_have_pieces[index] == false); @@ -924,7 +1007,19 @@ namespace libtorrent // disconnect all peers and close all // files belonging to the torrents disconnect_all(); - if (m_storage.get()) m_storage->release_files(); + if (m_owning_storage.get()) m_storage->async_release_files( + bind(&torrent::on_files_released, shared_from_this(), _1, _2)); + m_owning_storage = 0; + } + + void torrent::on_files_released(int ret, disk_io_job const& j) + { + session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); + + if (alerts().should_post(alert::warning)) + { + alerts().post_alert(torrent_paused_alert(get_handle(), "torrent paused")); + } } void torrent::announce_piece(int index) @@ -981,7 +1076,19 @@ namespace libtorrent return m_username + ":" + m_password; } + void torrent::piece_availability(std::vector& avail) const + { + INVARIANT_CHECK; + assert(valid_metadata()); + if (is_seed()) + { + avail.clear(); + return; + } + + m_picker->get_availability(avail); + } void torrent::set_piece_priority(int index, int priority) { @@ -1713,8 +1820,13 @@ namespace libtorrent void torrent::set_metadata(entry const& metadata) { + INVARIANT_CHECK; + + assert(!m_torrent_file.is_valid()); m_torrent_file.parse_info_section(metadata); + init(); + boost::mutex::scoped_lock(m_checker.m_mutex); boost::shared_ptr d( @@ -1928,7 +2040,9 @@ namespace libtorrent std::for_each(seeds.begin(), seeds.end() , bind(&peer_connection::disconnect, _1)); - m_storage->release_files(); + assert(m_storage); + m_storage->async_release_files( + bind(&torrent::on_files_released, shared_from_this(), _1, _2)); } // called when torrent is complete (all pieces downloaded) @@ -2004,17 +2118,12 @@ namespace libtorrent { INVARIANT_CHECK; - if (!m_storage.get()) - { - // this means we have received the metadata through the - // metadata extension, and we have to initialize - init(); - } - - assert(m_storage.get()); + assert(valid_metadata()); bool done = true; try { + assert(m_storage); + assert(m_owning_storage.get()); done = m_storage->check_fastresume(data, m_have_pieces, m_num_pieces , m_compact_mode); } @@ -2043,11 +2152,12 @@ namespace libtorrent { INVARIANT_CHECK; - assert(m_storage.get()); + assert(m_owning_storage.get()); std::pair progress(true, 1.f); try { + assert(m_storage); progress = m_storage->check_files(m_have_pieces, m_num_pieces , m_ses.m_mutex); } @@ -2082,9 +2192,19 @@ namespace libtorrent if (!is_seed()) { - m_picker->files_checked(m_have_pieces, unfinished_pieces); + // this is filled in with pieces that needs to be checked + // against its hashes. + std::vector verify_pieces; + m_picker->files_checked(m_have_pieces, unfinished_pieces, verify_pieces); if (m_sequenced_download_threshold > 0) picker().set_sequenced_download_threshold(m_sequenced_download_threshold); + while (!verify_pieces.empty()) + { + int piece = verify_pieces.back(); + verify_pieces.pop_back(); + async_verify_piece(piece, bind(&torrent::piece_finished + , shared_from_this(), piece, _1)); + } } #ifndef TORRENT_DISABLE_EXTENSIONS @@ -2137,34 +2257,45 @@ namespace libtorrent return m_ses.m_alerts; } - boost::filesystem::path torrent::save_path() const + fs::path torrent::save_path() const { - return m_save_path; + if (m_owning_storage.get()) + return m_owning_storage->save_path(); + else + return m_save_path; } - bool torrent::move_storage(boost::filesystem::path const& save_path) + void torrent::move_storage(fs::path const& save_path) { INVARIANT_CHECK; - bool ret = true; - if (m_storage.get()) + if (m_owning_storage.get()) { - ret = m_storage->move_storage(save_path); - m_save_path = m_storage->save_path(); + m_owning_storage->async_move_storage(save_path + , bind(&torrent::on_storage_moved, shared_from_this(), _1, _2)); } else { m_save_path = save_path; } - return ret; + } + + void torrent::on_storage_moved(int ret, disk_io_job const& j) + { + session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); + + if (alerts().should_post(alert::warning)) + { + alerts().post_alert(storage_moved_alert(get_handle(), j.str)); + } } piece_manager& torrent::filesystem() { INVARIANT_CHECK; - assert(m_storage.get()); - return *m_storage; + assert(m_owning_storage.get()); + return *m_owning_storage; } @@ -2198,11 +2329,11 @@ namespace libtorrent if (valid_metadata()) { - assert(int(m_have_pieces.size()) == m_torrent_file.num_pieces()); + assert(m_abort || int(m_have_pieces.size()) == m_torrent_file.num_pieces()); } else { - assert(m_have_pieces.empty()); + assert(m_abort || m_have_pieces.empty()); } size_type total_done = quantized_bytes_done(); @@ -2321,7 +2452,14 @@ namespace libtorrent m_just_paused = true; // this will make the storage close all // files and flush all cached data - if (m_storage.get()) m_storage->release_files(); + if (m_owning_storage.get()) + { + assert(m_storage); + // TOOD: add a callback which posts + // an alert for the client to sync. with + m_storage->async_release_files( + bind(&torrent::on_files_released, shared_from_this(), _1, _2)); + } } void torrent::resume() @@ -2448,33 +2586,31 @@ namespace libtorrent m_time_scaler--; if (m_time_scaler <= 0) { - m_time_scaler = 10; + m_time_scaler = settings().unchoke_interval; m_policy->pulse(); } } - bool torrent::verify_piece(int piece_index) + void torrent::async_verify_piece(int piece_index, boost::function const& f) { -// INVARIANT_CHECK; + INVARIANT_CHECK; - assert(m_storage.get()); + assert(m_storage); + assert(m_storage->refcount() > 0); assert(piece_index >= 0); assert(piece_index < m_torrent_file.num_pieces()); assert(piece_index < (int)m_have_pieces.size()); - int size = static_cast(m_torrent_file.piece_size(piece_index)); - std::vector buffer(size); - assert(size > 0); - m_storage->read(&buffer[0], piece_index, 0, size); + m_storage->async_hash(piece_index, bind(&torrent::on_piece_verified + , shared_from_this(), _1, _2, f)); + } - hasher h; - h.update(&buffer[0], size); - sha1_hash digest = h.final(); - - if (m_torrent_file.hash_for_piece(piece_index) != digest) - return false; - - return true; + void torrent::on_piece_verified(int ret, disk_io_job const& j + , boost::function f) + { + sha1_hash h(j.str); + session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); + f(m_torrent_file.hash_for_piece(j.piece) == h); } const tcp::endpoint& torrent::current_tracker() const @@ -2483,7 +2619,7 @@ namespace libtorrent } bool torrent::is_allocating() const - { return m_storage.get() && m_storage->is_allocating(); } + { return m_owning_storage.get() && m_owning_storage->is_allocating(); } void torrent::file_progress(std::vector& fp) const { @@ -2533,8 +2669,8 @@ namespace libtorrent torrent_status st; st.num_peers = (int)std::count_if(m_connections.begin(), m_connections.end(), - boost::bind(std::logical_not(), boost::bind(&peer_connection::is_connecting, - boost::bind(&std::map::value_type::second, _1)))); + !boost::bind(&peer_connection::is_connecting + , boost::bind(&std::map::value_type::second, _1))); st.num_complete = m_complete; st.num_incomplete = m_incomplete; @@ -2635,7 +2771,6 @@ namespace libtorrent } else if (st.total_wanted_done == st.total_wanted) { - assert(st.total_done != m_torrent_file.total_size()); st.state = torrent_status::finished; } else @@ -2655,10 +2790,10 @@ namespace libtorrent { INVARIANT_CHECK; - return (int)std::count_if(m_connections.begin(), m_connections.end(), - boost::bind(&peer_connection::is_seed, - boost::bind(&std::map::value_type::second, _1))); + return (int)std::count_if(m_connections.begin(), m_connections.end() + , boost::bind(&peer_connection::is_seed + , boost::bind(&std::map::value_type::second, _1))); } void torrent::tracker_request_timed_out( diff --git a/libtorrent/src/torrent_handle.cpp b/libtorrent/src/torrent_handle.cpp index da571ab63..c9ade14e9 100755 --- a/libtorrent/src/torrent_handle.cpp +++ b/libtorrent/src/torrent_handle.cpp @@ -80,6 +80,8 @@ using libtorrent::aux::session_impl; namespace libtorrent { + namespace fs = boost::filesystem; + namespace { void throw_invalid_handle() @@ -205,12 +207,12 @@ namespace libtorrent , bind(&torrent::download_limit, _1)); } - bool torrent_handle::move_storage( - boost::filesystem::path const& save_path) const + void torrent_handle::move_storage( + fs::path const& save_path) const { INVARIANT_CHECK; - return call_member(m_ses, m_chk, m_info_hash + call_member(m_ses, m_chk, m_info_hash , bind(&torrent::move_storage, _1, save_path)); } @@ -352,6 +354,14 @@ namespace libtorrent } + void torrent_handle::piece_availability(std::vector& avail) const + { + INVARIANT_CHECK; + + call_member(m_ses, m_chk, m_info_hash + , bind(&torrent::piece_availability, _1, boost::ref(avail))); + } + void torrent_handle::piece_priority(int index, int priority) const { INVARIANT_CHECK; @@ -556,9 +566,12 @@ namespace libtorrent for (int j = 0; j < num_bitmask_bytes; ++j) { unsigned char v = 0; - for (int k = 0; k < 8; ++k) - v |= i->info[j*8+k].finished?(1 << k):0; + int bits = std::min(num_blocks_per_piece - j*8, 8); + for (int k = 0; k < bits; ++k) + v |= (i->info[j*8+k].state == piece_picker::block_info::state_finished) + ? (1 << k) : 0; bitmask.insert(bitmask.end(), v); + assert(bits == 8 || j == num_bitmask_bytes - 1); } piece_struct["bitmask"] = bitmask; @@ -608,11 +621,11 @@ namespace libtorrent } - boost::filesystem::path torrent_handle::save_path() const + fs::path torrent_handle::save_path() const { INVARIANT_CHECK; - return call_member(m_ses, m_chk, m_info_hash + return call_member(m_ses, m_chk, m_info_hash , bind(&torrent::save_path, _1)); } @@ -717,7 +730,7 @@ namespace libtorrent { peer_connection* peer = i->second; - // peers that haven't finished the handshake should + // incoming peers that haven't finished the handshake should // not be included in this list if (peer->associated_torrent().expired()) continue; @@ -760,10 +773,9 @@ namespace libtorrent pi.blocks_in_piece = p.blocks_in_piece(i->index); for (int j = 0; j < pi.blocks_in_piece; ++j) { - pi.peer[j] = i->info[j].peer; - pi.num_downloads[j] = i->info[j].num_downloads; - pi.finished_blocks[j] = i->info[j].finished; - pi.requested_blocks[j] = i->info[j].requested; + pi.blocks[j].peer = i->info[j].peer; + pi.blocks[j].num_downloads = i->info[j].num_downloads; + pi.blocks[j].state = i->info[j].state; } pi.piece_index = i->index; queue.push_back(pi); diff --git a/libtorrent/src/torrent_info.cpp b/libtorrent/src/torrent_info.cpp index e546a1243..4ea09aefd 100755 --- a/libtorrent/src/torrent_info.cpp +++ b/libtorrent/src/torrent_info.cpp @@ -62,10 +62,12 @@ namespace pt = boost::posix_time; namespace gr = boost::gregorian; using namespace libtorrent; -using namespace boost::filesystem; namespace { + + namespace fs = boost::filesystem; + void convert_to_utf8(std::string& str, unsigned char chr) { str += 0xc0 | ((chr & 0xff) >> 6); @@ -153,7 +155,7 @@ namespace // encoded string if (!valid_encoding) { - target.orig_path.reset(new path(target.path)); + target.orig_path.reset(new fs::path(target.path)); target.path = tmp_path; } } @@ -203,8 +205,8 @@ namespace offset += target.back().size; } } - - void remove_dir(path& p) +/* + void remove_dir(fs::path& p) { assert(p.begin() != p.end()); path tmp; @@ -212,6 +214,7 @@ namespace tmp /= *i; p = tmp; } +*/ } namespace libtorrent @@ -277,6 +280,29 @@ namespace libtorrent torrent_info::~torrent_info() {} + void torrent_info::swap(torrent_info& ti) + { + using std::swap; + m_urls.swap(ti.m_urls); + m_url_seeds.swap(ti.m_url_seeds); + swap(m_piece_length, ti.m_piece_length); + m_piece_hash.swap(ti.m_piece_hash); + m_files.swap(ti.m_files); + m_nodes.swap(ti.m_nodes); + swap(m_num_pieces, ti.m_num_pieces); + swap(m_info_hash, ti.m_info_hash); + m_name.swap(ti.m_name); + swap(m_creation_date, ti.m_creation_date); + m_comment.swap(ti.m_comment); + m_created_by.swap(ti.m_created_by); + swap(m_multifile, ti.m_multifile); + swap(m_private, ti.m_private); + m_extra_info.swap(ti.m_extra_info); +#ifndef NDEBUG + swap(m_half_metadata, ti.m_half_metadata); +#endif + } + void torrent_info::set_piece_size(int size) { // make sure the size is an even power of 2 @@ -323,7 +349,7 @@ namespace libtorrent else { m_name = info["name"].string(); } - path tmp = m_name; + fs::path tmp = m_name; if (tmp.is_complete()) throw std::runtime_error("torrent contains " "a file with an absolute path: '" + m_name + "'"); if (tmp.has_branch_path()) throw std::runtime_error( @@ -526,9 +552,9 @@ namespace libtorrent , bind(&announce_entry::tier, _1), bind(&announce_entry::tier, _2))); } - void torrent_info::add_file(boost::filesystem::path file, size_type size) + void torrent_info::add_file(fs::path file, size_type size) { - assert(file.begin() != file.end()); +// assert(file.begin() != file.end()); if (!file.has_branch_path()) { @@ -589,8 +615,6 @@ namespace libtorrent entry torrent_info::create_info_metadata() const { - namespace fs = boost::filesystem; - // you have to add files to the torrent first assert(!m_files.empty()); @@ -650,8 +674,6 @@ namespace libtorrent { assert(m_piece_length > 0); - namespace fs = boost::filesystem; - if ((m_urls.empty() && m_nodes.empty()) || m_files.empty()) { // TODO: throw something here diff --git a/libtorrent/src/tracker_manager.cpp b/libtorrent/src/tracker_manager.cpp index 7ee8c57ba..7bd511588 100755 --- a/libtorrent/src/tracker_manager.cpp +++ b/libtorrent/src/tracker_manager.cpp @@ -289,28 +289,6 @@ namespace libtorrent return ret; } - void intrusive_ptr_add_ref(timeout_handler const* c) - { - assert(c != 0); - assert(c->m_refs >= 0); - timeout_handler::mutex_t::scoped_lock l(c->m_mutex); - ++c->m_refs; - } - - void intrusive_ptr_release(timeout_handler const* c) - { - assert(c != 0); - assert(c->m_refs > 0); - timeout_handler::mutex_t::scoped_lock l(c->m_mutex); - --c->m_refs; - if (c->m_refs == 0) - { - l.unlock(); - delete c; - } - } - - timeout_handler::timeout_handler(asio::strand& str) : m_strand(str) , m_start_time(time_now()) @@ -318,7 +296,6 @@ namespace libtorrent , m_timeout(str.io_service()) , m_completion_timeout(0) , m_read_timeout(0) - , m_refs(0) {} void timeout_handler::set_timeout(int completion_timeout, int read_timeout) @@ -467,8 +444,19 @@ namespace libtorrent ++start; } - std::string::iterator port_pos - = std::find(start, url.end(), ':'); + std::string::iterator port_pos; + + // this is for IPv6 addresses + if (start != url.end() && *start == '[') + { + port_pos = std::find(start, url.end(), ']'); + if (port_pos == url.end()) throw std::runtime_error("invalid hostname syntax"); + port_pos = std::find(port_pos, url.end(), ':'); + } + else + { + port_pos = std::find(start, url.end(), ':'); + } if (port_pos < end) { diff --git a/libtorrent/src/udp_tracker_connection.cpp b/libtorrent/src/udp_tracker_connection.cpp index d992ca050..d08abd359 100755 --- a/libtorrent/src/udp_tracker_connection.cpp +++ b/libtorrent/src/udp_tracker_connection.cpp @@ -307,7 +307,10 @@ namespace libtorrent // event detail::write_int32(req.event, out); // ip address - detail::write_int32(0, out); + if (m_settings.announce_ip != address() && m_settings.announce_ip.is_v4()) + detail::write_uint32(m_settings.announce_ip.to_v4().to_ulong(), out); + else + detail::write_int32(0, out); // key detail::write_int32(req.key, out); // num_want diff --git a/setup.py b/setup.py index 3183e004f..70fb35bd0 100644 --- a/setup.py +++ b/setup.py @@ -160,6 +160,7 @@ deluge_core = Extension('deluge_core', 'libtorrent/src/bandwidth_manager.cpp', 'libtorrent/src/bt_peer_connection.cpp', 'libtorrent/src/connection_queue.cpp', + 'libtorrent/src/disk_io_thread.cpp', 'libtorrent/src/entry.cpp', 'libtorrent/src/escape_string.cpp', 'libtorrent/src/file.cpp', @@ -181,6 +182,7 @@ deluge_core = Extension('deluge_core', 'libtorrent/src/session.cpp', 'libtorrent/src/session_impl.cpp', 'libtorrent/src/sha1.cpp', + 'libtorrent/src/socks4_stream.cpp', 'libtorrent/src/socks5_stream.cpp', 'libtorrent/src/stat.cpp', 'libtorrent/src/storage.cpp', diff --git a/src/core.py b/src/core.py index dce7c897e..24e753e3b 100644 --- a/src/core.py +++ b/src/core.py @@ -71,7 +71,10 @@ PREF_FUNCTIONS = { "max_active_torrents" : None, # no need for a function, applied constantly "auto_seed_ratio" : None, # no need for a function, applied constantly "max_download_rate_bps" : deluge_core.set_download_rate_limit, - "max_upload_rate_bps" : deluge_core.set_upload_rate_limit + "max_upload_rate_bps" : deluge_core.set_upload_rate_limit, + "use_upnp" : deluge_core.use_upnp, + "use_natpmp" : deluge_core.use_natpmp, + "use_utpex" : deluge_core.use_utpex } STATE_MESSAGES = ( "Queued", @@ -443,6 +446,7 @@ class Manager: # altering max_active_torrents), or just from time to time # ___ALL queuing code should be in this function, and ONLY here___ def apply_queue(self, efficient = True): + print "applying queue"; # Handle autoseeding - downqueue as needed if self.get_pref('auto_seed_ratio') > 0: for unique_ID in self.unique_IDs: @@ -781,3 +785,6 @@ class Manager: # Adds an IP range (as two dotted quad strings) to the filter def add_range_to_ip_filter(self, start, end): return deluge_core.add_range_to_IP_filter(start, end) + + def netextras(self, proto, action): + return deluge_core.netextras(proto, action) diff --git a/src/deluge_core.cpp b/src/deluge_core.cpp index a38504da7..1e8b8d0fd 100644 --- a/src/deluge_core.cpp +++ b/src/deluge_core.cpp @@ -338,7 +338,6 @@ static PyObject *torrent_init(PyObject *self, PyObject *args) M_ses->set_severity_level(alert::debug); M_ses->add_extension(&libtorrent::create_metadata_plugin); - M_ses->add_extension(&libtorrent::create_ut_pex_plugin); M_constants = Py_BuildValue("{s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i}", "EVENT_NULL", EVENT_NULL, @@ -1228,6 +1227,56 @@ static PyObject *torrent_add_range_to_IP_filter(PyObject *self, PyObject *args) Py_INCREF(Py_None); return Py_None; } +static PyObject *torrent_use_upnp(PyObject *self, PyObject *args) +{ + python_long action; + PyArg_ParseTuple(args, "i", &action); + + if (action){ + printf("Starting UPnP\r\n"); + M_ses->start_upnp(); + } + else{ + printf("Stopping natpmp\r\n"); + M_ses->stop_upnp(); + } + + Py_INCREF(Py_None); return Py_None; + +} + +static PyObject *torrent_use_natpmp(PyObject *self, PyObject *args) +{ + python_long action; + + PyArg_ParseTuple(args, "i", &action); + + if (action){ + printf("Starting NAT-PMP\r\n"); + M_ses->start_natpmp(); + } + else{ + printf("Stopping NAT-PMP\r\n"); + M_ses->stop_natpmp(); + } + + Py_INCREF(Py_None); return Py_None; +} + +static PyObject *torrent_use_utpex(PyObject *self, PyObject *args) +{ + python_long action; + + PyArg_ParseTuple(args, "i", &action); + + if (action){ + printf("Starting UTPEX\r\n"); + M_ses->add_extension(&libtorrent::create_ut_pex_plugin); + } + + Py_INCREF(Py_None); return Py_None; +} + static PyObject *torrent_pe_settings(PyObject *self, PyObject *args) { M_pe_settings = new pe_settings(); @@ -1284,6 +1333,9 @@ static PyMethodDef deluge_core_methods[] = {"create_torrent", torrent_create_torrent, METH_VARARGS, "."}, {"reset_IP_filter", torrent_reset_IP_filter, METH_VARARGS, "."}, {"add_range_to_IP_filter", torrent_add_range_to_IP_filter, METH_VARARGS, "."}, + {"use_upnp", torrent_use_upnp, METH_VARARGS, "."}, + {"use_natpmp", torrent_use_natpmp, METH_VARARGS, "."}, + {"use_utpex", torrent_use_utpex, METH_VARARGS, "."}, {NULL} }; diff --git a/src/dialogs.py b/src/dialogs.py index c82206752..4a657e2fc 100644 --- a/src/dialogs.py +++ b/src/dialogs.py @@ -53,6 +53,9 @@ class PreferencesDlg: self.glade.get_widget("combo_encout").set_active(self.preferences.get("encout_state", int, default=common.EncState.enabled)) self.glade.get_widget("combo_enclevel").set_active(self.preferences.get("enclevel_type", int, default=common.EncLevel.both)) self.glade.get_widget("chk_pref_rc4").set_active(self.preferences.get("pref_rc4", bool, default=True)) + self.glade.get_widget("chk_upnp").set_active(self.preferences.get("use_upnp", bool, default=True)) + self.glade.get_widget("chk_natpmp").set_active(self.preferences.get("use_natpmp", bool, default=True)) + self.glade.get_widget("chk_utpex").set_active(self.preferences.get("use_utpex", bool, default=True)) self.glade.get_widget("chk_use_tray").set_active(self.preferences.get("enable_system_tray", bool, default=True)) self.glade.get_widget("chk_min_on_close").set_active(self.preferences.get("close_to_tray", bool, default=False)) self.glade.get_widget("chk_lock_tray").set_active(self.preferences.get("lock_tray", bool, default=False)) @@ -90,6 +93,9 @@ class PreferencesDlg: self.preferences.set("encout_state", self.glade.get_widget("combo_encout").get_active()) self.preferences.set("enclevel_type", self.glade.get_widget("combo_enclevel").get_active()) self.preferences.set("pref_rc4", self.glade.get_widget("chk_pref_rc4").get_active()) + self.preferences.set("use_upnp", self.glade.get_widget("chk_upnp").get_active()) + self.preferences.set("use_natpmp", self.glade.get_widget("chk_natpmp").get_active()) + self.preferences.set("use_utpex", self.glade.get_widget("chk_utpex").get_active()) self.preferences.set("system_tray", self.glade.get_widget("chk_use_tray").get_active()) self.preferences.set("close_to_tray", self.glade.get_widget("chk_min_on_close").get_active()) self.preferences.set("lock_tray", self.glade.get_widget("chk_lock_tray").get_active()) diff --git a/src/pref.py b/src/pref.py index 00ae1680e..8787a2d73 100644 --- a/src/pref.py +++ b/src/pref.py @@ -76,6 +76,9 @@ DEFAULT_PREFS = { "tray_passwd" : "", "use_compact_storage" : False, "use_default_dir" : False, + "use_natpmp" : False, + "use_upnp" : False, + "use_utpex" : False, "window_height" : 480, "window_maximized" : False, "window_pane_position" : -1,