Index: src/policy.cpp =================================================================== --- src/policy.cpp (revision 5092) +++ src/policy.cpp (revision 5093) @@ -149,7 +149,8 @@ - (int)c.request_queue().size(); #ifdef TORRENT_VERBOSE_LOGGING - (*c.m_logger) << time_now_string() << " PIECE_PICKER [ req: " << num_requests << " ]\n"; + (*c.m_logger) << time_now_string() << " PIECE_PICKER [ req: " << num_requests + << " endgame: " << c.endgame() << "]\n"; #endif TORRENT_ASSERT(c.desired_queue_size() > 0); // if our request queue is already full, we @@ -256,7 +257,10 @@ // 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 @@ -267,12 +271,27 @@ 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; Index: src/peer_connection.cpp =================================================================== --- src/peer_connection.cpp (revision 5092) +++ src/peer_connection.cpp (revision 5093) @@ -144,6 +144,7 @@ , m_snubbed(false) , m_bitfield_received(false) , m_no_download(false) + , m_endgame_mode(false) #ifdef TORRENT_DEBUG , m_in_constructor(true) , m_disconnect_started(false) @@ -267,6 +268,7 @@ , m_snubbed(false) , m_bitfield_received(false) , m_no_download(false) + , m_endgame_mode(false) #ifdef TORRENT_DEBUG , m_in_constructor(true) , m_disconnect_started(false) @@ -3279,6 +3281,7 @@ 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; if (peer_info_struct()) { policy::peer* pi = peer_info_struct(); @@ -3499,6 +3502,23 @@ 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 Index: include/libtorrent/peer_info.hpp =================================================================== --- include/libtorrent/peer_info.hpp (revision 5092) +++ include/libtorrent/peer_info.hpp (revision 5093) @@ -58,7 +58,8 @@ seed = 0x400, optimistic_unchoke = 0x800, snubbed = 0x1000, - upload_only = 0x2000 + upload_only = 0x2000, + endgame_mode = 0x4000 #ifndef TORRENT_DISABLE_ENCRYPTION , rc4_encrypted = 0x100000, plaintext_encrypted = 0x200000 Index: include/libtorrent/peer_connection.hpp =================================================================== --- include/libtorrent/peer_connection.hpp (revision 5092) +++ include/libtorrent/peer_connection.hpp (revision 5093) @@ -230,6 +230,9 @@ 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; } @@ -371,7 +374,12 @@ 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; + } // compares this connection against the given connection // for which one is more eligible for an unchoke. @@ -1022,6 +1030,13 @@ // if this is set to true, the client will not // 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; template struct handler_storage