diff --git a/libtorrent/include/libtorrent/aux_/session_impl.hpp b/libtorrent/include/libtorrent/aux_/session_impl.hpp index e1345fab6..cc24d6987 100644 --- a/libtorrent/include/libtorrent/aux_/session_impl.hpp +++ b/libtorrent/include/libtorrent/aux_/session_impl.hpp @@ -549,6 +549,10 @@ namespace libtorrent udp_socket m_dht_socket; + // these are used when starting the DHT + // (and bootstrapping it), and then erased + std::list > m_dht_router_nodes; + void on_receive_udp(error_code const& e , udp::endpoint const& ep, char const* buf, int len); #endif diff --git a/libtorrent/include/libtorrent/http_tracker_connection.hpp b/libtorrent/include/libtorrent/http_tracker_connection.hpp index 04701fc00..ec34260b0 100644 --- a/libtorrent/include/libtorrent/http_tracker_connection.hpp +++ b/libtorrent/include/libtorrent/http_tracker_connection.hpp @@ -75,6 +75,7 @@ namespace libtorrent , proxy_settings const& ps , std::string const& password = ""); + void start(); void close(); private: @@ -92,6 +93,11 @@ namespace libtorrent tracker_manager& m_man; boost::shared_ptr m_tracker_connection; + session_settings const& m_settings; + address m_bind_iface; + proxy_settings const& m_ps; + connection_queue& m_cc; + io_service& m_ios; }; } diff --git a/libtorrent/include/libtorrent/kademlia/dht_tracker.hpp b/libtorrent/include/libtorrent/kademlia/dht_tracker.hpp index ef07b1b91..7b4a9e71f 100644 --- a/libtorrent/include/libtorrent/kademlia/dht_tracker.hpp +++ b/libtorrent/include/libtorrent/kademlia/dht_tracker.hpp @@ -71,8 +71,9 @@ namespace libtorrent { namespace dht { friend void intrusive_ptr_add_ref(dht_tracker const*); friend void intrusive_ptr_release(dht_tracker const*); - dht_tracker(udp_socket& sock, dht_settings const& settings - , entry const& bootstrap); + dht_tracker(udp_socket& sock, dht_settings const& settings); + + void start(entry const& bootstrap); void stop(); void add_node(udp::endpoint node); diff --git a/libtorrent/include/libtorrent/kademlia/node.hpp b/libtorrent/include/libtorrent/kademlia/node.hpp index 88a1677f1..8b59e0ce8 100644 --- a/libtorrent/include/libtorrent/kademlia/node.hpp +++ b/libtorrent/include/libtorrent/kademlia/node.hpp @@ -161,7 +161,7 @@ class node_impl : boost::noncopyable typedef std::map table_t; public: node_impl(boost::function const& f - , dht_settings const& settings, boost::optional node_id); + , dht_settings const& settings); virtual ~node_impl() {} @@ -186,7 +186,9 @@ public: typedef table_t::iterator data_iterator; + void set_node_id(node_id const& nid) { m_id = nid; } node_id const& nid() const { return m_id; } + boost::tuple size() const{ return m_table.size(); } size_type num_global_nodes() const { return m_table.num_global_nodes(); } diff --git a/libtorrent/include/libtorrent/peer_connection.hpp b/libtorrent/include/libtorrent/peer_connection.hpp index cb4f30e40..4be5d772b 100644 --- a/libtorrent/include/libtorrent/peer_connection.hpp +++ b/libtorrent/include/libtorrent/peer_connection.hpp @@ -151,7 +151,7 @@ namespace libtorrent policy::peer* peer_info_struct() const { return m_peer_info; } - enum peer_speed_t { slow, medium, fast }; + enum peer_speed_t { slow = 1, medium, fast }; peer_speed_t peer_speed(); void send_allowed_set(); @@ -187,6 +187,8 @@ namespace libtorrent bool on_parole() const { return peer_info_struct() && peer_info_struct()->on_parole; } + int picker_options() const; + void prefer_whole_pieces(int num) { m_prefer_whole_pieces = num; } @@ -213,7 +215,12 @@ namespace libtorrent bool is_seed() const; - void set_upload_only(bool u) { m_upload_only = u; } + void set_upload_only(bool u) + { + m_upload_only = u; + disconnect_if_redundant(); + } + bool upload_only() const { return m_upload_only; } // will send a keep-alive message to the peer diff --git a/libtorrent/include/libtorrent/piece_picker.hpp b/libtorrent/include/libtorrent/piece_picker.hpp index 5bcae3c96..f9f4985e4 100644 --- a/libtorrent/include/libtorrent/piece_picker.hpp +++ b/libtorrent/include/libtorrent/piece_picker.hpp @@ -88,6 +88,14 @@ namespace libtorrent { public: + enum + { + // the number of priority levels + priority_levels = 8, + // priority factor + prio_factor = priority_levels - 4 + }; + struct block_info { block_info(): peer(0), num_peers(0), state(state_none) {} @@ -110,6 +118,20 @@ namespace libtorrent enum piece_state_t { none, slow, medium, fast }; + enum options_t + { + // pick rarest first + rarest_first = 1, + // pick the most common first, or the last pieces if sequential + reverse = 2, + // only pick pieces exclusively requested from this peer + on_parole = 4, + // always pick partial pieces before any other piece + prioritize_partials = 8, + // pick pieces in sequential order + sequential = 16 + }; + struct downloading_piece { downloading_piece(): finished(0), writing(0), requested(0) {} @@ -133,9 +155,6 @@ namespace libtorrent void get_availability(std::vector& avail) const; - void sequential_download(bool sd); - bool sequential_download() const { return m_sequential_download >= 0; } - // increases the peer count for the given piece // (is used when a HAVE message is received) void inc_refcount(int index); @@ -161,6 +180,9 @@ namespace libtorrent void we_have(int index); void we_dont_have(int index); + int cursor() const { return m_cursor; } + int reverse_cursor() const { return m_reverse_cursor; } + // sets all pieces to dont-have void init(int blocks_per_piece, int total_num_blocks); int num_pieces() const { return int(m_piece_map.size()); } @@ -202,11 +224,9 @@ namespace libtorrent // The last argument is the policy::peer pointer for the peer that // we'll download from. void pick_pieces(bitfield const& pieces - , std::vector& interesting_blocks - , int num_pieces, int prefer_whole_pieces - , void* peer, piece_state_t speed - , bool rarest_first, bool on_parole - , std::vector const& suggested_pieces) const; + , std::vector& interesting_blocks, int num_blocks + , int prefer_whole_pieces, void* peer, piece_state_t speed + , int options, std::vector const& suggested_pieces) const; // picks blocks from each of the pieces in the piece_list // vector that is also in the piece bitmask. The blocks @@ -214,20 +234,23 @@ namespace libtorrent // added to backup_blocks. num blocks is the number of // blocks to be picked. Blocks are not picked from pieces // that are being downloaded - int add_blocks(std::vector const& piece_list - , bitfield const& pieces - , std::vector& interesting_blocks - , int num_blocks, int prefer_whole_pieces - , void* peer, std::vector const& ignore) const; - - // picks blocks only from downloading pieces - int add_blocks_downloading( - bitfield const& pieces + int add_blocks(int piece, bitfield const& pieces , std::vector& interesting_blocks , std::vector& backup_blocks + , std::vector& backup_blocks2 + , int num_blocks, int prefer_whole_pieces + , void* peer, std::vector const& ignore + , piece_state_t speed, int options) const; + + // picks blocks only from downloading pieces + int add_blocks_downloading(downloading_piece const& dp + , bitfield const& pieces + , std::vector& interesting_blocks + , std::vector& backup_blocks + , std::vector& backup_blocks2 , int num_blocks, int prefer_whole_pieces , void* peer, piece_state_t speed - , bool on_parole) const; + , int options) const; // clears the peer pointer in all downloading pieces with this // peer pointer @@ -291,6 +314,8 @@ namespace libtorrent void check_invariant(const torrent* t = 0) const; void verify_pick(std::vector const& picked , bitfield const& bits) const; +#endif +#if defined TORRENT_PICKER_LOG || !defined NDEBUG void print_pieces() const; #endif @@ -313,6 +338,7 @@ namespace libtorrent friend struct piece_pos; bool can_pick(int piece, bitfield const& bitmask) const; + bool is_piece_free(int piece, bitfield const& bitmask) const; std::pair expand_piece(int piece, int whole_pieces , bitfield const& have) const; @@ -354,7 +380,7 @@ namespace libtorrent // the priority value that means the piece is filtered filter_priority = 0, // the max number the peer count can hold - max_peer_count = 0x3ff + max_peer_count = 0x3ff, }; bool have() const { return index == we_have_index; } @@ -364,20 +390,41 @@ namespace libtorrent bool filtered() const { return piece_priority == filter_priority; } void filtered(bool f) { piece_priority = f ? filter_priority : 0; } + // prio 7 is always top priority + // prio 0 is always -1 (don't pick) + // downloading pieces are always on an even prio_factor priority + // + // availability x, downloading + // | availability x, prio 3; availability 2x, prio 6 + // | | availability x, prio 2; availability 2x, prio 5 + // | | | availability x, prio 1; availability 2x, prio 4 + // | | | | + // +---+---+---+---+ + // | 0 | 1 | 2 | 3 | + // +---+---+---+---+ + int priority(piece_picker const* picker) const { - if (downloading || filtered() - || have() || peer_count + picker->m_seeds == 0) + // filtered pieces (prio = 0), pieces we have or pieces with + // availability = 0 should not be present in the piece list + // returning -1 indicates that they shouldn't. + if (filtered() || have() || peer_count + picker->m_seeds == 0) return -1; - // priority 5, 6 and 7 disregards availability of the piece - if (piece_priority > 4) return 7 - piece_priority; + // prio 7 disregards availability + if (piece_priority == priority_levels - 1) return 1 - downloading; - // pieces we are currently downloading have high priority - int prio = peer_count * 4; -// if (prio >= picker->m_prio_limit * 6) prio = picker->m_prio_limit * 6; + // prio 4,5,6 halves the availability of a piece + int availability = peer_count; + int priority = piece_priority; + if (piece_priority >= priority_levels / 2) + { + availability /= 2; + priority -= (priority_levels - 2) / 2; + } - return prio + (4 - piece_priority); + if (downloading) return availability * prio_factor; + return availability * prio_factor + (priority_levels / 2) - priority; } bool operator!=(piece_pos p) const @@ -467,11 +514,14 @@ namespace libtorrent // the number of pieces we have int m_num_have; - // -1 means sequential download is not active. - // >= 0 means that pieces are requested in sequential order - // and this variable is the next piece to request. - // in that case m_pieces is cleared and not used. - int m_sequential_download; + // we have all pieces in the range [0, m_cursor) + // m_cursor is the first piece we don't have + int m_cursor; + + // we have all pieces in the range [m_reverse_cursor, end) + // m_reverse_cursor is the first piece where we also have + // all the subsequent pieces + int m_reverse_cursor; // if this is set to true, it means update_pieces() // has to be called before accessing m_pieces. diff --git a/libtorrent/include/libtorrent/session_settings.hpp b/libtorrent/include/libtorrent/session_settings.hpp index 9aafbb921..071e0d5c0 100644 --- a/libtorrent/include/libtorrent/session_settings.hpp +++ b/libtorrent/include/libtorrent/session_settings.hpp @@ -141,6 +141,7 @@ namespace libtorrent , auto_scrape_min_interval(300) , max_peerlist_size(8000) , min_announce_interval(5 * 60) + , prioritize_partial_pieces(false) {} // this is the user agent that will be sent to the tracker @@ -436,6 +437,10 @@ namespace libtorrent // that is lower than this, will be clamped to this // value. It's specified in seconds int min_announce_interval; + + // if true, partial pieces are picked before pieces + // that are more rare + bool prioritize_partial_pieces; }; #ifndef TORRENT_DISABLE_DHT diff --git a/libtorrent/include/libtorrent/storage.hpp b/libtorrent/include/libtorrent/storage.hpp index d2847cb8e..f144b5fc8 100644 --- a/libtorrent/include/libtorrent/storage.hpp +++ b/libtorrent/include/libtorrent/storage.hpp @@ -259,6 +259,8 @@ namespace libtorrent disk_check_aborted = -3 }; + storage_interface* get_storage_impl() { return m_storage.get(); } + private: fs::path save_path() const; diff --git a/libtorrent/include/libtorrent/torrent.hpp b/libtorrent/include/libtorrent/torrent.hpp index c812aed33..7c8a69020 100644 --- a/libtorrent/include/libtorrent/torrent.hpp +++ b/libtorrent/include/libtorrent/torrent.hpp @@ -170,6 +170,12 @@ namespace libtorrent int seed_rank(session_settings const& s) const; storage_mode_t storage_mode() const { return m_storage_mode; } + storage_interface* get_storage() + { + if (!m_owning_storage) return 0; + return m_owning_storage->get_storage_impl(); + } + // this will flag the torrent as aborted. The main // loop in session_impl will check for this state // on all torrents once every second, and take diff --git a/libtorrent/include/libtorrent/torrent_handle.hpp b/libtorrent/include/libtorrent/torrent_handle.hpp index 97d8edb37..87b3e984a 100644 --- a/libtorrent/include/libtorrent/torrent_handle.hpp +++ b/libtorrent/include/libtorrent/torrent_handle.hpp @@ -366,6 +366,8 @@ namespace libtorrent bool resolve_countries() const; #endif + storage_interface* get_storage_impl() const; + // all these are deprecated, use piece // priority functions instead diff --git a/libtorrent/include/libtorrent/tracker_manager.hpp b/libtorrent/include/libtorrent/tracker_manager.hpp index c9d1f52c6..8b3781bf8 100644 --- a/libtorrent/include/libtorrent/tracker_manager.hpp +++ b/libtorrent/include/libtorrent/tracker_manager.hpp @@ -194,6 +194,7 @@ namespace libtorrent void fail(int code, char const* msg); void fail_timeout(); + virtual void start() = 0; virtual void close(); address const& bind_interface() const { return m_bind_interface; } diff --git a/libtorrent/include/libtorrent/udp_tracker_connection.hpp b/libtorrent/include/libtorrent/udp_tracker_connection.hpp index dd0dea9fe..fe51df1fb 100644 --- a/libtorrent/include/libtorrent/udp_tracker_connection.hpp +++ b/libtorrent/include/libtorrent/udp_tracker_connection.hpp @@ -74,6 +74,7 @@ namespace libtorrent , session_settings const& stn , proxy_settings const& ps); + void start(); void close(); private: diff --git a/libtorrent/src/alert.cpp b/libtorrent/src/alert.cpp index 844cfb93b..3e65d2b08 100755 --- a/libtorrent/src/alert.cpp +++ b/libtorrent/src/alert.cpp @@ -74,8 +74,9 @@ namespace libtorrent { xt.sec += 1; } xt.nsec = boost::xtime::xtime_nsec_t(nsec); + // apparently this call can be interrupted + // prematurely if there are other signals if (!m_condition.timed_wait(lock, xt)) return 0; - TORRENT_ASSERT(!m_alerts.empty()); if (m_alerts.empty()) return 0; return m_alerts.front(); } diff --git a/libtorrent/src/bt_peer_connection.cpp b/libtorrent/src/bt_peer_connection.cpp index 4f8992a03..9d1b7f9e5 100755 --- a/libtorrent/src/bt_peer_connection.cpp +++ b/libtorrent/src/bt_peer_connection.cpp @@ -945,7 +945,7 @@ namespace libtorrent { std::stringstream msg; msg << "got bitfield with invalid size: " << (packet_size() - 1) - << "bytes. expected: " << ((t->torrent_file().num_pieces() + 7) / 8) + << " bytes. expected: " << ((t->torrent_file().num_pieces() + 7) / 8) << " bytes"; disconnect(msg.str().c_str(), 2); return; @@ -1444,7 +1444,7 @@ namespace libtorrent { // don't send a bitfield if we don't have any pieces #ifdef TORRENT_VERBOSE_LOGGING - (*m_logger) << time_now_string() << " *** NOT SENDING BITFIELD"; + (*m_logger) << time_now_string() << " *** NOT SENDING BITFIELD\n"; #endif #ifndef NDEBUG m_sent_bitfield = true; diff --git a/libtorrent/src/http_tracker_connection.cpp b/libtorrent/src/http_tracker_connection.cpp index 6a58c477b..f83163bd2 100755 --- a/libtorrent/src/http_tracker_connection.cpp +++ b/libtorrent/src/http_tracker_connection.cpp @@ -79,11 +79,19 @@ namespace libtorrent , std::string const& auth) : tracker_connection(man, req, ios, bind_infc, c) , m_man(man) + , m_settings(stn) + , m_bind_iface(bind_infc) + , m_ps(ps) + , m_cc(cc) + , m_ios(ios) + {} + + void http_tracker_connection::start() { // TODO: authentication - std::string url = req.url; + std::string url = tracker_req().url; - if (req.kind == tracker_request::scrape_request) + if (tracker_req().kind == tracker_request::scrape_request) { // find and replace "announce" with "scrape" // in request @@ -91,8 +99,8 @@ namespace libtorrent std::size_t pos = url.find("announce"); if (pos == std::string::npos) { - //fail(-1, ("scrape is not available on url: '" - // + req.url +"'").c_str()); + fail(-1, ("scrape is not available on url: '" + + tracker_req().url +"'").c_str()); return; } url.replace(pos, 8, "scrape"); @@ -109,70 +117,70 @@ namespace libtorrent url += "info_hash="; url += escape_string( - reinterpret_cast(req.info_hash.begin()), 20); + reinterpret_cast(tracker_req().info_hash.begin()), 20); - if (req.kind == tracker_request::announce_request) + if (tracker_req().kind == tracker_request::announce_request) { url += "&peer_id="; url += escape_string( - reinterpret_cast(req.pid.begin()), 20); + reinterpret_cast(tracker_req().pid.begin()), 20); url += "&port="; - url += boost::lexical_cast(req.listen_port); + url += boost::lexical_cast(tracker_req().listen_port); url += "&uploaded="; - url += boost::lexical_cast(req.uploaded); + url += boost::lexical_cast(tracker_req().uploaded); url += "&downloaded="; - url += boost::lexical_cast(req.downloaded); + url += boost::lexical_cast(tracker_req().downloaded); url += "&left="; - url += boost::lexical_cast(req.left); + url += boost::lexical_cast(tracker_req().left); - if (req.event != tracker_request::none) + if (tracker_req().event != tracker_request::none) { const char* event_string[] = {"completed", "started", "stopped"}; url += "&event="; - url += event_string[req.event - 1]; + url += event_string[tracker_req().event - 1]; } url += "&key="; std::stringstream key_string; - key_string << std::hex << req.key; + key_string << std::hex << tracker_req().key; url += key_string.str(); url += "&compact=1"; url += "&numwant="; url += boost::lexical_cast( - (std::min)(req.num_want, 999)); + (std::min)(tracker_req().num_want, 999)); - if (stn.announce_ip != address()) + if (m_settings.announce_ip != address()) { url += "&ip="; - url += stn.announce_ip.to_string(); + url += m_settings.announce_ip.to_string(); } #ifndef TORRENT_DISABLE_ENCRYPTION url += "&supportcrypto=1"; #endif url += "&ipv6="; - url += req.ipv6; + url += tracker_req().ipv6; // extension that tells the tracker that // we don't need any peer_id's in the response url += "&no_peer_id=1"; } - m_tracker_connection.reset(new http_connection(ios, cc + m_tracker_connection.reset(new http_connection(m_ios, m_cc , boost::bind(&http_tracker_connection::on_response, self(), _1, _2, _3, _4))); - int timeout = req.event==tracker_request::stopped - ?stn.stop_tracker_timeout - :stn.tracker_completion_timeout; + int timeout = tracker_req().event==tracker_request::stopped + ?m_settings.stop_tracker_timeout + :m_settings.tracker_completion_timeout; m_tracker_connection->get(url, seconds(timeout) - , 1, &ps, 5, stn.user_agent, bind_infc); + , 1, &m_ps, 5, m_settings.user_agent, m_bind_iface); #if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) diff --git a/libtorrent/src/kademlia/dht_tracker.cpp b/libtorrent/src/kademlia/dht_tracker.cpp index 34d55bb69..dbd917706 100644 --- a/libtorrent/src/kademlia/dht_tracker.cpp +++ b/libtorrent/src/kademlia/dht_tracker.cpp @@ -83,21 +83,6 @@ namespace } }; - boost::optional read_id(libtorrent::entry const& d) - { - using namespace libtorrent; - using libtorrent::dht::node_id; - - if (d.type() != entry::dictionary_t) return boost::optional(); - entry const* nid = d.find_key("node-id"); - if (!nid - || nid->type() != entry::string_t - || nid->string().length() != 40) - return boost::optional(); - return boost::optional( - boost::lexical_cast(nid->string())); - } - template void read_endpoint_list(libtorrent::entry const* n, std::vector& epl) { @@ -142,10 +127,8 @@ namespace libtorrent { namespace dht // class that puts the networking and the kademlia node in a single // unit and connecting them together. - dht_tracker::dht_tracker(udp_socket& sock, dht_settings const& settings - , entry const& bootstrap) - : m_dht(bind(&dht_tracker::send_packet, this, _1), settings - , read_id(bootstrap)) + dht_tracker::dht_tracker(udp_socket& sock, dht_settings const& settings) + : m_dht(bind(&dht_tracker::send_packet, this, _1), settings) , m_sock(sock) , m_last_new_key(time_now() - minutes(key_refresh)) , m_timer(sock.get_io_service()) @@ -185,6 +168,10 @@ namespace libtorrent { namespace dht // dht_tracker_log.enable(false); #endif + } + + void dht_tracker::start(entry const& bootstrap) + { std::vector initial_nodes; if (bootstrap.type() == entry::dictionary_t) @@ -194,6 +181,12 @@ namespace libtorrent { namespace dht if (entry const* nodes = bootstrap.find_key("nodes")) read_endpoint_list(nodes, initial_nodes); } catch (std::exception&) {} + + entry const* nid = bootstrap.find_key("node-id"); + if (nid + && nid->type() == entry::string_t + && nid->string().length() == 40) + m_dht.set_node_id(boost::lexical_cast(nid->string())); } m_timer.expires_from_now(seconds(1)); @@ -465,6 +458,8 @@ namespace libtorrent { namespace dht m.transaction_id = e["t"].string(); #ifdef TORRENT_DHT_VERBOSE_LOGGING + log_line << " t: " << to_hex(m.transaction_id); + try { entry const* ver = e.find_key("v"); @@ -512,8 +507,7 @@ namespace libtorrent { namespace dht if (msg_type == "r") { #ifdef TORRENT_DHT_VERBOSE_LOGGING - log_line << " r: " << messages::ids[m.message_id] - << " t: " << to_hex(m.transaction_id); + log_line << " r: " << messages::ids[m.message_id]; #endif m.reply = true; @@ -616,7 +610,7 @@ namespace libtorrent { namespace dht if (target.size() != 20) throw std::runtime_error("invalid size of target id"); std::copy(target.begin(), target.end(), m.info_hash.begin()); #ifdef TORRENT_DHT_VERBOSE_LOGGING - log_line << " t: " << boost::lexical_cast(m.info_hash); + log_line << " target: " << boost::lexical_cast(m.info_hash); #endif m.message_id = libtorrent::dht::messages::find_node; diff --git a/libtorrent/src/kademlia/node.cpp b/libtorrent/src/kademlia/node.cpp index 63a8ac84b..67c6174e2 100644 --- a/libtorrent/src/kademlia/node.cpp +++ b/libtorrent/src/kademlia/node.cpp @@ -91,9 +91,9 @@ void purge_peers(std::set& peers) void nop() {} node_impl::node_impl(boost::function const& f - , dht_settings const& settings, boost::optional node_id) + , dht_settings const& settings) : m_settings(settings) - , m_id(node_id ? *node_id : generate_id()) + , m_id(generate_id()) , m_table(m_id, 8, settings) , m_rpc(bind(&node_impl::incoming_request, this, _1) , m_id, m_table, f) diff --git a/libtorrent/src/peer_connection.cpp b/libtorrent/src/peer_connection.cpp index 34e1995c3..5ce04bcf4 100755 --- a/libtorrent/src/peer_connection.cpp +++ b/libtorrent/src/peer_connection.cpp @@ -572,6 +572,50 @@ namespace libtorrent #endif } + int peer_connection::picker_options() const + { + int ret = 0; + boost::shared_ptr t = m_torrent.lock(); + TORRENT_ASSERT(t); + if (!t) return 0; + + if (t->is_sequential_download()) + { + ret |= piece_picker::sequential; + } + else if (t->num_have() < t->settings().initial_picker_threshold) + { + // if we have fewer pieces than a certain threshols + // don't pick rare pieces, just pick random ones, + // and prioritize finishing them + ret |= piece_picker::prioritize_partials; + } + else + { + ret |= piece_picker::rarest_first; + } + + if (m_snubbed) + { + // snubbed peers should request + // the common pieces first, just to make + // it more likely for all snubbed peers to + // request blocks from the same piece + ret |= piece_picker::reverse; + } + + if (t->settings().prioritize_partial_pieces) + ret |= piece_picker::prioritize_partials; + + if (on_parole()) ret |= piece_picker::on_parole + | piece_picker::prioritize_partials; + + // only one of rarest_first, common_first and sequential can be set. + TORRENT_ASSERT(bool(ret & piece_picker::rarest_first) + + bool(ret & piece_picker::sequential) <= 1); + return ret; + } + void peer_connection::fast_reconnect(bool r) { if (!peer_info_struct() || peer_info_struct()->fast_reconnects > 1) @@ -1129,6 +1173,8 @@ namespace libtorrent << " <== HAVE [ piece: " << index << "]\n"; #endif + if (is_disconnecting()) return; + if (!t->valid_metadata() && index > int(m_have_piece.size())) { if (index < 65536) @@ -1343,6 +1389,7 @@ namespace libtorrent // if we haven't received a bitfield, it was // probably omitted, which is the same as 'have_none' if (!m_bitfield_received) incoming_have_none(); + if (is_disconnecting()) return; #ifndef TORRENT_DISABLE_EXTENSIONS for (extension_list_t::iterator i = m_extensions.begin() @@ -1549,6 +1596,7 @@ namespace libtorrent // if we haven't received a bitfield, it was // probably omitted, which is the same as 'have_none' if (!m_bitfield_received) incoming_have_none(); + if (is_disconnecting()) return; #ifndef TORRENT_DISABLE_EXTENSIONS for (extension_list_t::iterator i = m_extensions.begin() @@ -1572,7 +1620,7 @@ namespace libtorrent "s: " << p.start << " | " "l: " << p.length << " | " "ds: " << statistics().download_rate() << " | " - "qs: " << m_desired_queue_size << " ]\n"; + "qs: " << int(m_desired_queue_size) << " ]\n"; #endif if (p.length == 0) @@ -1639,7 +1687,7 @@ namespace libtorrent return; } - int block_index = b - m_download_queue.begin(); + int block_index = b - m_download_queue.begin() - 1; for (int i = 0; i < block_index; ++i) { pending_block& qe = m_download_queue[i]; @@ -1660,6 +1708,7 @@ namespace libtorrent m_ses.m_alerts.post_alert(request_dropped_alert(t->get_handle() , remote(), pid(), qe.block.block_index, qe.block.piece_index)); picker.abort_download(qe.block); + TORRENT_ASSERT(m_download_queue.begin() + i != b); m_download_queue.erase(m_download_queue.begin() + i); --i; --block_index; @@ -1723,9 +1772,12 @@ namespace libtorrent bool multi = picker.num_peers(block_finished) > 1; picker.mark_as_writing(block_finished, peer_info_struct()); + TORRENT_ASSERT(picker.num_peers(block_finished) == 0); // if we requested this block from other peers, cancel it now if (multi) t->cancel_block(block_finished); + TORRENT_ASSERT(picker.num_peers(block_finished) == 0); + #if !defined NDEBUG && !defined TORRENT_DISABLE_INVARIANT_CHECKS t->check_invariant(); #endif @@ -1776,6 +1828,7 @@ namespace libtorrent TORRENT_ASSERT(p.piece == j.piece); TORRENT_ASSERT(p.start == j.offset); + TORRENT_ASSERT(picker.num_peers(block_finished) == 0); picker.mark_as_finished(block_finished, peer_info_struct()); if (t->alerts().should_post()) { @@ -2332,7 +2385,7 @@ namespace libtorrent "s: " << r.start << " | " "l: " << r.length << " | " "ds: " << statistics().download_rate() << " B/s | " - "qs: " << m_desired_queue_size << " " + "qs: " << int(m_desired_queue_size) << " " "blk: " << (m_request_large_blocks?"large":"single") << " ]\n"; #endif } @@ -3898,7 +3951,7 @@ namespace libtorrent m_speed = medium; else if (download_rate < torrent_download_rate / 15 && m_speed == fast) m_speed = medium; - else if (download_rate < torrent_download_rate / 63 && m_speed == medium) + else m_speed = slow; return m_speed; diff --git a/libtorrent/src/piece_picker.cpp b/libtorrent/src/piece_picker.cpp index bb0b460a3..f2ee3d450 100755 --- a/libtorrent/src/piece_picker.cpp +++ b/libtorrent/src/piece_picker.cpp @@ -46,9 +46,9 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/torrent.hpp" #endif -//#define TORRENT_PIECE_PICKER_INVARIANT_CHECK INVARIANT_CHECK +#define TORRENT_PIECE_PICKER_INVARIANT_CHECK INVARIANT_CHECK //#define TORRENT_NO_EXPENSIVE_INVARIANT_CHECK -#define TORRENT_PIECE_PICKER_INVARIANT_CHECK +//#define TORRENT_PIECE_PICKER_INVARIANT_CHECK //#define TORRENT_PICKER_LOG @@ -58,10 +58,13 @@ namespace libtorrent piece_picker::piece_picker() : m_seeds(0) , m_priority_boundries(1, int(m_pieces.size())) + , m_blocks_per_piece(0) + , m_blocks_in_last_piece(0) , m_num_filtered(0) , m_num_have_filtered(0) , m_num_have(0) - , m_sequential_download(-1) + , m_cursor(0) + , m_reverse_cursor(0) , m_dirty(false) { #ifdef TORRENT_PICKER_LOG @@ -77,10 +80,15 @@ namespace libtorrent TORRENT_ASSERT(blocks_per_piece > 0); TORRENT_ASSERT(total_num_blocks >= 0); +#ifdef TORRENT_PICKER_LOG + std::cerr << "piece_picker::init()" << std::endl; +#endif // allocate the piece_map to cover all pieces // and make them invalid (as if we don't have a single piece) m_piece_map.resize((total_num_blocks + blocks_per_piece-1) / blocks_per_piece , piece_pos(0, 0)); + m_reverse_cursor = int(m_piece_map.size()); + m_cursor = 0; m_num_filtered += m_num_have_filtered; m_num_have_filtered = 0; @@ -94,10 +102,16 @@ namespace libtorrent i->index = 0; } + for (std::vector::const_iterator i = m_piece_map.begin() + m_cursor + , end(m_piece_map.end()); i != end && (i->have() || i->filtered()); + ++i, ++m_cursor); + for (std::vector::const_iterator i = m_piece_map.begin() + + m_reverse_cursor - 1; m_reverse_cursor > 0 && (i->have() || i->filtered()); + --i, --m_reverse_cursor); + // the piece index is stored in 20 bits, which limits the allowed // number of pieces somewhat - if (m_piece_map.size() >= piece_pos::we_have_index) - throw std::runtime_error("too many pieces in torrent"); + TORRENT_ASSERT(m_piece_map.size() < piece_pos::we_have_index); m_blocks_per_piece = blocks_per_piece; m_blocks_in_last_piece = total_num_blocks % blocks_per_piece; @@ -106,30 +120,6 @@ namespace libtorrent TORRENT_ASSERT(m_blocks_in_last_piece <= m_blocks_per_piece); } - void piece_picker::sequential_download(bool sd) - { - if (sd == sequential_download()) return; - - TORRENT_PIECE_PICKER_INVARIANT_CHECK; - - if (sd) - { - std::vector().swap(m_pieces); - std::vector().swap(m_priority_boundries); - - // initialize m_sdquential_download - m_sequential_download = 0; - for (std::vector::const_iterator i = m_piece_map.begin() - , end(m_piece_map.end()); i != end && (i->have() || i->filtered()); - ++i, ++m_sequential_download); - } - else - { - m_sequential_download = -1; - m_dirty = true; - } - } - void piece_picker::piece_info(int index, piece_picker::downloading_piece& st) const { TORRENT_PIECE_PICKER_INVARIANT_CHECK; @@ -234,7 +224,7 @@ namespace libtorrent } } -#ifdef TORRENT_PICKER_LOG +#if defined TORRENT_PICKER_LOG || !defined NDEBUG void piece_picker::print_pieces() const { for (std::vector::const_iterator i = m_priority_boundries.begin() @@ -314,11 +304,19 @@ namespace libtorrent TORRENT_ASSERT(num_writing == i->writing); TORRENT_ASSERT(num_finished == i->finished); } + int num_pieces = int(m_piece_map.size()); + TORRENT_ASSERT(m_cursor >= 0); + TORRENT_ASSERT(m_cursor <= num_pieces); + TORRENT_ASSERT(m_reverse_cursor <= num_pieces); + TORRENT_ASSERT(m_reverse_cursor >= 0); + TORRENT_ASSERT(m_reverse_cursor > m_cursor + || (m_cursor == num_pieces && m_reverse_cursor == 0)); + #ifdef TORRENT_NO_EXPENSIVE_INVARIANT_CHECK return; #endif - if (m_sequential_download == -1 && !m_dirty) + if (!m_dirty) { TORRENT_ASSERT(!m_priority_boundries.empty()); int prio = 0; @@ -332,13 +330,24 @@ namespace libtorrent } TORRENT_ASSERT(m_priority_boundries.back() == int(m_pieces.size())); } - else if (m_sequential_download >= 0) + int index = 0; + for (std::vector::const_iterator i = m_piece_map.begin() + , end(m_piece_map.end()); i != end && (i->have() || i->filtered()); + ++i, ++index); + TORRENT_ASSERT(m_cursor == index); + index = num_pieces; + if (num_pieces > 0) { - int index = 0; for (std::vector::const_iterator i = m_piece_map.begin() - , end(m_piece_map.end()); i != end && (i->have() || i->filtered()); - ++i, ++index); - TORRENT_ASSERT(m_sequential_download == index); + + (index - 1); index > 0 && (i->have() || i->filtered()); --i, --index); + TORRENT_ASSERT(index == num_pieces + || m_piece_map[index].have() + || m_piece_map[index].filtered()); + TORRENT_ASSERT(m_reverse_cursor == index); + } + else + { + TORRENT_ASSERT(m_reverse_cursor == 0); } int num_filtered = 0; @@ -404,9 +413,11 @@ namespace libtorrent if (t != 0) TORRENT_ASSERT(!t->have_piece(index)); - if (m_sequential_download == -1 && !m_dirty) + int prio = p.priority(this); + TORRENT_ASSERT(prio == -1 || p.downloading == (prio % piece_picker::prio_factor == 0)); + + if (!m_dirty) { - int prio = p.priority(this); TORRENT_ASSERT(prio < int(m_priority_boundries.size()) || m_dirty); if (prio >= 0) @@ -424,11 +435,6 @@ namespace libtorrent == m_pieces.end()); } } - else if (m_sequential_download >= 0) - { - TORRENT_ASSERT(m_pieces.empty()); - TORRENT_ASSERT(m_priority_boundries.empty()); - } int count = std::count_if(m_downloads.begin(), m_downloads.end() , has_index(index)); @@ -512,7 +518,6 @@ namespace libtorrent piece_pos& p = m_piece_map[index]; TORRENT_ASSERT(!p.filtered()); TORRENT_ASSERT(!p.have()); - TORRENT_ASSERT(m_sequential_download == -1); int priority = p.priority(this); TORRENT_ASSERT(priority >= 0); @@ -564,6 +569,10 @@ namespace libtorrent #ifdef TORRENT_PICKER_LOG print_pieces(); +#endif +// shuffle(priority, new_index); +#ifdef TORRENT_PICKER_LOG +// print_pieces(); #endif } } @@ -572,7 +581,6 @@ namespace libtorrent { TORRENT_ASSERT(!m_dirty); TORRENT_ASSERT(priority >= 0); - TORRENT_ASSERT(m_sequential_download == -1); #ifdef TORRENT_PICKER_LOG std::cerr << "remove " << m_pieces[elem_index] << " (" << priority << ")" << std::endl; @@ -618,7 +626,6 @@ namespace libtorrent TORRENT_ASSERT(!m_dirty); TORRENT_ASSERT(priority >= 0); TORRENT_ASSERT(elem_index >= 0); - TORRENT_ASSERT(m_sequential_download == -1); TORRENT_ASSERT(int(m_priority_boundries.size()) > priority); @@ -721,10 +728,15 @@ namespace libtorrent void piece_picker::shuffle(int priority, int elem_index) { +#ifdef TORRENT_PICKER_LOG + std::cerr << "shuffle()" << std::endl; +#endif + TORRENT_ASSERT(!m_dirty); TORRENT_ASSERT(priority >= 0); TORRENT_ASSERT(elem_index >= 0); - TORRENT_ASSERT(m_sequential_download == -1); + TORRENT_ASSERT(elem_index < int(m_pieces.size())); + TORRENT_ASSERT(m_piece_map[m_pieces[elem_index]].priority(this) == priority); int range_start, range_end; priority_range(priority, &range_start, &range_end); @@ -773,6 +785,14 @@ namespace libtorrent , has_index(index)); TORRENT_ASSERT(i != m_downloads.end()); +#ifndef NDEBUG + int num_blocks = blocks_in_piece(i->index); + for (int k = 0; k < num_blocks; ++k) + { + TORRENT_ASSERT(i->info[k].state == block_info::state_finished); + TORRENT_ASSERT(i->info[k].num_peers == 0); + } +#endif erase_download_piece(i); piece_pos& p = m_piece_map[index]; @@ -782,11 +802,6 @@ namespace libtorrent if (new_priority == prev_priority) return; if (m_dirty) return; - if (m_sequential_download >= 0) - { - m_dirty = true; - return; - } if (prev_priority == -1) { add(index); @@ -840,15 +855,11 @@ namespace libtorrent void piece_picker::inc_refcount(int index) { +#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS TORRENT_PIECE_PICKER_INVARIANT_CHECK; +#endif piece_pos& p = m_piece_map[index]; - if (m_sequential_download >= 0) - { - ++p.peer_count; - m_dirty = true; - return; - } int prev_priority = p.priority(this); ++p.peer_count; @@ -863,16 +874,11 @@ namespace libtorrent void piece_picker::dec_refcount(int index) { +#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS TORRENT_PIECE_PICKER_INVARIANT_CHECK; +#endif piece_pos& p = m_piece_map[index]; - if (m_sequential_download >= 0) - { - TORRENT_ASSERT(p.peer_count > 0); - --p.peer_count; - m_dirty = true; - return; - } int prev_priority = p.priority(this); TORRENT_ASSERT(p.peer_count > 0); --p.peer_count; @@ -897,7 +903,7 @@ namespace libtorrent } } - if (updated && m_sequential_download == -1) m_dirty = true; + if (updated) m_dirty = true; } void piece_picker::dec_refcount(bitfield const& bitmask) @@ -917,13 +923,12 @@ namespace libtorrent } } - if (updated && m_sequential_download == -1) m_dirty = true; + if (updated) m_dirty = true; } void piece_picker::update_pieces() const { TORRENT_ASSERT(m_dirty); - TORRENT_ASSERT(m_sequential_download == -1); if (m_priority_boundries.empty()) m_priority_boundries.resize(1, 0); #ifdef TORRENT_PICKER_LOG std::cerr << "update_pieces" << std::endl; @@ -1004,16 +1009,29 @@ namespace libtorrent piece_pos& p = m_piece_map[index]; TORRENT_ASSERT(p.downloading == 0); +#ifdef TORRENT_PICKER_LOG + std::cerr << "piece_picker::we_dont_have(" << index << ")" << std::endl; +#endif if (!p.have()) return; - if (m_sequential_download > index) - m_sequential_download = index; - if (p.filtered()) { ++m_num_filtered; --m_num_have_filtered; } + else + { + // update cursors + if (index < m_cursor) + m_cursor = index; + if (index >= m_reverse_cursor) + m_reverse_cursor = index + 1; + if (m_reverse_cursor == m_cursor) + { + m_reverse_cursor = 0; + m_cursor = num_pieces(); + } + } --m_num_have; p.set_not_have(); @@ -1032,10 +1050,13 @@ namespace libtorrent TORRENT_ASSERT(index >= 0); TORRENT_ASSERT(index < (int)m_piece_map.size()); +#ifdef TORRENT_PICKER_LOG + std::cerr << "piece_picker::we_have(" << index << ")" << std::endl; +#endif piece_pos& p = m_piece_map[index]; int info_index = p.index; int priority = p.priority(this); - TORRENT_ASSERT(priority < int(m_priority_boundries.size())); + TORRENT_ASSERT(priority < int(m_priority_boundries.size()) || m_dirty); if (p.downloading) { @@ -1051,13 +1072,6 @@ namespace libtorrent TORRENT_ASSERT(std::find_if(m_downloads.begin(), m_downloads.end() , has_index(index)) == m_downloads.end()); - if (m_sequential_download == index) - { - ++m_sequential_download; - for (std::vector::const_iterator i = m_piece_map.begin() + m_sequential_download - , end(m_piece_map.end()); i != end && (i->have() || i->filtered()); - ++i, ++m_sequential_download); - } if (p.have()) return; if (p.filtered()) { @@ -1066,6 +1080,33 @@ namespace libtorrent } ++m_num_have; p.set_have(); + if (m_cursor == m_reverse_cursor - 1 && + m_cursor == index) + { + m_cursor = int(m_piece_map.size()); + m_reverse_cursor = 0; + TORRENT_ASSERT(num_pieces() > 0); + } + else if (m_cursor == index) + { + ++m_cursor; + for (std::vector::const_iterator i = m_piece_map.begin() + m_cursor + , end(m_piece_map.end()); i != end && (i->have() || i->filtered()); + ++i, ++m_cursor); + } + else if (m_reverse_cursor - 1 == index) + { + --m_reverse_cursor; + TORRENT_ASSERT(m_piece_map[m_reverse_cursor].have() + || m_piece_map[m_reverse_cursor].filtered()); + for (std::vector::const_iterator i = m_piece_map.begin() + + m_reverse_cursor - 1; m_reverse_cursor > 0 && (i->have() || i->filtered()); + --i, --m_reverse_cursor); + TORRENT_ASSERT(m_piece_map[m_reverse_cursor].have() + || m_piece_map[m_reverse_cursor].filtered()); + } + TORRENT_ASSERT(m_reverse_cursor > m_cursor + || (m_cursor == num_pieces() && m_reverse_cursor == 0)); if (priority == -1) return; if (m_dirty) return; remove(priority, info_index); @@ -1074,7 +1115,9 @@ namespace libtorrent bool piece_picker::set_piece_priority(int index, int new_piece_priority) { +#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS TORRENT_PIECE_PICKER_INVARIANT_CHECK; +#endif TORRENT_ASSERT(new_piece_priority >= 0); TORRENT_ASSERT(new_piece_priority <= 7); TORRENT_ASSERT(index >= 0); @@ -1093,16 +1136,61 @@ namespace libtorrent && p.piece_priority != piece_pos::filter_priority) { // the piece just got filtered - if (p.have()) ++m_num_have_filtered; - else ++m_num_filtered; + if (p.have()) + { + ++m_num_have_filtered; + } + else + { + ++m_num_filtered; + + // update m_cursor + if (m_cursor == m_reverse_cursor - 1 && m_cursor == index) + { + m_cursor = int(m_piece_map.size()); + m_reverse_cursor = 0; + } + else if (m_cursor == index) + { + ++m_cursor; + while (m_cursor < int(m_piece_map.size()) + && (m_piece_map[m_cursor].have() + || m_piece_map[m_cursor].filtered())) + ++m_cursor; + } + else if (m_reverse_cursor == index + 1) + { + --m_reverse_cursor; + while (m_reverse_cursor > 0 + && (m_piece_map[m_reverse_cursor-1].have() + || m_piece_map[m_reverse_cursor-1].filtered())) + --m_reverse_cursor; + } + } ret = true; } else if (new_piece_priority != piece_pos::filter_priority && p.piece_priority == piece_pos::filter_priority) { // the piece just got unfiltered - if (p.have()) --m_num_have_filtered; - else --m_num_filtered; + if (p.have()) + { + --m_num_have_filtered; + } + else + { + --m_num_filtered; + // update cursors + if (index < m_cursor) + m_cursor = index; + if (index >= m_reverse_cursor) + m_reverse_cursor = index + 1; + if (m_reverse_cursor == m_cursor) + { + m_reverse_cursor = 0; + m_cursor = num_pieces(); + } + } ret = true; } TORRENT_ASSERT(m_num_filtered >= 0); @@ -1159,6 +1247,25 @@ namespace libtorrent // ============ end deprecation ============== + namespace + { + int append_blocks(std::vector& dst, std::vector& src + , int num_blocks) + { + if (src.empty()) return num_blocks; + int to_copy; +// if (prefer_whole_pieces == 0) + to_copy = (std::min)(int(src.size()), num_blocks); +// else +// to_copy = int(src.size()); + + dst.insert(dst.end() + , src.begin(), src.begin() + to_copy); + src.clear(); + return num_blocks - to_copy; + } + } + // pieces describes which pieces the peer we're requesting from // has. // interesting_blocks is an out parameter, and will be filled @@ -1174,18 +1281,37 @@ namespace libtorrent // to pick blocks from the same pieces as fast peers, and vice // versa. Downloading pieces are marked as being fast, medium // or slow once they're started. + + // options are: + // * rarest_first + // pick the rarest pieces first + // * reverse + // reverse the piece picking. Pick the most common + // pieces first or the last pieces (if picking sequential) + // * sequential + // download pieces in-order + // * on_parole + // the peer is on parole, only pick whole pieces which + // has only been downloaded and requested from the same + // peer + // * prioritize_partials + // pick blocks from downloading pieces first + + // only one of rarest_first, sequential can be set + void piece_picker::pick_pieces(bitfield const& pieces - , std::vector& interesting_blocks - , int num_blocks, int prefer_whole_pieces - , void* peer, piece_state_t speed, bool rarest_first - , bool on_parole, std::vector const& suggested_pieces) const + , std::vector& interesting_blocks, int num_blocks + , int prefer_whole_pieces, void* peer, piece_state_t speed + , int options, std::vector const& suggested_pieces) const { + // only one of rarest_first and sequential can be set. + TORRENT_ASSERT(bool(options & rarest_first) + + bool(options & sequential) <= 1); TORRENT_PIECE_PICKER_INVARIANT_CHECK; TORRENT_ASSERT(num_blocks > 0); TORRENT_ASSERT(pieces.size() == m_piece_map.size()); TORRENT_ASSERT(!m_priority_boundries.empty() - || m_sequential_download >= 0 || m_dirty); // this will be filled with blocks that we should not request @@ -1195,6 +1321,7 @@ namespace libtorrent // blocks belonging to a piece that others have // downloaded to std::vector backup_blocks; + std::vector backup_blocks2; const std::vector empty_vector; // When prefer_whole_pieces is set (usually set when downloading from @@ -1202,43 +1329,122 @@ namespace libtorrent // ignored as long as possible. All blocks found in downloading // pieces are regarded as backup blocks - num_blocks = add_blocks_downloading(pieces - , interesting_blocks, backup_blocks, num_blocks - , prefer_whole_pieces, peer, speed, on_parole); + if (options & prioritize_partials) + { + for (std::vector::const_iterator i = m_downloads.begin() + , end(m_downloads.end()); i != end; ++i) + { + if (!pieces[i->index]) continue; + num_blocks = add_blocks_downloading(*i, pieces + , interesting_blocks, backup_blocks, backup_blocks2 + , num_blocks, prefer_whole_pieces, peer, speed, options); + if (num_blocks <= 0) return; + } - if (num_blocks <= 0) return; + num_blocks = append_blocks(interesting_blocks, backup_blocks + , num_blocks); + if (num_blocks <= 0) return; + + num_blocks = append_blocks(interesting_blocks, backup_blocks2 + , num_blocks); + if (num_blocks <= 0) return; + } if (!suggested_pieces.empty()) { - num_blocks = add_blocks(suggested_pieces, pieces - , interesting_blocks, num_blocks - , prefer_whole_pieces, peer, empty_vector); - if (num_blocks == 0) return; + for (std::vector::const_iterator i = suggested_pieces.begin(); + i != suggested_pieces.end(); ++i) + { + if (!is_piece_free(*i, pieces)) continue; + num_blocks = add_blocks(*i, pieces + , interesting_blocks, backup_blocks + , backup_blocks2, num_blocks + , prefer_whole_pieces, peer, empty_vector + , speed, options); + if (num_blocks <= 0) return; + } } - if (m_sequential_download >= 0) + if (options & sequential) { - for (int i = m_sequential_download; - i < int(m_piece_map.size()) && num_blocks > 0; ++i) + if (options & reverse) { - if (!can_pick(i, pieces)) continue; - int num_blocks_in_piece = blocks_in_piece(i); - if (prefer_whole_pieces == 0 && 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; + for (int i = m_reverse_cursor - 1; i >= m_cursor; --i) + { + if (!is_piece_free(i, pieces)) continue; + num_blocks = add_blocks(i, pieces + , interesting_blocks, backup_blocks + , backup_blocks2, num_blocks + , prefer_whole_pieces, peer, suggested_pieces + , speed, options); + if (num_blocks <= 0) return; + } + } + else + { + for (int i = m_cursor; i < m_reverse_cursor; ++i) + { + if (!is_piece_free(i, pieces)) continue; + num_blocks = add_blocks(i, pieces + , interesting_blocks, backup_blocks + , backup_blocks2, num_blocks + , prefer_whole_pieces, peer, suggested_pieces + , speed, options); + if (num_blocks <= 0) return; } } } - else if (rarest_first) + else if (options & rarest_first) { if (m_dirty) update_pieces(); - num_blocks = add_blocks(m_pieces, pieces - , interesting_blocks, num_blocks - , prefer_whole_pieces, peer, suggested_pieces); - TORRENT_ASSERT(num_blocks >= 0); + TORRENT_ASSERT(!m_dirty); + + if (options & reverse) + { + // it's a bit complicated in order to always prioritize + // partial pieces, and respect priorities. Every chunk + // of 4 priority levels are traversed in forward order, but otherwise + // they are traversed in reverse order + // round up to an even 4 priority boundry, to make it simpler + // to do the akward reverse traversing +#define div_round_up(n, d) (((n) + (d) - 1) / (d)) + m_priority_boundries.resize(div_round_up(m_priority_boundries.size() + , prio_factor) * prio_factor, m_priority_boundries.back()); + for (int i = m_priority_boundries.size() - 1; i >= 0; --i) + { + int prio = (i / prio_factor) * prio_factor + + prio_factor - 1 - (i % prio_factor); + + TORRENT_ASSERT(prio >= 0); + TORRENT_ASSERT(prio < int(m_priority_boundries.size())); + int start = prio == 0 ? 0 : m_priority_boundries[prio - 1]; + for (int p = start; p < m_priority_boundries[prio]; ++p) + { + if (!is_piece_free(m_pieces[p], pieces)) continue; + num_blocks = add_blocks(m_pieces[p], pieces + , interesting_blocks, backup_blocks + , backup_blocks2, num_blocks + , prefer_whole_pieces, peer, suggested_pieces + , speed, options); + if (num_blocks <= 0) return; + } + } +#undef div_round_up + } + else + { + for (std::vector::const_iterator i = m_pieces.begin(); + i != m_pieces.end(); ++i) + { + if (!is_piece_free(*i, pieces)) continue; + num_blocks = add_blocks(*i, pieces + , interesting_blocks, backup_blocks + , backup_blocks2, num_blocks + , prefer_whole_pieces, peer, suggested_pieces + , speed, options); + if (num_blocks <= 0) return; + } + } } else { @@ -1247,24 +1453,23 @@ namespace libtorrent // pieces are) int start_piece = rand() % m_piece_map.size(); - // if we have suggested pieces, try to find one of those instead - for (std::vector::const_iterator i = suggested_pieces.begin() - , end(suggested_pieces.end()); i != end; ++i) - { - if (!can_pick(*i, pieces)) continue; - start_piece = *i; - break; - } int piece = start_piece; while (num_blocks > 0) { - while (!can_pick(piece, pieces)) + bool done = false; + // skip pieces we can't pick, and suggested pieces + // since we've already picked those + while (!can_pick(piece, pieces) + && std::find(suggested_pieces.begin() + , suggested_pieces.end(), piece) + == suggested_pieces.end()) { ++piece; if (piece == int(m_piece_map.size())) piece = 0; // could not find any more pieces - if (piece == start_piece) return; + if (piece == start_piece) { done = true; break; } } + if (done) break; int start, end; boost::tie(start, end) = expand_piece(piece, prefer_whole_pieces, pieces); @@ -1284,16 +1489,125 @@ namespace libtorrent piece = end; if (piece == int(m_piece_map.size())) piece = 0; // could not find any more pieces - if (piece == start_piece) return; + if (piece == start_piece) break; } } if (num_blocks <= 0) return; - if (!backup_blocks.empty()) - interesting_blocks.insert(interesting_blocks.end() - , backup_blocks.begin(), backup_blocks.end()); +#ifndef NDEBUG + verify_pick(interesting_blocks, pieces); + verify_pick(backup_blocks, pieces); + verify_pick(backup_blocks2, pieces); +#endif + + num_blocks = append_blocks(interesting_blocks, backup_blocks + , num_blocks); + if (num_blocks <= 0) return; + + num_blocks = append_blocks(interesting_blocks, backup_blocks2 + , num_blocks); + if (num_blocks <= 0) return; + + // don't double-pick anything if the peer is on parole + if (options & on_parole) return; + + for (std::vector::const_iterator i = m_downloads.begin() + , end(m_downloads.end()); i != end; ++i) + { + if (!pieces[i->index]) continue; + + int num_blocks_in_piece = blocks_in_piece(i->index); + + // fill in with blocks requested from other peers + // as backups + for (int j = 0; j < num_blocks_in_piece; ++j) + { + block_info const& info = i->info[j]; + if (info.state != block_info::state_requested + || info.peer == peer) + continue; + interesting_blocks.push_back(piece_block(i->index, j)); + } + } + +#ifndef NDEBUG +// make sure that we at this point have added requests to all unrequested blocks +// in all downloading pieces + + for (std::vector::const_iterator i = m_downloads.begin() + , end(m_downloads.end()); i != end; ++i) + { + if (!pieces[i->index]) continue; + + int num_blocks_in_piece = blocks_in_piece(i->index); + for (int j = 0; j < num_blocks_in_piece; ++j) + { + block_info const& info = i->info[j]; + if (info.state != block_info::state_none) continue; + std::vector::iterator k = std::find( + interesting_blocks.begin(), interesting_blocks.end() + , piece_block(i->index, j)); + if (k != interesting_blocks.end()) continue; + + std::cerr << "interesting blocks:" << std::endl; + for (k = interesting_blocks.begin(); k != interesting_blocks.end(); ++k) + std::cerr << "(" << k->piece_index << ", " << k->block_index << ") "; + std::cerr << std::endl; + std::cerr << "num_blocks: " << num_blocks << std::endl; + + for (std::vector::const_iterator l = m_downloads.begin() + , end(m_downloads.end()); l != end; ++l) + { + std::cerr << l->index << " : "; + int num_blocks_in_piece = blocks_in_piece(l->index); + for (int m = 0; m < num_blocks_in_piece; ++m) + std::cerr << l->info[m].state; + std::cerr << std::endl; + } + + TORRENT_ASSERT(false); + } + } + + if (interesting_blocks.empty()) + { +// print_pieces(); + for (int i = 0; i < num_pieces(); ++i) + { + if (!pieces[i]) continue; + if (piece_priority(i) == 0) continue; + if (have_piece(i)) continue; + + std::vector::const_iterator k + = std::find_if(m_downloads.begin(), m_downloads.end(), has_index(i)); + + TORRENT_ASSERT(k != m_downloads.end()); + if (k == m_downloads.end()) continue; + + // this assert is not valid for web_seeds + /* + int num_blocks_in_piece = blocks_in_piece(k->index); + for (int j = 0; j < num_blocks_in_piece; ++j) + { + block_info const& info = k->info[j]; + if (info.state == block_info::state_finished) continue; + TORRENT_ASSERT(info.peer != 0); + } + */ + } + } +#endif + + } + + bool piece_picker::is_piece_free(int piece, bitfield const& bitmask) const + { + TORRENT_ASSERT(piece >= 0 && piece < int(m_piece_map.size())); + return bitmask[piece] + && !m_piece_map[piece].have() + && !m_piece_map[piece].filtered(); } bool piece_picker::can_pick(int piece, bitfield const& bitmask) const @@ -1342,254 +1656,168 @@ namespace libtorrent } } - int piece_picker::add_blocks(std::vector const& piece_list + int piece_picker::add_blocks(int piece , bitfield const& pieces , std::vector& interesting_blocks + , std::vector& backup_blocks + , std::vector& backup_blocks2 , int num_blocks, int prefer_whole_pieces - , void* peer, std::vector const& ignore) const + , void* peer, std::vector const& ignore + , piece_state_t speed, int options) const { - for (std::vector::const_iterator i = piece_list.begin(); - i != piece_list.end(); ++i) + TORRENT_ASSERT(piece >= 0); + TORRENT_ASSERT(piece < (int)m_piece_map.size()); + TORRENT_ASSERT(is_piece_free(piece, pieces)); + +// std::cout << "add_blocks(" << piece << ")" << std::endl; +// std::cout << " num_blocks " << num_blocks << std::endl; + + // ignore pieces found in the ignore list + if (std::find(ignore.begin(), ignore.end(), piece) != ignore.end()) return num_blocks; + + TORRENT_ASSERT(m_piece_map[piece].priority(this) >= 0); + if (m_piece_map[piece].downloading) { - TORRENT_ASSERT(*i >= 0); - TORRENT_ASSERT(*i < (int)m_piece_map.size()); + // if we're prioritizing partials, we've already + // looked through the downloading pieces + if (options & prioritize_partials) return num_blocks; - // if the peer doesn't have the piece - // or if it's set to 0 priority - // skip it - if (!can_pick(*i, pieces)) continue; + std::vector::const_iterator i + = std::find_if(m_downloads.begin(), m_downloads.end(), has_index(piece)); + TORRENT_ASSERT(i != m_downloads.end()); - // ignore pieces found in the ignore list - if (std::find(ignore.begin(), ignore.end(), *i) != ignore.end()) continue; +// std::cout << "add_blocks_downloading(" << piece << ")" << std::endl; - TORRENT_ASSERT(m_piece_map[*i].priority(this) >= 0); - TORRENT_ASSERT(m_piece_map[*i].downloading == 0); - - int num_blocks_in_piece = blocks_in_piece(*i); + return add_blocks_downloading(*i, pieces + , interesting_blocks, backup_blocks, backup_blocks2 + , num_blocks, prefer_whole_pieces, peer, speed, options); + } - // pick a new piece - if (prefer_whole_pieces == 0) + int num_blocks_in_piece = blocks_in_piece(piece); + + // pick a new piece + if (prefer_whole_pieces == 0) + { + if (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 -= num_blocks_in_piece; + } + else + { + int start, end; + boost::tie(start, end) = expand_piece(piece, prefer_whole_pieces, pieces); + for (int k = start; k < end; ++k) { - if (num_blocks_in_piece > num_blocks) - num_blocks_in_piece = num_blocks; + TORRENT_ASSERT(m_piece_map[k].priority(this) > 0); + num_blocks_in_piece = blocks_in_piece(k); for (int j = 0; j < num_blocks_in_piece; ++j) - interesting_blocks.push_back(piece_block(*i, j)); - num_blocks -= num_blocks_in_piece; - } - else - { - int start, end; - boost::tie(start, end) = expand_piece(*i, prefer_whole_pieces, pieces); - for (int k = start; k < end; ++k) { - TORRENT_ASSERT(m_piece_map[k].priority(this) > 0); - num_blocks_in_piece = blocks_in_piece(k); - for (int j = 0; j < num_blocks_in_piece; ++j) - { - interesting_blocks.push_back(piece_block(k, j)); - --num_blocks; - } + interesting_blocks.push_back(piece_block(k, j)); + --num_blocks; } } - if (num_blocks <= 0) - { -#ifndef NDEBUG - verify_pick(interesting_blocks, pieces); -#endif - return 0; - } } #ifndef NDEBUG verify_pick(interesting_blocks, pieces); #endif + if (num_blocks <= 0) return 0; return num_blocks; } - int piece_picker::add_blocks_downloading(bitfield const& pieces + int piece_picker::add_blocks_downloading(downloading_piece const& dp + , bitfield const& pieces , std::vector& interesting_blocks , std::vector& backup_blocks + , std::vector& backup_blocks2 , int num_blocks, int prefer_whole_pieces - , void* peer, piece_state_t speed, bool on_parole) const + , void* peer, piece_state_t speed, int options) const { - for (std::vector::const_iterator i = m_downloads.begin() - , end(m_downloads.end()); i != end; ++i) + if (!pieces[dp.index]) return num_blocks; + + int num_blocks_in_piece = blocks_in_piece(dp.index); + + // is true if all the other pieces that are currently + // requested from this piece are from the same + // peer as 'peer'. + bool exclusive; + bool exclusive_active; + boost::tie(exclusive, exclusive_active) + = requested_from(dp, num_blocks_in_piece, peer); + + // peers on parole are only allowed to pick blocks from + // pieces that only they have downloaded/requested from + if ((options & on_parole) && !exclusive) return num_blocks; + + // we prefer whole blocks, but there are other peers + // downloading from this piece, add it as backups + if (prefer_whole_pieces > 0 && !exclusive_active) { - if (!pieces[i->index]) continue; - - int num_blocks_in_piece = blocks_in_piece(i->index); - - // is true if all the other pieces that are currently - // requested from this piece are from the same - // peer as 'peer'. - bool exclusive; - bool exclusive_active; - boost::tie(exclusive, exclusive_active) - = requested_from(*i, num_blocks_in_piece, peer); - - // peers on parole are only allowed to pick blocks from - // pieces that only they have downloaded/requested from - if (on_parole && !exclusive) continue; - - if (prefer_whole_pieces > 0 && !exclusive_active) continue; - - // don't pick too many back-up blocks - if (i->state != none - && i->state != speed - && !exclusive_active - && int(backup_blocks.size()) >= num_blocks) - continue; + if (int(backup_blocks2.size()) >= num_blocks) + return num_blocks; for (int j = 0; j < num_blocks_in_piece; ++j) { // ignore completed blocks and already requested blocks - block_info const& info = i->info[j]; - if (info.state != block_info::state_none) - continue; - - TORRENT_ASSERT(i->info[j].state == block_info::state_none); - - // 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 - // have been requested from the same peer or - // if the state of the piece is none (the - // piece will in that case change state). - if (i->state != none && i->state != speed - && !exclusive_active) - { - backup_blocks.push_back(piece_block(i->index, j)); - continue; - } - - // this block is interesting (we don't have it - // yet). - interesting_blocks.push_back(piece_block(i->index, j)); - // we have found a block that's free to download - num_blocks--; - // if we prefer whole pieces, continue picking from this - // piece even though we have num_blocks - if (prefer_whole_pieces > 0) continue; - TORRENT_ASSERT(num_blocks >= 0); - if (num_blocks <= 0) break; + block_info const& info = dp.info[j]; + if (info.state != block_info::state_none) continue; + backup_blocks2.push_back(piece_block(dp.index, j)); } - if (num_blocks <= 0) break; + return num_blocks; + } + + for (int j = 0; j < num_blocks_in_piece; ++j) + { + // ignore completed blocks and already requested blocks + block_info const& info = dp.info[j]; + if (info.state != block_info::state_none) continue; + + TORRENT_ASSERT(dp.info[j].state == block_info::state_none); + + // 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 + // have been requested from the same peer or + // if the state of the piece is none (the + // piece will in that case change state). + if (dp.state != none && dp.state != speed + && !exclusive_active) + { + if (abs(dp.state - speed) == 1) + { + // don't pick too many back-up blocks + if (int(backup_blocks.size()) >= num_blocks) return num_blocks; + backup_blocks.push_back(piece_block(dp.index, j)); + } + else + { + // don't pick too many back-up blocks + if (int(backup_blocks2.size()) >= num_blocks) return num_blocks; + backup_blocks2.push_back(piece_block(dp.index, j)); + } + continue; + } + + // this block is interesting (we don't have it + // yet). + interesting_blocks.push_back(piece_block(dp.index, j)); + // we have found a block that's free to download + num_blocks--; + // if we prefer whole pieces, continue picking from this + // piece even though we have num_blocks + if (prefer_whole_pieces > 0) continue; + TORRENT_ASSERT(num_blocks >= 0); + if (num_blocks <= 0) return num_blocks; } TORRENT_ASSERT(num_blocks >= 0 || prefer_whole_pieces > 0); -#ifndef NDEBUG - verify_pick(interesting_blocks, pieces); - verify_pick(backup_blocks, pieces); -#endif - if (num_blocks <= 0) return 0; - if (on_parole) return num_blocks; - - int to_copy; - if (prefer_whole_pieces == 0) - to_copy = (std::min)(int(backup_blocks.size()), num_blocks); - else - to_copy = int(backup_blocks.size()); - - interesting_blocks.insert(interesting_blocks.end() - , backup_blocks.begin(), backup_blocks.begin() + to_copy); - num_blocks -= to_copy; - backup_blocks.clear(); - - if (num_blocks <= 0) return 0; - - if (prefer_whole_pieces > 0) - { - for (std::vector::const_iterator i = m_downloads.begin() - , end(m_downloads.end()); i != end; ++i) - { - if (!pieces[i->index]) continue; - int num_blocks_in_piece = blocks_in_piece(i->index); - bool exclusive; - bool exclusive_active; - boost::tie(exclusive, exclusive_active) - = requested_from(*i, num_blocks_in_piece, peer); - - if (exclusive_active) continue; - - for (int j = 0; j < num_blocks_in_piece; ++j) - { - block_info const& info = i->info[j]; - if (info.state != block_info::state_none) continue; - backup_blocks.push_back(piece_block(i->index, j)); - } - } - } + if (options & on_parole) return num_blocks; if (int(backup_blocks.size()) >= num_blocks) return num_blocks; - -#ifndef NDEBUG -// make sure that we at this point has added requests to all unrequested blocks -// in all downloading pieces - - for (std::vector::const_iterator i = m_downloads.begin() - , end(m_downloads.end()); i != end; ++i) - { - if (!pieces[i->index]) continue; - - int num_blocks_in_piece = blocks_in_piece(i->index); - for (int j = 0; j < num_blocks_in_piece; ++j) - { - block_info const& info = i->info[j]; - if (info.state != block_info::state_none) continue; - std::vector::iterator k = std::find( - interesting_blocks.begin(), interesting_blocks.end() - , piece_block(i->index, j)); - if (k != interesting_blocks.end()) continue; - - k = std::find(backup_blocks.begin() - , backup_blocks.end(), piece_block(i->index, j)); - if (k != backup_blocks.end()) continue; - - std::cerr << "interesting blocks:" << std::endl; - for (k = interesting_blocks.begin(); k != interesting_blocks.end(); ++k) - std::cerr << "(" << k->piece_index << ", " << k->block_index << ") "; - std::cerr << std::endl; - std::cerr << "backup blocks:" << std::endl; - for (k = backup_blocks.begin(); k != backup_blocks.end(); ++k) - std::cerr << "(" << k->piece_index << ", " << k->block_index << ") "; - std::cerr << std::endl; - std::cerr << "num_blocks: " << num_blocks << std::endl; - - for (std::vector::const_iterator l = m_downloads.begin() - , end(m_downloads.end()); l != end; ++l) - { - std::cerr << l->index << " : "; - int num_blocks_in_piece = blocks_in_piece(l->index); - for (int m = 0; m < num_blocks_in_piece; ++m) - std::cerr << l->info[m].state; - std::cerr << std::endl; - } - - TORRENT_ASSERT(false); - } - } -#endif - - for (std::vector::const_iterator i = m_downloads.begin() - , end(m_downloads.end()); i != end; ++i) - { - if (!pieces[i->index]) continue; - - int num_blocks_in_piece = blocks_in_piece(i->index); - - // fill in with blocks requested from other peers - // as backups - for (int j = 0; j < num_blocks_in_piece; ++j) - { - block_info const& info = i->info[j]; - if (info.state != block_info::state_requested - || info.peer == peer) - continue; - backup_blocks.push_back(piece_block(i->index, j)); - } - } #ifndef NDEBUG verify_pick(backup_blocks, pieces); #endif @@ -1696,6 +1924,7 @@ namespace libtorrent bool piece_picker::mark_as_downloading(piece_block block , void* peer, piece_state_t state) { + TORRENT_ASSERT(state != piece_picker::none); TORRENT_ASSERT(block.piece_index >= 0); TORRENT_ASSERT(block.block_index >= 0); TORRENT_ASSERT(block.piece_index < (int)m_piece_map.size()); @@ -1705,14 +1934,15 @@ namespace libtorrent piece_pos& p = m_piece_map[block.piece_index]; if (p.downloading == 0) { +#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS TORRENT_PIECE_PICKER_INVARIANT_CHECK; +#endif int prio = p.priority(this); TORRENT_ASSERT(prio < int(m_priority_boundries.size()) - || m_sequential_download >= 0 || m_dirty); - TORRENT_ASSERT(prio > 0); + TORRENT_ASSERT(prio >= 0); p.downloading = 1; - if (prio >= 0 && m_sequential_download == -1 && !m_dirty) update(prio, p.index); + if (prio >= 0 && !m_dirty) update(prio, p.index); downloading_piece& dp = add_download_piece(); dp.state = state; @@ -1725,7 +1955,9 @@ namespace libtorrent } else { +#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS TORRENT_PIECE_PICKER_INVARIANT_CHECK; +#endif std::vector::iterator i = std::find_if(m_downloads.begin(), m_downloads.end(), has_index(block.piece_index)); TORRENT_ASSERT(i != m_downloads.end()); @@ -1780,7 +2012,9 @@ namespace libtorrent void piece_picker::mark_as_writing(piece_block block, void* peer) { +#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS TORRENT_PIECE_PICKER_INVARIANT_CHECK; +#endif TORRENT_ASSERT(block.piece_index >= 0); TORRENT_ASSERT(block.block_index >= 0); @@ -1801,7 +2035,9 @@ namespace libtorrent TORRENT_ASSERT(info.state != block_info::state_writing); ++i->writing; info.state = block_info::state_writing; - TORRENT_ASSERT(info.num_peers > 0); + + // all other requests for this block should have been + // cancelled now info.num_peers = 0; if (i->requested == 0) @@ -1822,22 +2058,13 @@ namespace libtorrent TORRENT_ASSERT(i != m_downloads.end()); block_info& info = i->info[block.block_index]; TORRENT_ASSERT(info.state == block_info::state_writing); + TORRENT_ASSERT(info.num_peers == 0); --i->writing; - if (info.num_peers > 0) - { - // there are other peers on this block - // turn it back into requested - ++i->requested; - info.state = block_info::state_requested; - } - else - { - info.state = block_info::state_none; - } + info.state = block_info::state_none; info.peer = 0; } - + void piece_picker::mark_as_finished(piece_block block, void* peer) { TORRENT_ASSERT(block.piece_index >= 0); @@ -1849,7 +2076,9 @@ namespace libtorrent if (p.downloading == 0) { +#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS TORRENT_PIECE_PICKER_INVARIANT_CHECK; +#endif TORRENT_ASSERT(peer == 0); int prio = p.priority(this); @@ -1874,7 +2103,9 @@ namespace libtorrent } else { +#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS TORRENT_PIECE_PICKER_INVARIANT_CHECK; +#endif std::vector::iterator i = std::find_if(m_downloads.begin(), m_downloads.end(), has_index(block.piece_index)); @@ -1893,6 +2124,7 @@ namespace libtorrent } else { + TORRENT_ASSERT(info.state == block_info::state_none); info.state = block_info::state_finished; sort_piece(i); } @@ -1954,23 +2186,31 @@ namespace libtorrent block_info& info = i->info[block.block_index]; - if (i->info[block.block_index].state != block_info::state_requested) + TORRENT_ASSERT(info.state != block_info::state_none); + + if (info.state == block_info::state_finished + || info.state == block_info::state_none + || info.state == block_info::state_writing) return; - if (info.num_peers > 0) --info.num_peers; + if (info.state == block_info::state_requested) + { + TORRENT_ASSERT(info.num_peers > 0); + if (info.num_peers > 0) --info.num_peers; - TORRENT_ASSERT(block.block_index < blocks_in_piece(block.piece_index)); + TORRENT_ASSERT(block.block_index < blocks_in_piece(block.piece_index)); - // if there are other peers, leave the block requested - if (info.num_peers > 0) return; + // if there are other peers, leave the block requested + if (info.num_peers > 0) return; - // clear the downloader of this block - info.peer = 0; + // clear the downloader of this block + info.peer = 0; + + // clear this block as being downloaded + info.state = block_info::state_none; + --i->requested; + } - // clear this block as being downloaded - info.state = block_info::state_none; - --i->requested; - // if there are no other blocks in this piece // that's being downloaded, remove it from the list if (i->requested + i->finished + i->writing == 0) @@ -1981,7 +2221,7 @@ namespace libtorrent TORRENT_ASSERT(prev_prio < int(m_priority_boundries.size()) || m_dirty); p.downloading = 0; - if (m_sequential_download == -1 && !m_dirty) + if (!m_dirty) { int prio = p.priority(this); if (prev_prio == -1 && prio >= 0) add(block.piece_index); diff --git a/libtorrent/src/policy.cpp b/libtorrent/src/policy.cpp index f51b563cd..b98ce6363 100755 --- a/libtorrent/src/policy.cpp +++ b/libtorrent/src/policy.cpp @@ -200,8 +200,6 @@ namespace libtorrent int prefer_whole_pieces = c.prefer_whole_pieces(); - bool rarest_first = t.num_have() >= t.settings().initial_picker_threshold; - if (prefer_whole_pieces == 0) { prefer_whole_pieces = c.statistics().download_payload_rate() @@ -250,7 +248,7 @@ namespace libtorrent p.pick_pieces(mask, interesting_pieces , num_requests, prefer_whole_pieces, c.peer_info_struct() - , state, rarest_first, c.on_parole(), suggested); + , state, c.picker_options(), suggested); } else { @@ -264,7 +262,7 @@ namespace libtorrent // then use this mode. p.pick_pieces(bits, interesting_pieces , num_requests, prefer_whole_pieces, c.peer_info_struct() - , state, rarest_first, c.on_parole(), suggested); + , state, c.picker_options(), suggested); } #ifdef TORRENT_VERBOSE_LOGGING @@ -303,10 +301,6 @@ namespace libtorrent if (busy_pieces.empty() || num_requests <= 0) { - // 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. return; } diff --git a/libtorrent/src/session_impl.cpp b/libtorrent/src/session_impl.cpp index b5cc97486..b90b8ad43 100755 --- a/libtorrent/src/session_impl.cpp +++ b/libtorrent/src/session_impl.cpp @@ -1494,67 +1494,71 @@ namespace aux { } } - m_optimistic_unchoke_time_scaler--; - if (m_optimistic_unchoke_time_scaler <= 0) + if (m_allowed_upload_slots > 0) { - m_optimistic_unchoke_time_scaler - = settings().optimistic_unchoke_multiplier; - - // find the peer that has been waiting the longest to be optimistically - // unchoked - connection_map::iterator current_optimistic_unchoke = m_connections.end(); - connection_map::iterator optimistic_unchoke_candidate = m_connections.end(); - ptime last_unchoke = max_time(); - - for (connection_map::iterator i = m_connections.begin() - , end(m_connections.end()); i != end; ++i) + m_optimistic_unchoke_time_scaler--; + if (m_optimistic_unchoke_time_scaler <= 0) { - peer_connection* p = i->get(); - TORRENT_ASSERT(p); - policy::peer* pi = p->peer_info_struct(); - if (!pi) continue; - torrent* t = p->associated_torrent().lock().get(); - if (!t) continue; + m_optimistic_unchoke_time_scaler + = settings().optimistic_unchoke_multiplier; - if (pi->optimistically_unchoked) + // find the peer that has been waiting the longest to be optimistically + // unchoked + connection_map::iterator current_optimistic_unchoke = m_connections.end(); + connection_map::iterator optimistic_unchoke_candidate = m_connections.end(); + ptime last_unchoke = max_time(); + + for (connection_map::iterator i = m_connections.begin() + , end(m_connections.end()); i != end; ++i) { - TORRENT_ASSERT(!p->is_choked()); - TORRENT_ASSERT(current_optimistic_unchoke == m_connections.end()); - current_optimistic_unchoke = i; + peer_connection* p = i->get(); + TORRENT_ASSERT(p); + policy::peer* pi = p->peer_info_struct(); + if (!pi) continue; + torrent* t = p->associated_torrent().lock().get(); + if (!t) continue; + + if (pi->optimistically_unchoked) + { + TORRENT_ASSERT(!p->is_choked()); + TORRENT_ASSERT(current_optimistic_unchoke == m_connections.end()); + current_optimistic_unchoke = i; + } + + if (pi->last_optimistically_unchoked < last_unchoke + && !p->is_connecting() + && !p->is_disconnecting() + && p->is_peer_interested() + && t->free_upload_slots() + && p->is_choked() + && t->valid_metadata()) + { + last_unchoke = pi->last_optimistically_unchoked; + optimistic_unchoke_candidate = i; + } } - if (pi->last_optimistically_unchoked < last_unchoke - && !p->is_connecting() - && !p->is_disconnecting() - && p->is_peer_interested() - && t->free_upload_slots() - && p->is_choked()) + if (optimistic_unchoke_candidate != m_connections.end() + && optimistic_unchoke_candidate != current_optimistic_unchoke) { - last_unchoke = pi->last_optimistically_unchoked; - optimistic_unchoke_candidate = i; - } - } + if (current_optimistic_unchoke != m_connections.end()) + { + torrent* t = (*current_optimistic_unchoke)->associated_torrent().lock().get(); + TORRENT_ASSERT(t); + (*current_optimistic_unchoke)->peer_info_struct()->optimistically_unchoked = false; + t->choke_peer(*current_optimistic_unchoke->get()); + } + else + { + ++m_num_unchoked; + } - if (optimistic_unchoke_candidate != m_connections.end() - && optimistic_unchoke_candidate != current_optimistic_unchoke) - { - if (current_optimistic_unchoke != m_connections.end()) - { - torrent* t = (*current_optimistic_unchoke)->associated_torrent().lock().get(); + torrent* t = (*optimistic_unchoke_candidate)->associated_torrent().lock().get(); TORRENT_ASSERT(t); - (*current_optimistic_unchoke)->peer_info_struct()->optimistically_unchoked = false; - t->choke_peer(*current_optimistic_unchoke->get()); + bool ret = t->unchoke_peer(*optimistic_unchoke_candidate->get()); + TORRENT_ASSERT(ret); + (*optimistic_unchoke_candidate)->peer_info_struct()->optimistically_unchoked = true; } - else - { - ++m_num_unchoked; - } - - torrent* t = (*optimistic_unchoke_candidate)->associated_torrent().lock().get(); - TORRENT_ASSERT(t); - bool ret = t->unchoke_peer(*optimistic_unchoke_candidate->get()); - TORRENT_ASSERT(ret); - (*optimistic_unchoke_candidate)->peer_info_struct()->optimistically_unchoked = true; } } } @@ -2046,11 +2050,20 @@ namespace aux { , m_dht_settings.service_port , m_dht_settings.service_port); } - m_dht = new dht::dht_tracker(m_dht_socket, m_dht_settings, startup_state); + m_dht = new dht::dht_tracker(m_dht_socket, m_dht_settings); if (!m_dht_socket.is_open() || m_dht_socket.local_port() != m_dht_settings.service_port) { m_dht_socket.bind(m_dht_settings.service_port); } + + for (std::list >::iterator i = m_dht_router_nodes.begin() + , end(m_dht_router_nodes.end()); i != end; ++i) + { + m_dht->add_router_node(*i); + } + std::list >().swap(m_dht_router_nodes); + + m_dht->start(startup_state); } void session_impl::stop_dht() @@ -2114,9 +2127,10 @@ namespace aux { void session_impl::add_dht_router(std::pair const& node) { - TORRENT_ASSERT(m_dht); + // router nodes should be added before the DHT is started (and bootstrapped) mutex_t::scoped_lock l(m_mutex); - m_dht->add_router_node(node); + if (m_dht) m_dht->add_router_node(node); + else m_dht_router_nodes.push_back(node); } #endif @@ -2167,7 +2181,7 @@ namespace aux { void session_impl::set_max_uploads(int limit) { - TORRENT_ASSERT(limit > 0 || limit == -1); + TORRENT_ASSERT(limit >= 0 || limit == -1); mutex_t::scoped_lock l(m_mutex); INVARIANT_CHECK; diff --git a/libtorrent/src/torrent.cpp b/libtorrent/src/torrent.cpp index 6b8cce969..b5c43d918 100755 --- a/libtorrent/src/torrent.cpp +++ b/libtorrent/src/torrent.cpp @@ -754,10 +754,14 @@ namespace libtorrent m_progress = j.piece / float(torrent_file().num_pieces()); + m_picker->check_invariant(); + TORRENT_ASSERT(m_picker); if (j.offset >= 0 && !m_picker->have_piece(j.offset)) m_picker->we_have(j.offset); + m_picker->check_invariant(); + // we're not done checking yet // this handler will be called repeatedly until // we're done, or encounter a failure @@ -3298,9 +3302,6 @@ namespace libtorrent if (!is_seed()) { - if (m_sequential_download) - picker().sequential_download(m_sequential_download); - // if we just finished checking and we're not a seed, we are // likely to be unpaused if (m_ses.m_auto_manage_time_scaler > 1) @@ -3549,13 +3550,7 @@ namespace libtorrent #endif void torrent::set_sequential_download(bool sd) - { - m_sequential_download = sd; - if (has_picker()) - { - picker().sequential_download(sd); - } - } + { m_sequential_download = sd; } void torrent::set_queue_position(int p) { diff --git a/libtorrent/src/torrent_handle.cpp b/libtorrent/src/torrent_handle.cpp index b02da4a6d..80e266bc3 100755 --- a/libtorrent/src/torrent_handle.cpp +++ b/libtorrent/src/torrent_handle.cpp @@ -499,6 +499,12 @@ namespace libtorrent TORRENT_FORWARD(replace_trackers(urls)); } + storage_interface* torrent_handle::get_storage_impl() const + { + INVARIANT_CHECK; + TORRENT_FORWARD_RETURN(get_storage(), 0); + } + torrent_info const& torrent_handle::get_torrent_info() const { INVARIANT_CHECK; diff --git a/libtorrent/src/tracker_manager.cpp b/libtorrent/src/tracker_manager.cpp index 6a0b28d60..621c3b99c 100755 --- a/libtorrent/src/tracker_manager.cpp +++ b/libtorrent/src/tracker_manager.cpp @@ -229,6 +229,7 @@ namespace libtorrent boost::shared_ptr cb = con->requester(); if (cb) cb->m_manager = this; + con->start(); } void tracker_manager::abort_all_requests() diff --git a/libtorrent/src/udp_tracker_connection.cpp b/libtorrent/src/udp_tracker_connection.cpp index e19d5696d..191be4c61 100755 --- a/libtorrent/src/udp_tracker_connection.cpp +++ b/libtorrent/src/udp_tracker_connection.cpp @@ -93,14 +93,17 @@ namespace libtorrent , m_state(action_error) { m_socket.set_proxy_settings(proxy); + } + void udp_tracker_connection::start() + { std::string hostname; int port; char const* error; using boost::tuples::ignore; boost::tie(ignore, ignore, hostname, port, ignore, error) - = parse_url_components(req.url); + = parse_url_components(tracker_req().url); if (error) { @@ -112,7 +115,7 @@ namespace libtorrent m_name_lookup.async_resolve(q , boost::bind( &udp_tracker_connection::name_lookup, self(), _1, _2)); - set_timeout(req.event == tracker_request::stopped + set_timeout(tracker_req().event == tracker_request::stopped ? m_settings.stop_tracker_timeout : m_settings.tracker_completion_timeout , m_settings.tracker_receive_timeout);