diff --git a/ChangeLog b/ChangeLog index d3eb6b453..44c34a24e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -63,6 +63,7 @@ incoming connection * added more detailed instrumentation of the disk I/O thread + * optimized piece picking to not cause busy loops in some end-game modes * fixed python bindings for tcp::endpoint * fixed edge case of pad file support * limit number of torrents tracked by DHT diff --git a/docs/manual.rst b/docs/manual.rst index cdb276e2b..fb19b5143 100644 --- a/docs/manual.rst +++ b/docs/manual.rst @@ -3411,7 +3411,8 @@ It contains the following fields:: optimistic_unchoke = 0x800, snubbed = 0x1000, upload_only = 0x2000, - holepunched = 0x4000, + endgame_mode = 0x4000, + holepunched = 0x8000, rc4_encrypted = 0x100000, plaintext_encrypted = 0x200000 }; @@ -3560,6 +3561,11 @@ any combination of the enums above. The following table describes each flag: | | will not downloading anything more, regardless of | | | which pieces we have. | +-------------------------+-------------------------------------------------------+ +| ``endgame_mode`` | This means the last time this peer picket a piece, | +| | it could not pick as many as it wanted because there | +| | were not enough free ones. i.e. all pieces this peer | +| | has were already requested from other peers. | ++-------------------------+-------------------------------------------------------+ | ``holepunched`` | This flag is set if the peer was in holepunch mode | | | when the connection succeeded. This typically only | | | happens if both peers are behind a NAT and the peers | diff --git a/examples/client_test.cpp b/examples/client_test.cpp index 144f19030..c85f1711d 100644 --- a/examples/client_test.cpp +++ b/examples/client_test.cpp @@ -426,7 +426,7 @@ void print_peer_info(std::string& out, std::vector const& #endif snprintf(str, sizeof(str) - , "%s%s (%s|%s) %s%s (%s|%s) %s%3d (%3d) %3d %c%c%c%c%c%c%c%c%c%c%c%c%c%c%c %c%c%c%c%c%c " + , "%s%s (%s|%s) %s%s (%s|%s) %s%3d (%3d) %3d %c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c %c%c%c%c%c%c " , esc("32"), add_suffix(i->down_speed, "/s").c_str() , add_suffix(i->total_download).c_str(), add_suffix(i->download_rate_peak, "/s").c_str() , esc("31"), add_suffix(i->up_speed, "/s").c_str(), add_suffix(i->total_upload).c_str() @@ -451,6 +451,7 @@ void print_peer_info(std::string& out, std::vector const& (i->write_state == peer_info::bw_network)?'W':'.' , (i->flags & peer_info::snubbed)?'S':'.' , (i->flags & peer_info::upload_only)?'U':'D' + , (i->flags & peer_info::endgame_mode)?'-':'.' #ifndef TORRENT_DISABLE_ENCRYPTION , (i->flags & peer_info::rc4_encrypted)?'E': (i->flags & peer_info::plaintext_encrypted)?'e':'.' diff --git a/include/libtorrent/peer_connection.hpp b/include/libtorrent/peer_connection.hpp index 8c3440e41..99e195b66 100644 --- a/include/libtorrent/peer_connection.hpp +++ b/include/libtorrent/peer_connection.hpp @@ -248,6 +248,9 @@ namespace libtorrent void request_large_blocks(bool b) { m_request_large_blocks = b; } + void set_endgame(bool b) { m_endgame_mode = b; } + bool endgame() const { return m_endgame_mode; } + bool no_download() const { return m_no_download; } void no_download(bool b) { m_no_download = b; } @@ -409,7 +412,12 @@ namespace libtorrent bool failed() const { return m_failed; } - int desired_queue_size() const { return m_desired_queue_size; } + int desired_queue_size() const + { + // this peer is in end-game mode we only want + // one outstanding request + return m_endgame_mode ? 1: m_desired_queue_size; + } bool bittyrant_unchoke_compare( boost::intrusive_ptr const& p) const; @@ -1101,6 +1109,13 @@ namespace libtorrent // pick any pieces from this peer bool m_no_download:1; + // this is set to true if the last time we tried to + // pick a piece to download, we could only find + // blocks that were already requested from other + // peers. In this case, we should not try to pick + // another piece until the last one we requested is done + bool m_endgame_mode:1; + // set to true when we've sent the first round of suggests bool m_sent_suggests:1; diff --git a/include/libtorrent/peer_info.hpp b/include/libtorrent/peer_info.hpp index b094e387d..73678564a 100644 --- a/include/libtorrent/peer_info.hpp +++ b/include/libtorrent/peer_info.hpp @@ -60,7 +60,8 @@ namespace libtorrent optimistic_unchoke = 0x800, snubbed = 0x1000, upload_only = 0x2000, - holepunched = 0x4000 + endgame_mode = 0x4000, + holepunched = 0x8000 #ifndef TORRENT_DISABLE_ENCRYPTION , rc4_encrypted = 0x100000, plaintext_encrypted = 0x200000 diff --git a/src/peer_connection.cpp b/src/peer_connection.cpp index 101242482..2ca3bee3b 100644 --- a/src/peer_connection.cpp +++ b/src/peer_connection.cpp @@ -150,6 +150,7 @@ namespace libtorrent , m_snubbed(false) , m_bitfield_received(false) , m_no_download(false) + , m_endgame_mode(false) , m_sent_suggests(false) , m_holepunch_mode(false) , m_ignore_stats(false) @@ -292,6 +293,7 @@ namespace libtorrent , m_snubbed(false) , m_bitfield_received(false) , m_no_download(false) + , m_endgame_mode(false) , m_sent_suggests(false) , m_holepunch_mode(false) , m_ignore_stats(false) @@ -3593,6 +3595,7 @@ namespace libtorrent p.flags |= is_seed() ? peer_info::seed : 0; p.flags |= m_snubbed ? peer_info::snubbed : 0; p.flags |= m_upload_only ? peer_info::upload_only : 0; + p.flags |= m_endgame_mode ? peer_info::endgame_mode : 0; p.flags |= m_holepunch_mode ? peer_info::holepunched : 0; if (peer_info_struct()) { @@ -3852,6 +3855,23 @@ namespace libtorrent return; } + if (m_endgame_mode + && m_interesting + && m_download_queue.empty() + && m_request_queue.empty() + && total_seconds(now - m_last_request) > 5) + { + // this happens when we're in strict end-game + // mode and the peer could not request any blocks + // because they were all taken but there were still + // unrequested blocks. Now, 5 seconds later, there + // might not be any unrequested blocks anymore, so + // we should try to pick another block to see + // if we can pick a busy one + request_a_block(*t, *this); + if (m_disconnecting) return; + } + on_tick(); #ifndef TORRENT_DISABLE_EXTENSIONS diff --git a/src/policy.cpp b/src/policy.cpp index ebe188b77..5d779188a 100644 --- a/src/policy.cpp +++ b/src/policy.cpp @@ -145,7 +145,7 @@ namespace libtorrent - (int)c.request_queue().size(); #ifdef TORRENT_VERBOSE_LOGGING - c.peer_log("*** PIECE_PICKER [ req: %d ]", num_requests); + c.peer_log("*** PIECE_PICKER [ req: %d engame: %d ]", num_requests, c.endgame()); #endif TORRENT_ASSERT(c.desired_queue_size() > 0); // if our request queue is already full, we @@ -252,7 +252,10 @@ namespace libtorrent // don't request pieces we already have in our request queue if (std::find_if(dq.begin(), dq.end(), has_block(*i)) != dq.end() || std::find_if(rq.begin(), rq.end(), has_block(*i)) != rq.end()) + { + TORRENT_ASSERT(false); // this shouldn't happen! continue; + } // ok, we found a piece that's not being downloaded // by somebody else. request it from this peer @@ -263,12 +266,27 @@ namespace libtorrent num_requests--; } + // we have picked as many blocks as we should + // we're done! + if (num_requests <= 0) + { + // since we could pick as many blocks as we + // requested without having to resort to picking + // busy ones, we're not in end-game mode + c.set_endgame(false); + return; + } + + // we did not pick as many pieces as we wanted, because + // there aren't enough. This means we're in end-game mode + // as long as we have at least one request outstanding, + // we shouldn't pick another piece + c.set_endgame(true); + // if we don't have any potential busy blocks to request - // or if we have picked as many blocks as we should // or if we already have outstanding requests, don't // pick a busy piece if (busy_pieces.empty() - || num_requests <= 0 || dq.size() + rq.size() > 0) { return;