diff --git a/libtorrent/bindings/python/src/docstrings.cpp b/libtorrent/bindings/python/src/docstrings.cpp index c9a0c8a85..f51487a23 100755 --- a/libtorrent/bindings/python/src/docstrings.cpp +++ b/libtorrent/bindings/python/src/docstrings.cpp @@ -130,6 +130,10 @@ char const* session_remove_torrent_doc = "Close all peer connections associated with the torrent and tell the\n" "tracker that we've stopped participating in the swarm."; +char const* session_download_rate_limit_doc = + ""; +char const* session_upload_rate_limit_doc = + ""; char const* session_set_download_rate_limit_doc = ""; char const* session_set_upload_rate_limit_doc = diff --git a/libtorrent/bindings/python/src/session.cpp b/libtorrent/bindings/python/src/session.cpp index e6050d1bb..9f7d530af 100755 --- a/libtorrent/bindings/python/src/session.cpp +++ b/libtorrent/bindings/python/src/session.cpp @@ -38,7 +38,9 @@ extern char const* session_dht_state_doc; extern char const* session_add_torrent_doc; extern char const* session_remove_torrent_doc; extern char const* session_set_download_rate_limit_doc; +extern char const* session_download_rate_limit_doc; extern char const* session_set_upload_rate_limit_doc; +extern char const* session_upload_rate_limit_doc; extern char const* session_set_max_uploads_doc; extern char const* session_set_max_connections_doc; extern char const* session_set_max_half_open_connections_doc; @@ -176,10 +178,20 @@ void bind_session() "set_download_rate_limit", allow_threads(&session::set_download_rate_limit) , session_set_download_rate_limit_doc ) + .def( + "download_rate_limit", allow_threads(&session::download_rate_limit) + , session_download_rate_limit_doc + ) + .def( "set_upload_rate_limit", allow_threads(&session::set_upload_rate_limit) , session_set_upload_rate_limit_doc ) + .def( + "upload_rate_limit", allow_threads(&session::upload_rate_limit) + , session_upload_rate_limit_doc + ) + .def( "set_max_uploads", allow_threads(&session::set_max_uploads) , session_set_max_uploads_doc diff --git a/libtorrent/bindings/python/src/torrent_handle.cpp b/libtorrent/bindings/python/src/torrent_handle.cpp index 6e0bc5034..98540a5f8 100755 --- a/libtorrent/bindings/python/src/torrent_handle.cpp +++ b/libtorrent/bindings/python/src/torrent_handle.cpp @@ -64,6 +64,30 @@ list get_peer_info(torrent_handle const& handle) return result; } + +void prioritize_files(torrent_handle& info, object o) +{ + + std::vector result; + try + { + object iter_obj = object( handle<>( PyObject_GetIter( o.ptr() ) )); + while( 1 ) + { + object obj = extract( iter_obj.attr( "next" )() ); + result.push_back(extract( obj )); + } + } + catch( error_already_set ) + { + PyErr_Clear(); + info.prioritize_files(result); + return; + } + +} + + void replace_trackers(torrent_handle& info, object trackers) { object iter(trackers.attr("__iter__")()); @@ -106,7 +130,9 @@ list get_download_queue(torrent_handle& handle) { dict block_info; block_info["state"] = i->blocks[k].state; - block_info["num_downloads"] = i->blocks[k].num_downloads; + block_info["num_peers"] = i->blocks[k].num_peers; + block_info["bytes_progress"] = i->blocks[k].bytes_progress; + block_info["block_size"] = i->blocks[k].block_size; // block_info["peer"] = i->info[k].peer; block_list.append(block_info); } @@ -124,6 +150,9 @@ void bind_torrent_handle() void (torrent_handle::*force_reannounce1)(boost::posix_time::time_duration) const = &torrent_handle::force_reannounce; + int (torrent_handle::*piece_priority0)(int) const = &torrent_handle::piece_priority; + void (torrent_handle::*piece_priority1)(int, int) const = &torrent_handle::piece_priority; + return_value_policy copy; #define _ allow_threads @@ -148,6 +177,8 @@ void bind_torrent_handle() .def("is_paused", _(&torrent_handle::is_paused)) .def("is_seed", _(&torrent_handle::is_seed)) .def("filter_piece", _(&torrent_handle::filter_piece)) + .def("piece_priority", _(piece_priority0)) + .def("piece_priority", _(piece_priority1)) .def("is_piece_filtered", _(&torrent_handle::is_piece_filtered)) .def("has_metadata", _(&torrent_handle::has_metadata)) .def("save_path", _(&torrent_handle::save_path)) @@ -156,6 +187,7 @@ void bind_torrent_handle() .def("file_progress", file_progress) .def("trackers", range(begin_trackers, end_trackers)) .def("replace_trackers", replace_trackers) + .def("prioritize_files", prioritize_files) .def("get_peer_info", get_peer_info) .def("get_download_queue", get_download_queue) ; diff --git a/libtorrent/bindings/python/src/torrent_info.cpp b/libtorrent/bindings/python/src/torrent_info.cpp index 1c31ec185..301c4a5bf 100755 --- a/libtorrent/bindings/python/src/torrent_info.cpp +++ b/libtorrent/bindings/python/src/torrent_info.cpp @@ -70,7 +70,8 @@ void bind_torrent_info() .def("hash_for_piece", &torrent_info::hash_for_piece, copy) .def("piece_size", &torrent_info::piece_size) - + + .def("num_files", &torrent_info::num_files) .def("file_at", &torrent_info::file_at, return_internal_reference<>()) .def("files", range(&torrent_info::begin_files, &torrent_info::end_files)) diff --git a/libtorrent/include/libtorrent/bandwidth_manager.hpp b/libtorrent/include/libtorrent/bandwidth_manager.hpp index 30f99d0cf..4df9d4f2f 100644 --- a/libtorrent/include/libtorrent/bandwidth_manager.hpp +++ b/libtorrent/include/libtorrent/bandwidth_manager.hpp @@ -391,10 +391,6 @@ private: break; } - // don't hand out chunks larger than the throttle - // per second on the torrent - assert(qe.max_block_size <= t->bandwidth_throttle(m_channel)); - // so, hand out max_assignable, but no more than // the available bandwidth (amount) and no more // than the max_bandwidth_block_size @@ -402,6 +398,7 @@ private: , amount); assert(hand_out_amount > 0); amount -= hand_out_amount; + assert(hand_out_amount <= qe.max_block_size); t->assign_bandwidth(m_channel, hand_out_amount, qe.max_block_size); qe.peer->assign_bandwidth(m_channel, hand_out_amount); add_history_entry(history_entry( diff --git a/libtorrent/include/libtorrent/bt_peer_connection.hpp b/libtorrent/include/libtorrent/bt_peer_connection.hpp index 0f5e58e9d..beec94979 100755 --- a/libtorrent/include/libtorrent/bt_peer_connection.hpp +++ b/libtorrent/include/libtorrent/bt_peer_connection.hpp @@ -202,7 +202,7 @@ namespace libtorrent void write_metadata_request(std::pair req); void write_keepalive(); void write_dht_port(int listen_port); - void on_connected() {} + void on_connected(); void on_metadata(); #ifndef NDEBUG @@ -370,6 +370,8 @@ namespace libtorrent bool m_sent_bitfield; bool m_in_constructor; + + bool m_sent_handshake; #endif }; diff --git a/libtorrent/include/libtorrent/connection_queue.hpp b/libtorrent/include/libtorrent/connection_queue.hpp index 05a8a61fe..17be248bf 100644 --- a/libtorrent/include/libtorrent/connection_queue.hpp +++ b/libtorrent/include/libtorrent/connection_queue.hpp @@ -88,6 +88,9 @@ private: int m_half_open_limit; deadline_timer m_timer; +#ifndef NDEBUG + bool m_in_timeout_function; +#endif }; } diff --git a/libtorrent/include/libtorrent/peer_connection.hpp b/libtorrent/include/libtorrent/peer_connection.hpp index b459c491e..d32d250fc 100755 --- a/libtorrent/include/libtorrent/peer_connection.hpp +++ b/libtorrent/include/libtorrent/peer_connection.hpp @@ -395,6 +395,9 @@ namespace libtorrent #ifndef TORRENT_DISABLE_ENCRYPTION buffer::interval wr_recv_buffer() { +#if defined _SECURE_SCL && _SECURE_SCL > 0 + if (m_recv_buffer.empty()) return buffer::interval(0,0); +#endif return buffer::interval(&m_recv_buffer[0] , &m_recv_buffer[0] + m_recv_pos); } @@ -402,6 +405,9 @@ namespace libtorrent buffer::const_interval receive_buffer() const { +#if defined _SECURE_SCL && _SECURE_SCL > 0 + if (m_recv_buffer.empty()) return buffer::const_interval(0,0); +#endif return buffer::const_interval(&m_recv_buffer[0] , &m_recv_buffer[0] + m_recv_pos); } diff --git a/libtorrent/include/libtorrent/piece_picker.hpp b/libtorrent/include/libtorrent/piece_picker.hpp index c52521d0a..136e71c63 100755 --- a/libtorrent/include/libtorrent/piece_picker.hpp +++ b/libtorrent/include/libtorrent/piece_picker.hpp @@ -42,7 +42,6 @@ POSSIBILITY OF SUCH DAMAGE. #pragma warning(push, 1) #endif -#include #include #ifdef _MSC_VER @@ -90,12 +89,15 @@ namespace libtorrent struct block_info { - block_info(): num_downloads(0), state(state_none) {} + block_info(): peer(0), num_peers(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; + // downloaded from. This is a pointer to + // a policy::peer object + void* peer; + // the number of peers that has this block in their + // download or request queues + unsigned num_peers:14; + // the state of this block enum { state_none, state_requested, state_writing, state_finished }; unsigned state:2; }; @@ -185,12 +187,17 @@ namespace libtorrent // decides to download a piece, it must mark it as being downloaded // itself, by using the mark_as_downloading() member function. // THIS IS DONE BY THE peer_connection::send_request() MEMBER FUNCTION! - // The last argument is the tcp::endpoint of the peer that we'll download - // from. + // The last argument is the policy::peer pointer for the peer that + // we'll download from. void pick_pieces(const std::vector& pieces , std::vector& interesting_blocks , int num_pieces, bool prefer_whole_pieces - , tcp::endpoint peer, piece_state_t speed) const; + , void* peer, piece_state_t speed + , bool rarest_first) const; + + // clears the peer pointer in all downloading pieces with this + // peer pointer + void clear_peer(void* peer); // returns true if any client is currently downloading this // piece-block, or if it's queued for downloading by some client @@ -202,10 +209,11 @@ namespace libtorrent 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 + void mark_as_downloading(piece_block block, void* 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); + void mark_as_writing(piece_block block, void* peer); + void mark_as_finished(piece_block block, void* peer); + int num_peers(piece_block block) const; // if a piece had a hash-failure, it must be restored and // made available for redownloading @@ -224,12 +232,12 @@ namespace libtorrent // the hash-check yet int unverified_blocks() const; - void get_downloaders(std::vector& d, int index) const; + void get_downloaders(std::vector& d, int index) const; std::vector const& get_download_queue() const { return m_downloads; } - boost::optional get_downloader(piece_block block) const; + void* get_downloader(piece_block block) const; // the number of filtered pieces we don't have int num_filtered() const { return m_num_filtered; } @@ -271,7 +279,8 @@ namespace libtorrent assert(index_ >= 0); } - // selects which vector to look in + // the number of peers that has this piece + // (availability) unsigned peer_count : 10; // is 1 if the piece is marked as being downloaded unsigned downloading : 1; @@ -342,13 +351,15 @@ namespace libtorrent void add(int index); void move(int vec_index, int elem_index); + void sort_piece(std::vector::iterator dp); int add_interesting_blocks(const std::vector& piece_list , const std::vector& pieces , std::vector& interesting_blocks , std::vector& backup_blocks , int num_blocks, bool prefer_whole_pieces - , tcp::endpoint peer, piece_state_t speed) const; + , void* peer, piece_state_t speed + , bool ignore_downloading_pieces) const; downloading_piece& add_download_piece(); void erase_download_piece(std::vector::iterator i); diff --git a/libtorrent/include/libtorrent/policy.hpp b/libtorrent/include/libtorrent/policy.hpp index fffc3bfa2..e087ccb75 100755 --- a/libtorrent/include/libtorrent/policy.hpp +++ b/libtorrent/include/libtorrent/policy.hpp @@ -68,10 +68,7 @@ namespace libtorrent free_upload_amount = 4 * 16 * 1024 }; - void request_a_block( - torrent& t - , peer_connection& c - , std::vector ignore = std::vector()); + void request_a_block(torrent& t, peer_connection& c); class TORRENT_EXPORT policy { @@ -121,9 +118,9 @@ namespace libtorrent struct peer { - enum connection_type { not_connectable,connectable }; + enum connection_type { not_connectable, connectable }; - peer(const tcp::endpoint& ip, connection_type t, int src); + peer(tcp::endpoint const& ip, connection_type t, int src); size_type total_download() const; size_type total_upload() const; diff --git a/libtorrent/include/libtorrent/session_settings.hpp b/libtorrent/include/libtorrent/session_settings.hpp index d7a4f88c1..783492227 100644 --- a/libtorrent/include/libtorrent/session_settings.hpp +++ b/libtorrent/include/libtorrent/session_settings.hpp @@ -107,6 +107,7 @@ namespace libtorrent , inactivity_timeout(600) , unchoke_interval(20) , num_want(200) + , initial_picker_threshold(4) #ifndef TORRENT_DISABLE_DHT , use_dht_as_fallback(true) #endif @@ -246,6 +247,10 @@ namespace libtorrent // the num want sent to trackers int num_want; + // while we have fewer pieces than this, pick + // random pieces instead of rarest first. + int initial_picker_threshold; + #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/stat.hpp b/libtorrent/include/libtorrent/stat.hpp index 8ef8da1b4..2424d5d6c 100755 --- a/libtorrent/include/libtorrent/stat.hpp +++ b/libtorrent/include/libtorrent/stat.hpp @@ -131,6 +131,8 @@ namespace libtorrent // transfers from earlier connections. void add_stat(size_type downloaded, size_type uploaded) { + assert(downloaded >= 0); + assert(uploaded >= 0); m_total_download_payload += downloaded; m_total_upload_payload += uploaded; } diff --git a/libtorrent/include/libtorrent/torrent.hpp b/libtorrent/include/libtorrent/torrent.hpp index 1274c46fc..bed076fe9 100755 --- a/libtorrent/include/libtorrent/torrent.hpp +++ b/libtorrent/include/libtorrent/torrent.hpp @@ -262,6 +262,8 @@ namespace libtorrent // decreased in the piece_picker void remove_peer(peer_connection* p); + void cancel_block(piece_block block); + bool want_more_peers() const; bool try_connect_peer(); @@ -272,6 +274,18 @@ namespace libtorrent return i->second; } +#ifndef NDEBUG + void connection_for(address const& a, std::vector& pc) + { + for (peer_iterator i = m_connections.begin() + , end(m_connections.end()); i != end; ++i) + { + if (i->first.address() == a) pc.push_back(i->second); + } + return; + } +#endif + // the number of peers that belong to this torrent int num_peers() const { return (int)m_connections.size(); } int num_seeds() const; diff --git a/libtorrent/include/libtorrent/torrent_handle.hpp b/libtorrent/include/libtorrent/torrent_handle.hpp index b5e6bdc17..a4c8af923 100755 --- a/libtorrent/include/libtorrent/torrent_handle.hpp +++ b/libtorrent/include/libtorrent/torrent_handle.hpp @@ -101,6 +101,11 @@ namespace libtorrent , num_seeds(0) , distributed_copies(0.f) , block_size(0) + , num_uploads(0) + , num_connections(0) + , uploads_limit(0) + , connections_limit(0) + , compact_mode(false) {} enum state_t @@ -202,6 +207,15 @@ namespace libtorrent // the number of bytes each piece request asks for // and each bit in the download queue bitfield represents int block_size; + + int num_uploads; + int num_connections; + int uploads_limit; + int connections_limit; + + // true if the torrent is saved in compact mode + // false if it is saved in full allocation mode + bool compact_mode; }; struct TORRENT_EXPORT block_info @@ -210,8 +224,16 @@ namespace libtorrent { none, requested, writing, finished }; tcp::endpoint peer; + // number of bytes downloaded in this block + unsigned bytes_progress:16; + // the total number of bytes in this block + unsigned block_size:16; + // the state this block is in (see block_state_t) unsigned state:2; - unsigned num_downloads:14; + // the number of peers that has requested this block + // typically 0 or 1. If > 1, this block is in + // end game mode + unsigned num_peers:14; }; struct TORRENT_EXPORT partial_piece_info diff --git a/libtorrent/src/Makefile.am b/libtorrent/src/Makefile.am index 97e74ee25..ef834018b 100644 --- a/libtorrent/src/Makefile.am +++ b/libtorrent/src/Makefile.am @@ -13,7 +13,7 @@ kademlia/traversal_algorithm.cpp endif libtorrent_la_SOURCES = allocate_resources.cpp \ -bandwidth_manager.cpp entry.cpp escape_string.cpp \ +entry.cpp escape_string.cpp \ peer_connection.cpp bt_peer_connection.cpp web_peer_connection.cpp \ natpmp.cpp piece_picker.cpp policy.cpp session.cpp session_impl.cpp sha1.cpp \ stat.cpp storage.cpp torrent.cpp torrent_handle.cpp pe_crypto.cpp \ diff --git a/libtorrent/src/bt_peer_connection.cpp b/libtorrent/src/bt_peer_connection.cpp index 2483b4a2a..5b63f26cd 100755 --- a/libtorrent/src/bt_peer_connection.cpp +++ b/libtorrent/src/bt_peer_connection.cpp @@ -102,12 +102,73 @@ namespace libtorrent #ifndef NDEBUG , m_sent_bitfield(false) , m_in_constructor(true) + , m_sent_handshake(false) #endif { #ifdef TORRENT_VERBOSE_LOGGING (*m_logger) << "*** bt_peer_connection\n"; #endif +#ifndef NDEBUG + m_in_constructor = false; +#endif + } + + bt_peer_connection::bt_peer_connection( + session_impl& ses + , boost::shared_ptr s + , policy::peer* peerinfo) + : peer_connection(ses, s, peerinfo) + , m_state(read_protocol_identifier) +#ifndef TORRENT_DISABLE_EXTENSIONS + , m_supports_extensions(false) +#endif + , m_supports_dht_port(false) +#ifndef TORRENT_DISABLE_ENCRYPTION + , m_encrypted(false) + , m_rc4_encrypted(false) + , m_sync_bytes_read(0) + , m_enc_send_buffer(0, 0) +#endif +#ifndef NDEBUG + , m_sent_bitfield(false) + , m_in_constructor(true) + , m_sent_handshake(false) +#endif + { + + // we are not attached to any torrent yet. + // we have to wait for the handshake to see + // which torrent the connector want's to connect to + + + // upload bandwidth will only be given to connections + // that are part of a torrent. Since this is an incoming + // connection, we have to give it some initial bandwidth + // to send the handshake. +#ifndef TORRENT_DISABLE_ENCRYPTION + m_bandwidth_limit[download_channel].assign(2048); + m_bandwidth_limit[upload_channel].assign(2048); +#else + m_bandwidth_limit[download_channel].assign(80); + m_bandwidth_limit[upload_channel].assign(80); +#endif + + // start in the state where we are trying to read the + // handshake from the other side + reset_recv_buffer(20); + setup_receive(); +#ifndef NDEBUG + m_in_constructor = false; +#endif + } + + bt_peer_connection::~bt_peer_connection() + { + } + + void bt_peer_connection::on_connected() + { #ifndef TORRENT_DISABLE_ENCRYPTION pe_settings::enc_policy const& out_enc_policy = m_ses.get_pe_settings().out_enc_policy; @@ -158,64 +219,8 @@ namespace libtorrent reset_recv_buffer(20); setup_receive(); } - -#ifndef NDEBUG - m_in_constructor = false; -#endif } - - bt_peer_connection::bt_peer_connection( - session_impl& ses - , boost::shared_ptr s - , policy::peer* peerinfo) - : peer_connection(ses, s, peerinfo) - , m_state(read_protocol_identifier) -#ifndef TORRENT_DISABLE_EXTENSIONS - , m_supports_extensions(false) -#endif - , m_supports_dht_port(false) -#ifndef TORRENT_DISABLE_ENCRYPTION - , m_encrypted(false) - , m_rc4_encrypted(false) - , m_sync_bytes_read(0) - , m_enc_send_buffer(0, 0) -#endif -#ifndef NDEBUG - , m_sent_bitfield(false) - , m_in_constructor(true) -#endif - { - - // we are not attached to any torrent yet. - // we have to wait for the handshake to see - // which torrent the connector want's to connect to - - - // upload bandwidth will only be given to connections - // that are part of a torrent. Since this is an incoming - // connection, we have to give it some initial bandwidth - // to send the handshake. -#ifndef TORRENT_DISABLE_ENCRYPTION - m_bandwidth_limit[download_channel].assign(2048); - m_bandwidth_limit[upload_channel].assign(2048); -#else - m_bandwidth_limit[download_channel].assign(80); - m_bandwidth_limit[upload_channel].assign(80); -#endif - - // start in the state where we are trying to read the - // handshake from the other side - reset_recv_buffer(20); - setup_receive(); -#ifndef NDEBUG - m_in_constructor = false; -#endif - } - - bt_peer_connection::~bt_peer_connection() - { - } - + void bt_peer_connection::on_metadata() { boost::shared_ptr t = associated_torrent().lock(); @@ -226,6 +231,9 @@ namespace libtorrent void bt_peer_connection::write_dht_port(int listen_port) { INVARIANT_CHECK; + + assert(m_sent_handshake && m_sent_bitfield); + #ifdef TORRENT_VERBOSE_LOGGING (*m_logger) << time_now_string() << " ==> DHT_PORT [ " << listen_port << " ]\n"; @@ -282,6 +290,7 @@ namespace libtorrent assert(!m_encrypted); assert(!m_rc4_encrypted); assert(!m_DH_key_exchange.get()); + assert(!m_sent_handshake); #ifdef TORRENT_VERBOSE_LOGGING if (is_local()) @@ -314,9 +323,10 @@ namespace libtorrent { INVARIANT_CHECK; - assert (!m_encrypted); - assert (!m_rc4_encrypted); - assert (is_local()); + assert(!m_encrypted); + assert(!m_rc4_encrypted); + assert(is_local()); + assert(!m_sent_handshake); boost::shared_ptr t = associated_torrent().lock(); assert(t); @@ -398,6 +408,7 @@ namespace libtorrent assert(!m_encrypted); assert(!m_rc4_encrypted); assert(crypto_select == 0x02 || crypto_select == 0x01); + assert(!m_sent_handshake); int pad_size = 0; // rand() % 512; // Keep 0 for now @@ -423,8 +434,8 @@ namespace libtorrent #endif } - void bt_peer_connection::write_pe_vc_cryptofield(buffer::interval& write_buf, - int crypto_field, int pad_size) + void bt_peer_connection::write_pe_vc_cryptofield(buffer::interval& write_buf + , int crypto_field, int pad_size) { INVARIANT_CHECK; @@ -433,6 +444,7 @@ namespace libtorrent // vc,crypto_field,len(pad),pad, (len(ia)) assert( (write_buf.left() == 8+4+2+pad_size+2 && is_local()) || (write_buf.left() == 8+4+2+pad_size && !is_local()) ); + assert(!m_sent_handshake); // encrypt(vc, crypto_provide/select, len(Pad), len(IA)) // len(pad) is zero for now, len(IA) only for outgoing connections @@ -587,6 +599,11 @@ namespace libtorrent { INVARIANT_CHECK; + assert(!m_sent_handshake); +#ifndef NDEBUG + m_sent_handshake = true; +#endif + boost::shared_ptr t = associated_torrent().lock(); assert(t); @@ -1093,6 +1110,8 @@ namespace libtorrent { INVARIANT_CHECK; + assert(m_sent_handshake && m_sent_bitfield); + char buf[] = {0,0,0,0}; send_buffer(buf, buf + sizeof(buf)); } @@ -1100,8 +1119,8 @@ namespace libtorrent void bt_peer_connection::write_cancel(peer_request const& r) { INVARIANT_CHECK; - assert(m_sent_bitfield == true); + assert(m_sent_handshake && m_sent_bitfield); assert(associated_torrent().lock()->valid_metadata()); char buf[] = {0,0,0,13, msg_cancel}; @@ -1125,8 +1144,8 @@ namespace libtorrent void bt_peer_connection::write_request(peer_request const& r) { INVARIANT_CHECK; - assert(m_sent_bitfield == true); + assert(m_sent_handshake && m_sent_bitfield); assert(associated_torrent().lock()->valid_metadata()); char buf[] = {0,0,0,13, msg_request}; @@ -1153,7 +1172,7 @@ namespace libtorrent boost::shared_ptr t = associated_torrent().lock(); assert(t); - assert(m_sent_bitfield == false); + assert(m_sent_handshake && !m_sent_bitfield); assert(t->valid_metadata()); int num_pieces = bitfield.size(); @@ -1243,6 +1262,7 @@ namespace libtorrent (*m_logger) << time_now_string() << " ==> EXTENSIONS\n"; #endif assert(m_supports_extensions); + assert(m_sent_handshake); entry handshake(entry::dictionary_t); entry extension_list(entry::dictionary_t); @@ -1257,7 +1277,7 @@ namespace libtorrent std::string remote_address; std::back_insert_iterator out(remote_address); detail::write_address(remote().address(), out); - handshake["ip"] = remote_address; + handshake["yourip"] = remote_address; handshake["reqq"] = m_ses.settings().max_allowed_in_request_queue; // loop backwards, to make the first extension be the last @@ -1297,7 +1317,8 @@ namespace libtorrent void bt_peer_connection::write_choke() { INVARIANT_CHECK; - assert(m_sent_bitfield == true); + + assert(m_sent_handshake && m_sent_bitfield); if (is_choked()) return; char msg[] = {0,0,0,1,msg_choke}; @@ -1307,7 +1328,8 @@ namespace libtorrent void bt_peer_connection::write_unchoke() { INVARIANT_CHECK; - assert(m_sent_bitfield == true); + + assert(m_sent_handshake && m_sent_bitfield); char msg[] = {0,0,0,1,msg_unchoke}; send_buffer(msg, msg + sizeof(msg)); @@ -1316,7 +1338,8 @@ namespace libtorrent void bt_peer_connection::write_interested() { INVARIANT_CHECK; - assert(m_sent_bitfield == true); + + assert(m_sent_handshake && m_sent_bitfield); char msg[] = {0,0,0,1,msg_interested}; send_buffer(msg, msg + sizeof(msg)); @@ -1325,7 +1348,8 @@ namespace libtorrent void bt_peer_connection::write_not_interested() { INVARIANT_CHECK; - assert(m_sent_bitfield == true); + + assert(m_sent_handshake && m_sent_bitfield); char msg[] = {0,0,0,1,msg_not_interested}; send_buffer(msg, msg + sizeof(msg)); @@ -1337,7 +1361,7 @@ namespace libtorrent assert(associated_torrent().lock()->valid_metadata()); assert(index >= 0); assert(index < associated_torrent().lock()->torrent_file().num_pieces()); - assert(m_sent_bitfield == true); + assert(m_sent_handshake && m_sent_bitfield); const int packet_size = 9; char msg[packet_size] = {0,0,0,5,msg_have}; @@ -1349,7 +1373,8 @@ namespace libtorrent void bt_peer_connection::write_piece(peer_request const& r, char const* buffer) { INVARIANT_CHECK; - assert(m_sent_bitfield == true); + + assert(m_sent_handshake && m_sent_bitfield); const int packet_size = 4 + 5 + 4 + r.length; diff --git a/libtorrent/src/connection_queue.cpp b/libtorrent/src/connection_queue.cpp index f83baa196..473a92920 100644 --- a/libtorrent/src/connection_queue.cpp +++ b/libtorrent/src/connection_queue.cpp @@ -42,6 +42,9 @@ namespace libtorrent , m_num_connecting(0) , m_half_open_limit(0) , m_timer(ios) +#ifndef NDEBUG + , m_in_timeout_function(false) +#endif {} bool connection_queue::free_slots() const @@ -133,9 +136,22 @@ namespace libtorrent } } +#ifndef NDEBUG + struct function_guard + { + function_guard(bool& v): val(v) { assert(!val); val = true; } + ~function_guard() { val = false; } + + bool& val; + }; +#endif + void connection_queue::on_timeout(asio::error_code const& e) { INVARIANT_CHECK; +#ifndef NDEBUG + function_guard guard_(m_in_timeout_function); +#endif assert(!e || e == asio::error::operation_aborted); if (e) return; diff --git a/libtorrent/src/disk_io_thread.cpp b/libtorrent/src/disk_io_thread.cpp index 5398d5ae8..b02ab0012 100644 --- a/libtorrent/src/disk_io_thread.cpp +++ b/libtorrent/src/disk_io_thread.cpp @@ -216,6 +216,7 @@ namespace libtorrent break; case disk_io_job::move_storage: ret = j.storage->move_storage_impl(j.str) ? 1 : 0; + j.str = j.storage->save_path().string(); break; case disk_io_job::release_files: j.storage->release_files_impl(); diff --git a/libtorrent/src/pe_crypto.cpp b/libtorrent/src/pe_crypto.cpp index a763e5458..437c93e2c 100644 --- a/libtorrent/src/pe_crypto.cpp +++ b/libtorrent/src/pe_crypto.cpp @@ -50,6 +50,7 @@ namespace libtorrent { m_DH->p = BN_bin2bn (m_dh_prime, sizeof(m_dh_prime), NULL); m_DH->g = BN_bin2bn (m_dh_generator, sizeof(m_dh_generator), NULL); + m_DH->length = 160l; assert (sizeof(m_dh_prime) == DH_size(m_DH)); @@ -81,7 +82,7 @@ namespace libtorrent { DH_free (m_DH); } - char const* DH_key_exchange::get_local_key () const + char const* DH_key_exchange::get_local_key () const { return m_dh_local_key; } @@ -92,9 +93,17 @@ namespace libtorrent { { assert (remote_pubkey); BIGNUM* bn_remote_pubkey = BN_bin2bn ((unsigned char*)remote_pubkey, 96, NULL); + char dh_secret[96]; - int ret = - DH_compute_key ( (unsigned char*)m_dh_secret, bn_remote_pubkey, m_DH); // TODO Check for errors + int secret_size = DH_compute_key ( (unsigned char*)dh_secret, + bn_remote_pubkey, m_DH); // TODO Check for errors + + if (secret_size != 96) + { + assert(secret_size < 96 && secret_size > 0); + std::fill(m_dh_secret, m_dh_secret + 96 - secret_size, 0); + } + std::copy(dh_secret, dh_secret + secret_size, m_dh_secret + 96 - secret_size); BN_free (bn_remote_pubkey); } diff --git a/libtorrent/src/peer_connection.cpp b/libtorrent/src/peer_connection.cpp index 8a2ac0ae3..7dbef4381 100755 --- a/libtorrent/src/peer_connection.cpp +++ b/libtorrent/src/peer_connection.cpp @@ -397,15 +397,6 @@ namespace libtorrent try { (*i)->on_piece_pass(index); } catch (std::exception&) {} } #endif - - if (peer_info_struct()) - { - peer_info_struct()->on_parole = false; - int& trust_points = peer_info_struct()->trust_points; - trust_points++; - // TODO: make this limit user settable - if (trust_points > 20) trust_points = 20; - } } void peer_connection::received_invalid_data(int index) @@ -1090,10 +1081,6 @@ namespace libtorrent , m_download_queue.end() , block_finished); - // if there's another peer that needs to do another - // piece request, this will point to it - peer_connection* request_peer = 0; - if (b != m_download_queue.end()) { if (m_assume_fifo) @@ -1122,48 +1109,32 @@ namespace libtorrent { m_download_queue.erase(b); } + + t->cancel_block(block_finished); } else { -/* // cancel the block from the - // peer that has taken over it. - boost::optional peer - = t->picker().get_downloader(block_finished); - if (peer && t->picker().is_requested(block_finished)) + if (t->alerts().should_post(alert::debug)) { - peer_connection* pc = t->connection_for(*peer); - if (pc && pc != this) - { - pc->cancel_request(block_finished); - request_peer = pc; - } + t->alerts().post_alert( + peer_error_alert( + m_remote + , m_peer_id + , "got a block that was not in the request queue")); } - else -*/ { - if (t->alerts().should_post(alert::debug)) - { - t->alerts().post_alert( - peer_error_alert( - m_remote - , m_peer_id - , "got a block that was not requested")); - } #ifdef TORRENT_VERBOSE_LOGGING - (*m_logger) << " *** The block we just got was not in the " - "request queue ***\n"; + (*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; + 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_downloaded(block_finished)) { @@ -1174,25 +1145,12 @@ namespace libtorrent request_a_block(*t, *this); send_block_requests(); } - - if (request_peer && !request_peer->has_peer_choked() && !t->is_seed()) - { - request_a_block(*t, *request_peer); - request_peer->send_block_requests(); - } return; } 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(); - } - + picker.mark_as_writing(block_finished, peer_info_struct()); } void peer_connection::on_disk_write_complete(int ret, disk_io_job const& j @@ -1224,7 +1182,7 @@ namespace libtorrent 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); + picker.mark_as_finished(block_finished, peer_info_struct()); if (!has_peer_choked() && !t->is_seed() && !m_torrent.expired()) { @@ -1328,7 +1286,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_requested(block)); + assert(!t->picker().is_requested(block) || (t->picker().num_peers(block) > 0)); piece_picker::piece_state_t state; peer_speed_t speed = peer_speed(); @@ -1336,7 +1294,7 @@ namespace libtorrent else if (speed == medium) state = piece_picker::medium; else state = piece_picker::slow; - t->picker().mark_as_downloading(block, m_remote, state); + t->picker().mark_as_downloading(block, peer_info_struct(), state); m_request_queue.push_back(block); } @@ -1354,17 +1312,22 @@ 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_requested(block)); - t->picker().abort_download(block); + // if all the peers that requested this block has been + // cancelled, then just ignore the cancel. + if (!t->picker().is_requested(block)) return; std::deque::iterator it = std::find(m_download_queue.begin(), m_download_queue.end(), block); if (it == m_download_queue.end()) { it = std::find(m_request_queue.begin(), m_request_queue.end(), block); - assert(it != m_request_queue.end()); + // when a multi block is received, it is cancelled + // from all peers, so if this one hasn't requested + // the block, just ignore to cancel it. if (it == m_request_queue.end()) return; + + t->picker().abort_download(block); m_request_queue.erase(it); // since we found it in the request queue, it means it hasn't been // sent yet, so we don't have to send a cancel. @@ -1373,6 +1336,7 @@ namespace libtorrent else { m_download_queue.erase(it); + t->picker().abort_download(block); } int block_offset = block.block_index * t->block_size(); @@ -1858,8 +1822,6 @@ namespace libtorrent } } - m_statistics.second_tick(tick_interval); - // If the client sends more data // we send it data faster, otherwise, slower. // It will also depend on how much data the diff --git a/libtorrent/src/piece_picker.cpp b/libtorrent/src/piece_picker.cpp index bd568210f..61f3544b7 100755 --- a/libtorrent/src/piece_picker.cpp +++ b/libtorrent/src/piece_picker.cpp @@ -48,8 +48,8 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/torrent.hpp" #endif -#define TORRENT_PIECE_PICKER_INVARIANT_CHECK INVARIANT_CHECK -//#define TORRENT_PIECE_PICKER_INVARIANT_CHECK +//#define TORRENT_PIECE_PICKER_INVARIANT_CHECK INVARIANT_CHECK +#define TORRENT_PIECE_PICKER_INVARIANT_CHECK namespace libtorrent { @@ -116,11 +116,10 @@ namespace libtorrent for (std::vector::const_iterator i = unfinished.begin(); i != unfinished.end(); ++i) { - tcp::endpoint peer; for (int j = 0; j < m_blocks_per_piece; ++j) { if (i->info[j].state == block_info::state_finished) - mark_as_finished(piece_block(i->index, j), peer); + mark_as_finished(piece_block(i->index, j), 0); } if (is_piece_finished(i->index)) { @@ -197,12 +196,13 @@ namespace libtorrent int block_index = num_downloads * m_blocks_per_piece; if (int(m_block_info.size()) < block_index + m_blocks_per_piece) { + block_info* base = &m_block_info[0]; m_block_info.resize(block_index + m_blocks_per_piece); - if (!m_downloads.empty() && &m_block_info[0] != m_downloads.front().info) + if (!m_downloads.empty() && &m_block_info[0] != base) { // this means the memory was reallocated, update the pointers for (int i = 0; i < int(m_downloads.size()); ++i) - m_downloads[i].info = &m_block_info[i * m_blocks_per_piece]; + m_downloads[i].info = &m_block_info[m_downloads[i].info - base]; } } m_downloads.push_back(downloading_piece()); @@ -210,30 +210,27 @@ namespace libtorrent ret.info = &m_block_info[block_index]; for (int i = 0; i < m_blocks_per_piece; ++i) { - ret.info[i].num_downloads = 0; + ret.info[i].num_peers = 0; ret.info[i].state = block_info::state_none; - ret.info[i].peer = tcp::endpoint(); + ret.info[i].peer = 0; } return ret; } void piece_picker::erase_download_piece(std::vector::iterator i) { - if (i != m_downloads.end() - 1) + std::vector::iterator other = std::find_if( + m_downloads.begin(), m_downloads.end() + , bind(&downloading_piece::info, _1) + == &m_block_info[(m_downloads.size() - 1) * m_blocks_per_piece]); + assert(other != m_downloads.end()); + + if (i != other) { - int remove_index = i - m_downloads.begin(); - int last_index = m_downloads.size() - 1; - assert(remove_index < last_index); - - assert(int(m_block_info.size()) >= last_index * m_blocks_per_piece + m_blocks_per_piece); - std::copy(m_block_info.begin() + (last_index * m_blocks_per_piece) - , m_block_info.begin() + (last_index * m_blocks_per_piece + m_blocks_per_piece) - , m_block_info.begin() + (remove_index * m_blocks_per_piece)); - m_downloads[remove_index] = m_downloads[last_index]; - m_downloads[remove_index].info = &m_block_info[remove_index * m_blocks_per_piece]; + std::copy(other->info, other->info + m_blocks_per_piece, i->info); + other->info = i->info; } - - m_downloads.pop_back(); + m_downloads.erase(i); } #ifndef NDEBUG @@ -243,6 +240,17 @@ namespace libtorrent assert(m_piece_info.empty() || m_piece_info[0].empty()); + if (!m_downloads.empty()) + { + for (std::vector::const_iterator i = m_downloads.begin(); + i != m_downloads.end() - 1; ++i) + { + downloading_piece const& dp = *i; + downloading_piece const& next = *(i + 1); + assert(dp.finished + dp.writing >= next.finished + next.writing); + } + } + if (t != 0) assert((int)m_piece_map.size() == t->torrent_file().num_pieces()); @@ -594,6 +602,20 @@ namespace libtorrent } } + void piece_picker::sort_piece(std::vector::iterator dp) + { + assert(m_piece_map[dp->index].downloading); + int complete = dp->writing + dp->finished; + for (std::vector::iterator i = dp, j(dp-1); + i != m_downloads.begin(); --i, --j) + { + assert(j >= m_downloads.begin()); + if (j->finished + j->writing >= complete) return; + using std::swap; + swap(*j, *i); + } + } + void piece_picker::restore_piece(int index) { TORRENT_PIECE_PICKER_INVARIANT_CHECK; @@ -1001,10 +1023,10 @@ namespace libtorrent // prefer_whole_pieces can be set if this peer should download // whole pieces rather than trying to download blocks from the // same piece as other peers. - // the endpoint is the address of the peer we're picking pieces - // from. This is used when downloading whole pieces, to only - // pick from the same piece the same peer is downloading from. - // state is supposed to be set to fast if the peer is downloading + // the void* is the pointer to the policy::peer of the peer we're + // picking pieces from. This is used when downloading whole pieces, + // to only pick from the same piece the same peer is downloading + // from. state is supposed to be set to fast if the peer is downloading // relatively fast, by some notion. Slow peers will prefer not // to pick blocks from the same pieces as fast peers, and vice // versa. Downloading pieces are marked as being fast, medium @@ -1012,7 +1034,7 @@ namespace libtorrent void piece_picker::pick_pieces(const std::vector& pieces , std::vector& interesting_blocks , int num_blocks, bool prefer_whole_pieces - , tcp::endpoint peer, piece_state_t speed) const + , void* peer, piece_state_t speed, bool rarest_first) const { TORRENT_PIECE_PICKER_INVARIANT_CHECK; assert(num_blocks > 0); @@ -1028,6 +1050,22 @@ namespace libtorrent // blocks belonging to a piece that others have // downloaded to std::vector backup_blocks; + + bool ignore_downloading_pieces = false; + if (!prefer_whole_pieces) + { + std::vector downloading_pieces; + downloading_pieces.reserve(m_downloads.size()); + for (std::vector::const_iterator i = m_downloads.begin() + , end(m_downloads.end()); i != end; ++i) + { + downloading_pieces.push_back(i->index); + } + num_blocks = add_interesting_blocks(downloading_pieces, pieces + , interesting_blocks, backup_blocks, num_blocks + , prefer_whole_pieces, peer, speed, ignore_downloading_pieces); + ignore_downloading_pieces = true; + } // this loop will loop from pieces with priority 1 and up // until we either reach the end of the piece list or @@ -1050,9 +1088,40 @@ namespace libtorrent if (bucket->empty()) continue; num_blocks = add_interesting_blocks(*bucket, pieces , interesting_blocks, backup_blocks, num_blocks - , prefer_whole_pieces, peer, speed); + , prefer_whole_pieces, peer, speed, ignore_downloading_pieces); assert(num_blocks >= 0); if (num_blocks == 0) return; + if (rarest_first) continue; + + // we're not using rarest first (only for the first + // bucket, since that's where the currently downloading + // pieces are) + while (num_blocks > 0) + { + int start_piece = rand() % m_piece_map.size(); + int piece = start_piece; + while (!pieces[piece] + || m_piece_map[piece].index == piece_pos::we_have_index + || m_piece_map[piece].priority(m_sequenced_download_threshold) < 2) + { + ++piece; + if (piece == int(m_piece_map.size())) piece = 0; + // could not find any more pieces + if (piece == start_piece) return; + } + + assert(m_piece_map[piece].downloading == false); + + int num_blocks_in_piece = blocks_in_piece(piece); + + if (!prefer_whole_pieces && num_blocks_in_piece > num_blocks) + num_blocks_in_piece = num_blocks; + for (int j = 0; j < num_blocks_in_piece; ++j) + interesting_blocks.push_back(piece_block(piece, j)); + num_blocks -= (std::min)(num_blocks_in_piece, num_blocks); + } + if (num_blocks == 0) return; + break; } assert(num_blocks > 0); @@ -1063,22 +1132,40 @@ namespace libtorrent + (std::min)(num_blocks, (int)backup_blocks.size())); } + void piece_picker::clear_peer(void* peer) + { + for (std::vector::iterator i = m_block_info.begin() + , end(m_block_info.end()); i != end; ++i) + if (i->peer == peer) i->peer = 0; + } + namespace { - bool exclusively_requested_from(piece_picker::downloading_piece const& p - , int num_blocks_in_piece, tcp::endpoint peer) + // the first bool is true if this is the only peer that has requested and downloaded + // blocks from this piece. + // the second bool is true if this is the only active peer that is requesting + // and downloading blocks from this piece. Active means having a connection. + boost::tuple requested_from(piece_picker::downloading_piece const& p + , int num_blocks_in_piece, void* peer) { + bool exclusive = true; + bool exclusive_active = true; for (int j = 0; j < num_blocks_in_piece; ++j) { piece_picker::block_info const& info = p.info[j]; if (info.state != piece_picker::block_info::state_none - && info.peer != peer - && info.peer != tcp::endpoint()) + && info.peer != peer) { - return false; + exclusive = false; + if (info.state == piece_picker::block_info::state_requested + && info.peer != 0) + { + exclusive_active = false; + return boost::make_tuple(exclusive, exclusive_active); + } } } - return true; + return boost::make_tuple(exclusive, exclusive_active); } } @@ -1087,7 +1174,8 @@ namespace libtorrent , std::vector& interesting_blocks , std::vector& backup_blocks , int num_blocks, bool prefer_whole_pieces - , tcp::endpoint peer, piece_state_t speed) const + , void* peer, piece_state_t speed + , bool ignore_downloading_pieces) const { // if we have less than 1% of the pieces, ignore speed priorities and just try // to finish any downloading piece @@ -1106,6 +1194,7 @@ namespace libtorrent if (m_piece_map[*i].downloading == 1) { + if (ignore_downloading_pieces) continue; std::vector::const_iterator p = std::find_if(m_downloads.begin(), m_downloads.end(), has_index(*i)); assert(p != m_downloads.end()); @@ -1113,8 +1202,10 @@ namespace libtorrent // is true if all the other pieces that are currently // requested from this piece are from the same // peer as 'peer'. - bool only_same_peer = exclusively_requested_from(*p - , num_blocks_in_piece, peer); + bool exclusive; + bool exclusive_active; + boost::tie(exclusive, exclusive_active) + = requested_from(*p, num_blocks_in_piece, peer); // this means that this partial piece has // been downloaded/requested partially from @@ -1123,7 +1214,7 @@ namespace libtorrent // blocks to the backup list. If the prioritized // blocks aren't enough, blocks from this list // will be picked. - if (prefer_whole_pieces && !only_same_peer) + if (prefer_whole_pieces && !exclusive) { if (int(backup_blocks.size()) >= num_blocks) continue; for (int j = 0; j < num_blocks_in_piece; ++j) @@ -1157,7 +1248,7 @@ namespace libtorrent // if the state of the piece is none (the // piece will in that case change state). if (p->state != none && p->state != speed - && !only_same_peer + && !exclusive_active && !ignore_speed_categories) { if (int(backup_blocks.size()) >= num_blocks) continue; @@ -1193,9 +1284,7 @@ namespace libtorrent if (!prefer_whole_pieces && num_blocks_in_piece > num_blocks) num_blocks_in_piece = num_blocks; for (int j = 0; j < num_blocks_in_piece; ++j) - { interesting_blocks.push_back(piece_block(*i, j)); - } num_blocks -= (std::min)(num_blocks_in_piece, num_blocks); } assert(num_blocks >= 0); @@ -1281,7 +1370,7 @@ namespace libtorrent void piece_picker::mark_as_downloading(piece_block block - , const tcp::endpoint& peer, piece_state_t state) + , void* peer, piece_state_t state) { TORRENT_PIECE_PICKER_INVARIANT_CHECK; @@ -1303,6 +1392,7 @@ namespace libtorrent block_info& info = dp.info[block.block_index]; info.state = block_info::state_requested; info.peer = peer; + info.num_peers = 1; ++dp.requested; } else @@ -1311,14 +1401,38 @@ 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.state == block_info::state_none); + assert(info.state == block_info::state_none + || (info.state == block_info::state_requested + && (info.num_peers > 0))); info.peer = peer; - info.state = block_info::state_requested; - ++i->requested; + if (info.state != block_info::state_requested) + { + info.state = block_info::state_requested; + ++i->requested; + } + ++info.num_peers; if (i->state == none) i->state = state; } } + int piece_picker::num_peers(piece_block block) const + { + 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 const& p = m_piece_map[block.piece_index]; + if (!p.downloading) return 0; + + std::vector::const_iterator i + = std::find_if(m_downloads.begin(), m_downloads.end(), has_index(block.piece_index)); + assert(i != m_downloads.end()); + + block_info const& info = i->info[block.block_index]; + return info.num_peers; + } + void piece_picker::get_availability(std::vector& avail) const { TORRENT_PIECE_PICKER_INVARIANT_CHECK; @@ -1330,7 +1444,7 @@ namespace libtorrent *j = i->peer_count; } - void piece_picker::mark_as_writing(piece_block block, tcp::endpoint const& peer) + void piece_picker::mark_as_writing(piece_block block, void* peer) { TORRENT_PIECE_PICKER_INVARIANT_CHECK; @@ -1339,52 +1453,32 @@ namespace libtorrent 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]; - assert(p.downloading); + assert(m_piece_map[block.piece_index].downloading); -/* if (p.downloading == 0) + 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 (info.num_peers > 0) --info.num_peers; + assert(info.num_peers >= 0); + + if (i->requested == 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); - - 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; - 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; - } + // there are no blocks requested in this piece. + // remove the fast/slow state from it + i->state = none; } + sort_piece(i); } - void piece_picker::mark_as_finished(piece_block block, tcp::endpoint const& peer) + void piece_picker::mark_as_finished(piece_block block, void* peer) { assert(block.piece_index >= 0); assert(block.block_index >= 0); @@ -1397,7 +1491,7 @@ namespace libtorrent { TORRENT_PIECE_PICKER_INVARIANT_CHECK; - assert(peer == tcp::endpoint()); + assert(peer == 0); int prio = p.priority(m_sequenced_download_threshold); p.downloading = 1; if (prio > 0) move(prio, p.index); @@ -1409,9 +1503,11 @@ namespace libtorrent 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; + if (info.state != block_info::state_finished) + { + ++dp.finished; + sort_piece(m_downloads.end() - 1); + } info.state = block_info::state_finished; } else @@ -1424,22 +1520,23 @@ namespace libtorrent block_info& info = i->info[block.block_index]; info.peer = peer; assert(info.state == block_info::state_writing - || peer == tcp::endpoint()); - if (info.state == block_info::state_writing) --i->writing; + || peer == 0); assert(i->writing >= 0); ++i->finished; - info.state = block_info::state_finished; - - if (i->requested == 0) + if (info.state == block_info::state_writing) { - // there are no blocks requested in this piece. - // remove the fast/slow state from it - i->state = none; + --i->writing; + info.state = block_info::state_finished; + } + else + { + info.state = block_info::state_finished; + sort_piece(i); } } } - void piece_picker::get_downloaders(std::vector& d, int index) const + void piece_picker::get_downloaders(std::vector& d, int index) const { assert(index >= 0 && index <= (int)m_piece_map.size()); std::vector::const_iterator i @@ -1453,22 +1550,21 @@ namespace libtorrent } } - boost::optional piece_picker::get_downloader(piece_block block) const + void* piece_picker::get_downloader(piece_block block) const { std::vector::const_iterator i = std::find_if( m_downloads.begin() , m_downloads.end() , has_index(block.piece_index)); - if (i == m_downloads.end()) - return boost::optional(); + if (i == m_downloads.end()) return 0; assert(block.block_index >= 0); if (i->info[block.block_index].state == block_info::state_none) - return boost::optional(); + return 0; - return boost::optional(i->info[block.block_index].peer); + return i->info[block.block_index].peer; } void piece_picker::abort_download(piece_block block) @@ -1491,6 +1587,11 @@ namespace libtorrent , m_downloads.end(), has_index(block.piece_index)); assert(i != m_downloads.end()); + block_info& info = i->info[block.block_index]; + --info.num_peers; + assert(info.num_peers >= 0); + if (info.num_peers > 0) return; + if (i->info[block.block_index].state == block_info::state_finished || i->info[block.block_index].state == block_info::state_writing) { @@ -1501,11 +1602,11 @@ namespace libtorrent assert(i->info[block.block_index].state == block_info::state_requested); // clear this block as being downloaded - i->info[block.block_index].state = block_info::state_none; + info.state = block_info::state_none; --i->requested; // clear the downloader of this block - i->info[block.block_index].peer = tcp::endpoint(); + info.peer = 0; // if there are no other blocks in this piece // that's being downloaded, remove it from the list diff --git a/libtorrent/src/policy.cpp b/libtorrent/src/policy.cpp index eca5ba613..2444deeb7 100755 --- a/libtorrent/src/policy.cpp +++ b/libtorrent/src/policy.cpp @@ -54,6 +54,11 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/invariant_check.hpp" #include "libtorrent/time.hpp" #include "libtorrent/aux_/session_impl.hpp" +#include "libtorrent/piece_picker.hpp" + +#ifndef NDEBUG +#include "libtorrent/bt_peer_connection.hpp" +#endif namespace libtorrent { @@ -135,14 +140,14 @@ namespace struct match_peer_ip { - match_peer_ip(tcp::endpoint const& ip) + match_peer_ip(address const& ip) : m_ip(ip) {} bool operator()(policy::peer const& p) const - { return p.ip.address() == m_ip.address(); } + { return p.ip.address() == m_ip; } - tcp::endpoint const& m_ip; + address const& m_ip; }; struct match_peer_id @@ -184,11 +189,11 @@ namespace libtorrent // infinite loop, fighting to request the same blocks. void request_a_block( torrent& t - , peer_connection& c - , std::vector ignore) + , peer_connection& c) { assert(!t.is_seed()); assert(!c.has_peer_choked()); + assert(c.peer_info_struct() != 0 || !dynamic_cast(&c)); int num_requests = c.desired_queue_size() - (int)c.download_queue().size() - (int)c.request_queue().size(); @@ -205,6 +210,8 @@ namespace libtorrent bool prefer_whole_pieces = c.prefer_whole_pieces() || (c.peer_info_struct() && c.peer_info_struct()->on_parole); + bool rarest_first = t.num_pieces() >= t.settings().initial_picker_threshold; + if (!prefer_whole_pieces) { prefer_whole_pieces = c.statistics().download_payload_rate() @@ -233,7 +240,8 @@ namespace libtorrent // for this peer. If we're downloading one piece in 20 seconds // then use this mode. p.pick_pieces(c.get_bitfield(), interesting_pieces - , num_requests, prefer_whole_pieces, c.remote(), state); + , num_requests, prefer_whole_pieces, c.peer_info_struct() + , state, rarest_first); // this vector is filled with the interesting pieces // that some other peer is currently downloading @@ -248,10 +256,18 @@ namespace libtorrent { if (p.is_requested(*i)) { + // don't request pieces we already have in our request queue + const std::deque& dq = c.download_queue(); + const std::deque& rq = c.request_queue(); + if (std::find(dq.begin(), dq.end(), *i) != dq.end() + || std::find(rq.begin(), rq.end(), *i) != rq.end()) + continue; + busy_pieces.push_back(*i); continue; } + assert(p.num_peers(*i) == 0); // ok, we found a piece that's not being downloaded // by somebody else. request it from this peer // and return @@ -259,139 +275,26 @@ namespace libtorrent num_requests--; } - c.send_block_requests(); - // in this case, we could not find any blocks // that was free. If we couldn't find any busy // blocks as well, we cannot download anything // more from this peer. - if (busy_pieces.empty()) return; - - // first look for blocks that are just queued - // and not actually sent to us yet - // (then we can cancel those and request them - // from this peer instead) - - while (num_requests > 0) + if (busy_pieces.empty() || num_requests == 0) { - peer_connection* peer = 0; - - const int initial_queue_size = (int)c.download_queue().size() - + (int)c.request_queue().size(); - - // This peer's weight will be the minimum, to prevent - // cancelling requests from a faster peer. - float min_weight = initial_queue_size == 0 - ? std::numeric_limits::max() - : c.statistics().download_payload_rate() / initial_queue_size; - - // find the peer with the lowest download - // speed that also has a piece that this - // peer could send us - for (torrent::peer_iterator i = t.begin(); - i != t.end(); ++i) - { - // don't try to take over blocks from ourself - if (i->second == &c) - continue; - - // ignore all peers in the ignore list - if (std::find(ignore.begin(), ignore.end(), i->second) != ignore.end()) - continue; - - const std::deque& download_queue = i->second->download_queue(); - const std::deque& request_queue = i->second->request_queue(); - const int queue_size = (int)i->second->download_queue().size() - + (int)i->second->request_queue().size(); - - bool in_request_queue = std::find_first_of( - busy_pieces.begin() - , busy_pieces.end() - , request_queue.begin() - , request_queue.end()) != busy_pieces.end(); - - bool in_download_queue = std::find_first_of( - busy_pieces.begin() - , busy_pieces.end() - , download_queue.begin() - , download_queue.end()) != busy_pieces.end(); - - // if the block is in the request queue rather than the download queue - // (i.e. the request message hasn't been sent yet) lower the weight in - // order to prioritize it. Taking over a block in the request queue is - // free in terms of redundant download. A block that already has been - // requested is likely to be in transit already, and would in that case - // mean redundant data to receive. - const float weight = (queue_size == 0) - ? std::numeric_limits::max() - : i->second->statistics().download_payload_rate() / queue_size - * in_request_queue ? .1f : 1.f; - - // if the peer's (i) weight is less than the lowest we've found so - // far (weight == priority) and it has blocks in its request- - // or download queue that we could request from this peer (c), - // replace the currently lowest ranking peer. - if (weight < min_weight && (in_request_queue || in_download_queue)) - { - peer = i->second; - min_weight = weight; - } - } - - if (peer == 0) - { - // we probably couldn't request the block because - // we are ignoring some peers - break; - } - - // find a suitable block to take over from this peer - - std::deque::const_reverse_iterator common_block = - std::find_first_of( - peer->request_queue().rbegin() - , peer->request_queue().rend() - , busy_pieces.begin() - , busy_pieces.end()); - - if (common_block == peer->request_queue().rend()) - { - common_block = std::find_first_of( - peer->download_queue().rbegin() - , peer->download_queue().rend() - , busy_pieces.begin() - , busy_pieces.end()); - assert(common_block != peer->download_queue().rend()); - } - - piece_block block = *common_block; - - // the one we interrupted may need to request a new piece. - // make sure it doesn't take over a block from the peer - // that just took over its block (that would cause an - // infinite recursion) - peer->cancel_request(block); - c.add_request(block); - ignore.push_back(&c); - if (!peer->has_peer_choked() && !t.is_seed()) - { - request_a_block(t, *peer, ignore); - peer->send_block_requests(); - } - - num_requests--; - - const int queue_size = (int)c.download_queue().size() - + (int)c.request_queue().size(); - const float weight = queue_size == 0 - ? std::numeric_limits::max() - : c.statistics().download_payload_rate() / queue_size; - - // this peer doesn't have a faster connection than the - // slowest peer. Don't take over any blocks - if (weight <= min_weight) break; + c.send_block_requests(); + return; } + + std::random_shuffle(busy_pieces.begin(), busy_pieces.end()); + + // find the block with the fewest requests to it + std::vector::iterator i = std::min_element( + busy_pieces.begin(), busy_pieces.end() + , bind(&piece_picker::num_peers, boost::cref(p), _1) < + bind(&piece_picker::num_peers, boost::cref(p), _2)); + + c.add_request(*i); c.send_block_requests(); } @@ -680,6 +583,10 @@ namespace libtorrent if (m_torrent->is_paused()) return; + piece_picker* p = 0; + if (m_torrent->has_picker()) + p = &m_torrent->picker(); + ptime now = time_now(); // remove old disconnected peers from the list for (iterator i = m_peers.begin(); i != m_peers.end();) @@ -689,6 +596,7 @@ namespace libtorrent && i->connected != min_time() && now - i->connected > minutes(120)) { + if (p) p->clear_peer(&(*i)); m_peers.erase(i++); } else @@ -949,7 +857,7 @@ namespace libtorrent i = std::find_if( m_peers.begin() , m_peers.end() - , match_peer_ip(c.remote())); + , match_peer_ip(c.remote().address())); } if (i != m_peers.end()) @@ -974,19 +882,27 @@ namespace libtorrent "connection in favour of this one"); #endif i->connection->disconnect(); +#ifndef NDEBUG + check_invariant(); +#endif } } } else { - // we don't have ny info about this peer. + // we don't have any info about this peer. // add a new entry assert(c.remote() == c.get_socket()->remote_endpoint()); peer p(c.remote(), peer::not_connectable, 0); m_peers.push_back(p); i = boost::prior(m_peers.end()); +#ifndef NDEBUG + check_invariant(); +#endif } + + assert(m_torrent->connection_for(c.remote()) == &c); c.set_peer_info(&*i); assert(i->connection == 0); @@ -1037,7 +953,7 @@ namespace libtorrent i = std::find_if( m_peers.begin() , m_peers.end() - , match_peer_ip(remote)); + , match_peer_ip(remote.address())); } if (i == m_peers.end()) @@ -1324,7 +1240,7 @@ namespace libtorrent if (c.failed()) { ++i->failcount; - i->connected = time_now(); +// i->connected = time_now(); } // if the share ratio is 0 (infinite), the @@ -1393,17 +1309,26 @@ namespace libtorrent for (const_iterator i = m_peers.begin(); i != m_peers.end(); ++i) { + peer const& p = *i; if (!m_torrent->settings().allow_multiple_connections_per_ip) - assert(unique_test.find(i->ip.address()) == unique_test.end()); - unique_test.insert(i->ip.address()); + assert(unique_test.find(p.ip.address()) == unique_test.end()); + unique_test.insert(p.ip.address()); ++total_connections; - if (!i->connection) continue; - assert(i->connection->peer_info_struct() == 0 - || i->connection->peer_info_struct() == &*i); + if (!p.connection) continue; + if (!m_torrent->settings().allow_multiple_connections_per_ip) + { + std::vector conns; + m_torrent->connection_for(p.ip.address(), conns); + assert(std::find_if(conns.begin(), conns.end() + , boost::bind(std::equal_to(), _1, p.connection)) + != conns.end()); + } + assert(p.connection->peer_info_struct() == 0 + || p.connection->peer_info_struct() == &p); ++nonempty_connections; - if (!i->connection->is_disconnecting()) + if (!p.connection->is_disconnecting()) ++connected_peers; - if (!i->connection->is_choked()) ++actual_unchoked; + if (!p.connection->is_choked()) ++actual_unchoked; } // assert(actual_unchoked <= m_torrent->m_uploads_quota.given); assert(actual_unchoked == m_num_unchoked); @@ -1419,6 +1344,33 @@ namespace libtorrent ++num_torrent_peers; } + if (m_torrent->has_picker()) + { + piece_picker& p = m_torrent->picker(); + std::vector downloaders = p.get_download_queue(); + + std::set peer_set; + std::vector peers; + for (std::vector::iterator i = downloaders.begin() + , end(downloaders.end()); i != end; ++i) + { + p.get_downloaders(peers, i->index); + std::copy(peers.begin(), peers.end() + , std::insert_iterator >(peer_set, peer_set.begin())); + } + + for (std::set::iterator i = peer_set.begin() + , end(peer_set.end()); i != end; ++i) + { + policy::peer* p = static_cast(*i); + if (p == 0) continue; + std::list::const_iterator k = m_peers.begin(); + for (; k != m_peers.end(); ++k) + if (&(*k) == p) break; + assert(k != m_peers.end()); + } + } + // this invariant is a bit complicated. // the usual case should be that connected_peers // == num_torrent_peers. But when there's an incoming diff --git a/libtorrent/src/session_impl.cpp b/libtorrent/src/session_impl.cpp index f07f48d62..f4323586c 100755 --- a/libtorrent/src/session_impl.cpp +++ b/libtorrent/src/session_impl.cpp @@ -75,6 +75,22 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/aux_/session_impl.hpp" #include "libtorrent/kademlia/dht_tracker.hpp" +#ifndef TORRENT_DISABLE_ENCRYPTION + +#include + +namespace +{ + // openssl requires this to clean up internal + // structures it allocates + struct openssl_cleanup + { + ~openssl_cleanup() { CRYPTO_cleanup_all_ex_data(); } + } openssl_global_destructor; +} + +#endif + using boost::shared_ptr; using boost::weak_ptr; using boost::bind; @@ -1584,6 +1600,8 @@ namespace detail boost::shared_ptr t = find_torrent(ih).lock(); if (!t) return; + // don't add peers from lsd to private torrents + if (t->torrent_file().priv()) return; #if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) (*m_logger) << time_now_string() diff --git a/libtorrent/src/storage.cpp b/libtorrent/src/storage.cpp index 863cc5c07..793f95365 100755 --- a/libtorrent/src/storage.cpp +++ b/libtorrent/src/storage.cpp @@ -1019,7 +1019,11 @@ namespace libtorrent return true; } - return true; + if (!strcmp(fsinfo.f_fstypename, "hfs") + || !strcmp(fsinfo.f_fstypename, "ufs")) + return true; + + return false; #endif #if defined(__linux__) @@ -1037,7 +1041,9 @@ namespace libtorrent case 0xEF53: // EXT2 and EXT3 case 0x00011954: // UFS case 0x52654973: // ReiserFS + case 0x52345362: // Reiser4 case 0x58465342: // XFS + case 0x65735546: // NTFS-3G return true; } } diff --git a/libtorrent/src/torrent.cpp b/libtorrent/src/torrent.cpp index d173f8943..39f29172f 100755 --- a/libtorrent/src/torrent.cpp +++ b/libtorrent/src/torrent.cpp @@ -204,7 +204,7 @@ namespace libtorrent m_initial_done = 0; #endif - m_uploads_quota.min = 2; + m_uploads_quota.min = 0; m_connections_quota.min = 2; // this will be corrected the next time the main session // distributes resources, i.e. on average in 0.5 seconds @@ -277,7 +277,7 @@ namespace libtorrent if (name) m_name.reset(new std::string(name)); - m_uploads_quota.min = 2; + m_uploads_quota.min = 0; m_connections_quota.min = 2; // this will be corrected the next time the main session // distributes resources, i.e. on average in 0.5 seconds @@ -395,13 +395,22 @@ namespace libtorrent { boost::weak_ptr self(shared_from_this()); - // announce on local network every 5 minutes - m_announce_timer.expires_from_now(minutes(5)); - m_announce_timer.async_wait(m_ses.m_strand.wrap( - bind(&torrent::on_announce_disp, self, _1))); + if (!m_torrent_file.priv()) + { + // announce on local network every 5 minutes + m_announce_timer.expires_from_now(minutes(5)); + m_announce_timer.async_wait(m_ses.m_strand.wrap( + bind(&torrent::on_announce_disp, self, _1))); - // announce with the local discovery service - m_ses.announce_lsd(m_torrent_file.info_hash()); + // announce with the local discovery service + m_ses.announce_lsd(m_torrent_file.info_hash()); + } + else + { + m_announce_timer.expires_from_now(minutes(15)); + m_announce_timer.async_wait(m_ses.m_strand.wrap( + bind(&torrent::on_announce_disp, self, _1))); + } #ifndef TORRENT_DISABLE_DHT if (!m_ses.m_dht) return; @@ -914,13 +923,13 @@ namespace libtorrent // increase the total amount of failed bytes m_total_failed_bytes += m_torrent_file.piece_size(index); - std::vector downloaders; + std::vector downloaders; m_picker->get_downloaders(downloaders, index); // decrease the trust point of all peers that sent // parts of this piece. // first, build a set of all peers that participated - std::set peers; + std::set peers; std::copy(downloaders.begin(), downloaders.end(), std::inserter(peers, peers.begin())); #ifndef TORRENT_DISABLE_EXTENSIONS @@ -931,19 +940,28 @@ namespace libtorrent } #endif - for (std::set::iterator i = peers.begin() + for (std::set::iterator i = peers.begin() , end(peers.end()); i != end; ++i) { - peer_iterator p = m_connections.find(*i); - if (p == m_connections.end()) continue; - peer_connection& peer = *p->second; - peer.received_invalid_data(index); + policy::peer* p = static_cast(*i); + if (p == 0) continue; +#ifndef NDEBUG + if (!settings().allow_multiple_connections_per_ip) + { + std::vector conns; + connection_for(p->ip.address(), conns); + assert(p->connection == 0 + || std::find_if(conns.begin(), conns.end() + , boost::bind(std::equal_to(), _1, p->connection)) + != conns.end()); + } +#endif + if (p->connection) p->connection->received_invalid_data(index); // either, we have received too many failed hashes // or this was the only peer that sent us this piece. // TODO: make this a changable setting - if ((peer.peer_info_struct() - && peer.peer_info_struct()->trust_points <= -7) + if (p->trust_points <= -7 || peers.size() == 1) { // we don't trust this peer anymore @@ -951,31 +969,22 @@ namespace libtorrent if (m_ses.m_alerts.should_post(alert::info)) { m_ses.m_alerts.post_alert(peer_ban_alert( - p->first + p->ip , get_handle() , "banning peer because of too many corrupt pieces")); } // mark the peer as banned - policy::peer* peerinfo = p->second->peer_info_struct(); - if (peerinfo) - { - peerinfo->banned = true; - } - else - { - // it might be a web seed - if (web_peer_connection const* wpc - = dynamic_cast(p->second)) - { - remove_url_seed(wpc->url()); - } - } + p->banned = true; + if (p->connection) + { #if defined(TORRENT_VERBOSE_LOGGING) - (*p->second->m_logger) << "*** BANNING PEER 'too many corrupt pieces'\n"; + (*p->connection->m_logger) << "*** BANNING PEER [ " << p->ip + << " ] 'too many corrupt pieces'\n"; #endif - p->second->disconnect(); + p->connection->disconnect(); + } } } @@ -1028,12 +1037,12 @@ namespace libtorrent assert(index >= 0); assert(index < m_torrent_file.num_pieces()); - std::vector downloaders; + std::vector downloaders; m_picker->get_downloaders(downloaders, index); // increase the trust point of all peers that sent // parts of this piece. - std::set peers; + std::set peers; std::copy(downloaders.begin(), downloaders.end(), std::inserter(peers, peers.begin())); if (!m_have_pieces[index]) @@ -1047,12 +1056,16 @@ namespace libtorrent for (peer_iterator i = m_connections.begin(); i != m_connections.end(); ++i) try { i->second->announce_piece(index); } catch (std::exception&) {} - for (std::set::iterator i = peers.begin() + for (std::set::iterator i = peers.begin() , end(peers.end()); i != end; ++i) { - peer_iterator p = m_connections.find(*i); - if (p == m_connections.end()) continue; - p->second->received_valid_data(index); + policy::peer* p = static_cast(*i); + if (p == 0) continue; + p->on_parole = false; + ++p->trust_points; + // TODO: make this limit user settable + if (p->trust_points > 20) p->trust_points = 20; + if (p->connection) p->connection->received_valid_data(index); } #ifndef TORRENT_DISABLE_EXTENSIONS @@ -1364,6 +1377,15 @@ namespace libtorrent return req; } + void torrent::cancel_block(piece_block block) + { + for (peer_iterator i = m_connections.begin() + , end(m_connections.end()); i != end; ++i) + { + i->second->cancel_request(block); + } + } + void torrent::remove_peer(peer_connection* p) try { INVARIANT_CHECK; @@ -1900,6 +1922,8 @@ namespace libtorrent if (pp) p->add_extension(pp); } #endif + assert(connection_for(p->remote()) == p); + assert(ci->second == p); m_policy->new_connection(*ci->second); } catch (std::exception& e) @@ -1995,8 +2019,6 @@ namespace libtorrent , int block_size , bool non_prioritized) { - assert(m_bandwidth_limit[channel].max_assignable() >= block_size); - m_ses.m_bandwidth_manager[channel]->request_bandwidth(p , block_size, non_prioritized); m_bandwidth_limit[channel].assign(block_size); @@ -2321,14 +2343,30 @@ namespace libtorrent // size_type download = m_stat.total_payload_download(); // size_type done = boost::get<0>(bytes_done()); // assert(download >= done - m_initial_done); + std::map num_requests; for (const_peer_iterator i = begin(); i != end(); ++i) { peer_connection const& p = *i->second; + for (std::deque::const_iterator i = p.request_queue().begin() + , end(p.request_queue().end()); i != end; ++i) + ++num_requests[*i]; + for (std::deque::const_iterator i = p.download_queue().begin() + , end(p.download_queue().end()); i != end; ++i) + ++num_requests[*i]; torrent* associated_torrent = p.associated_torrent().lock().get(); if (associated_torrent != this) assert(false); } + if (has_picker()) + { + for (std::map::iterator i = num_requests.begin() + , end(num_requests.end()); i != end; ++i) + { + assert(m_picker->num_peers(i->first) == i->second); + } + } + if (valid_metadata()) { assert(m_abort || int(m_have_pieces.size()) == m_torrent_file.num_pieces()); @@ -2479,7 +2517,7 @@ namespace libtorrent #endif m_paused = false; - m_uploads_quota.min = 2; + m_uploads_quota.min = 0; m_connections_quota.min = 2; m_uploads_quota.max = std::numeric_limits::max(); m_connections_quota.max = std::numeric_limits::max(); @@ -2498,6 +2536,7 @@ namespace libtorrent m_connections_quota.used = (int)m_connections.size(); m_uploads_quota.used = m_policy->num_uploads(); + m_uploads_quota.max = (int)m_connections.size(); #ifndef TORRENT_DISABLE_EXTENSIONS for (extension_list_t::iterator i = m_extensions.begin() @@ -2674,6 +2713,8 @@ namespace libtorrent !boost::bind(&peer_connection::is_connecting , boost::bind(&std::map::value_type::second, _1))); + st.compact_mode = m_compact_mode; + st.num_complete = m_complete; st.num_incomplete = m_incomplete; st.paused = m_paused; @@ -2712,6 +2753,10 @@ namespace libtorrent = m_trackers[m_last_working_tracker].url; } + st.num_uploads = m_uploads_quota.used; + st.uploads_limit = m_uploads_quota.given; + st.num_connections = m_connections_quota.used; + st.connections_limit = m_connections_quota.given; // if we don't have any metadata, stop here if (!valid_metadata()) diff --git a/libtorrent/src/torrent_handle.cpp b/libtorrent/src/torrent_handle.cpp index c9ade14e9..10e1172fa 100755 --- a/libtorrent/src/torrent_handle.cpp +++ b/libtorrent/src/torrent_handle.cpp @@ -765,17 +765,61 @@ namespace libtorrent const std::vector& q = p.get_download_queue(); + int block_size = t->block_size(); + for (std::vector::const_iterator i = q.begin(); i != q.end(); ++i) { partial_piece_info pi; pi.piece_state = (partial_piece_info::state_t)i->state; pi.blocks_in_piece = p.blocks_in_piece(i->index); + int piece_size = t->torrent_file().piece_size(i->index); for (int j = 0; j < pi.blocks_in_piece; ++j) { - 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; + block_info& bi = pi.blocks[j]; + bi.state = i->info[j].state; + bi.block_size = j < pi.blocks_in_piece - 1 ? block_size + : piece_size - (j * block_size); + bool complete = bi.state == block_info::writing + || bi.state == block_info::finished; + if (i->info[j].peer == 0) + { + bi.peer = tcp::endpoint(); + bi.bytes_progress = complete ? bi.block_size : 0; + } + else + { + policy::peer* p = static_cast(i->info[j].peer); + if (p->connection) + { + bi.peer = p->connection->remote(); + if (bi.state == block_info::requested) + { + boost::optional pbp + = p->connection->downloading_piece_progress(); + if (pbp && pbp->piece_index == i->index && pbp->block_index == j) + { + bi.bytes_progress = pbp->bytes_downloaded; + assert(bi.bytes_progress <= bi.block_size); + } + else + { + bi.bytes_progress = 0; + } + } + else + { + bi.bytes_progress = complete ? bi.block_size : 0; + } + } + else + { + bi.peer = p->ip; + bi.bytes_progress = complete ? bi.block_size : 0; + } + } + + pi.blocks[j].num_peers = i->info[j].num_peers; } pi.piece_index = i->index; queue.push_back(pi);