diff --git a/include/libtorrent/aux_/session_impl.hpp b/include/libtorrent/aux_/session_impl.hpp index 67c3fef1d..bb3b41705 100644 --- a/include/libtorrent/aux_/session_impl.hpp +++ b/include/libtorrent/aux_/session_impl.hpp @@ -282,7 +282,9 @@ namespace libtorrent void unchoke_peer(peer_connection& c) { - c.send_unchoke(); + torrent* t = c.associated_torrent().lock().get(); + assert(t); + t->unchoke_peer(c); ++m_num_unchoked; } diff --git a/include/libtorrent/torrent.hpp b/include/libtorrent/torrent.hpp index 78bc53ab4..8c53166d4 100755 --- a/include/libtorrent/torrent.hpp +++ b/include/libtorrent/torrent.hpp @@ -249,6 +249,13 @@ namespace libtorrent void remove_url_seed(std::string const& url) { m_web_seeds.erase(url); } + + bool free_upload_slots() const + { return m_num_uploads < m_max_uploads; } + + void choke_peer(peer_connection& c); + bool unchoke_peer(peer_connection& c); + // used by peer_connection to attach itself to a torrent // since incoming connections don't know what torrent // they're a part of until they have received an info_hash. @@ -770,6 +777,9 @@ namespace libtorrent // the maximum number of uploads for this torrent int m_max_uploads; + // the number of unchoked peers in this torrent + int m_num_uploads; + // the maximum number of connections for this torrent int m_max_connections; diff --git a/src/session_impl.cpp b/src/session_impl.cpp index 2110e88a4..7a893e8fe 100755 --- a/src/session_impl.cpp +++ b/src/session_impl.cpp @@ -1046,14 +1046,15 @@ namespace detail peer_connection* p = i->second.get(); torrent* t = p->associated_torrent().lock().get(); if (!p->peer_info_struct() + || t == 0 || !p->is_peer_interested() || p->is_disconnecting() || p->is_connecting() || (p->share_diff() < -free_upload_amount - && t && !t->is_seed())) + && !t->is_seed())) { - if (!i->second->is_choked()) - i->second->send_choke(); + if (!i->second->is_choked() && t) + t->choke_peer(*i->second); continue; } peers.push_back(i->second.get()); @@ -1082,9 +1083,24 @@ namespace detail { peer_connection* p = *i; assert(p); + torrent* t = p->associated_torrent().lock().get(); + assert(t); if (unchoke_set_size > 0) { - if (p->is_choked()) p->send_unchoke(); + if (p->is_choked()) + { + if (t->unchoke_peer(*p)) + { + --unchoke_set_size; + ++m_num_unchoked; + } + } + else + { + --unchoke_set_size; + ++m_num_unchoked; + } + assert(p->peer_info_struct()); if (p->peer_info_struct()->optimistically_unchoked) { @@ -1092,13 +1108,11 @@ namespace detail m_optimistic_unchoke_time_scaler = 0; p->peer_info_struct()->optimistically_unchoked = false; } - --unchoke_set_size; - ++m_num_unchoked; } else { if (!p->is_choked() && !p->peer_info_struct()->optimistically_unchoked) - p->send_choke(); + t->choke_peer(*p); } } @@ -1121,15 +1135,21 @@ namespace detail 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) { 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()) + && p->is_peer_interested() + && t->free_upload_slots() + && p->is_choked()) { last_unchoke = pi->last_optimistically_unchoked; optimistic_unchoke_candidate = i; @@ -1141,11 +1161,16 @@ namespace detail { if (current_optimistic_unchoke != m_connections.end()) { - current_optimistic_unchoke->second->send_choke(); + torrent* t = current_optimistic_unchoke->second->associated_torrent().lock().get(); + assert(t); + t->choke_peer(*current_optimistic_unchoke->second); current_optimistic_unchoke->second->peer_info_struct()->optimistically_unchoked = false; } - optimistic_unchoke_candidate->second->send_unchoke(); + torrent* t = optimistic_unchoke_candidate->second->associated_torrent().lock().get(); + assert(t); + bool ret = t->unchoke_peer(*optimistic_unchoke_candidate->second); + assert(ret); optimistic_unchoke_candidate->second->peer_info_struct()->optimistically_unchoked = true; } @@ -2090,6 +2115,8 @@ namespace detail #ifndef NDEBUG void session_impl::check_invariant(const char *place) { + assert(m_max_connections > 0); + assert(m_max_uploads > 0); assert(place); int unchokes = 0; int num_optimistic = 0; diff --git a/src/torrent.cpp b/src/torrent.cpp index 7caacfb96..8b977f0a2 100755 --- a/src/torrent.cpp +++ b/src/torrent.cpp @@ -200,6 +200,7 @@ namespace libtorrent , m_settings(s) , m_storage_constructor(sc) , m_max_uploads(std::numeric_limits::max()) + , m_num_uploads(0) , m_max_connections(std::numeric_limits::max()) { #ifndef NDEBUG @@ -208,7 +209,6 @@ namespace libtorrent m_policy.reset(new policy(this)); } - torrent::torrent( session_impl& ses , aux::checker_impl& checker @@ -262,6 +262,9 @@ namespace libtorrent , m_connections_initialized(false) , m_settings(s) , m_storage_constructor(sc) + , m_max_uploads(std::numeric_limits::max()) + , m_num_uploads(0) + , m_max_connections(std::numeric_limits::max()) { #ifndef NDEBUG m_initial_done = 0; @@ -1387,6 +1390,27 @@ namespace libtorrent return req; } + void torrent::choke_peer(peer_connection& c) + { + INVARIANT_CHECK; + + assert(!c.is_choked()); + assert(m_num_uploads > 0); + c.send_choke(); + --m_num_uploads; + } + + bool torrent::unchoke_peer(peer_connection& c) + { + INVARIANT_CHECK; + + assert(c.is_choked()); + if (m_num_uploads >= m_max_uploads) return false; + c.send_unchoke(); + ++m_num_uploads; + return true; + } + void torrent::cancel_block(piece_block block) { for (peer_iterator i = m_connections.begin() @@ -1437,6 +1461,9 @@ namespace libtorrent } } + if (!p->is_choked()) + --m_num_uploads; + m_policy->connection_closed(*p); p->set_peer_info(0); m_connections.erase(i); @@ -2358,6 +2385,7 @@ namespace libtorrent // size_type download = m_stat.total_payload_download(); // size_type done = boost::get<0>(bytes_done()); // assert(download >= done - m_initial_done); + int num_uploads = 0; std::map num_requests; for (const_peer_iterator i = begin(); i != end(); ++i) { @@ -2368,10 +2396,12 @@ namespace libtorrent for (std::deque::const_iterator i = p.download_queue().begin() , end(p.download_queue().end()); i != end; ++i) ++num_requests[*i]; + if (!p.is_choked()) ++num_uploads; torrent* associated_torrent = p.associated_torrent().lock().get(); if (associated_torrent != this) assert(false); } + assert(num_uploads == m_num_uploads); if (has_picker()) { @@ -2429,14 +2459,14 @@ namespace libtorrent void torrent::set_max_uploads(int limit) { assert(limit >= -1); - if (limit < 0) limit = std::numeric_limits::max(); + if (limit <= 0) limit = std::numeric_limits::max(); m_max_uploads = limit; } void torrent::set_max_connections(int limit) { assert(limit >= -1); - if (limit < -1) limit = std::numeric_limits::max(); + if (limit <= 0) limit = std::numeric_limits::max(); m_max_connections = limit; } @@ -2459,7 +2489,7 @@ namespace libtorrent void torrent::set_upload_limit(int limit) { assert(limit >= -1); - if (limit == -1) limit = std::numeric_limits::max(); + if (limit <= 0) limit = std::numeric_limits::max(); if (limit < num_peers() * 10) limit = num_peers() * 10; m_bandwidth_limit[peer_connection::upload_channel].throttle(limit); } @@ -2474,7 +2504,7 @@ namespace libtorrent void torrent::set_download_limit(int limit) { assert(limit >= -1); - if (limit == -1) limit = std::numeric_limits::max(); + if (limit <= 0) limit = std::numeric_limits::max(); if (limit < num_peers() * 10) limit = num_peers() * 10; m_bandwidth_limit[peer_connection::download_channel].throttle(limit); } @@ -2759,7 +2789,7 @@ namespace libtorrent = m_trackers[m_last_working_tracker].url; } - st.num_uploads = -1; + st.num_uploads = m_num_uploads; st.uploads_limit = m_max_uploads; st.num_connections = int(m_connections.size()); st.connections_limit = m_max_connections;