diff --git a/src/session.cpp b/src/session.cpp index c40656398..95ce583ac 100755 --- a/src/session.cpp +++ b/src/session.cpp @@ -91,12 +91,6 @@ namespace return sum; } - // adjusts the upload rates of every peer connection - // to make sure the sum of all send quotas equals - // the given upload_limit. An upload limit of - // std::numeric_limits::max() means unlimited upload - // rate, but the rates of each peer has to be set anyway, - // since it depends on the download rate from the peer. void control_upload_rates( int upload_limit, libtorrent::detail::session_impl::connection_map connections) @@ -104,29 +98,56 @@ namespace assert(upload_limit >= 0); using namespace libtorrent; - std::vector requests; + std::vector peers; for (detail::session_impl::connection_map::iterator c = connections.begin(); c != connections.end(); ++c) { boost::shared_ptr p = c->second; - p->upload_bandwidth.used = - p->has_data() ? (int)ceil(p->statistics().upload_rate()) - : 0; - - requests.push_back(&p->upload_bandwidth); + int estimated_upload_capacity= + p->has_data() ? (int)ceil(p->statistics().upload_rate()) // std::max(10,(int)ceil(p->statistics().upload_rate()*1.1f)) + : 1; + + int limit=p->send_quota_limit(); + if(limit==-1) + limit=std::numeric_limits::max(); + + peers.push_back(resource_consumer(p,limit,estimated_upload_capacity)); } - allocate_resources(upload_limit, requests); + allocate_resources(upload_limit, peers); - for (detail::session_impl::connection_map::iterator c = connections.begin(); - c != connections.end(); ++c) + for (std::vector::iterator r=peers.begin(); + r!=peers.end(); ++r) { - boost::shared_ptr p = c->second; - p->update_send_quota_left(); + boost::any_cast > + (r->who())->set_send_quota(r->allowed_use()); } + +#ifndef NDEBUG + { + int sum_quota = 0; + int sum_quota_limit = 0; + for (detail::session_impl::connection_map::iterator i = connections.begin(); + i != connections.end(); + ++i) + { + peer_connection& p = *i->second; + + int quota=p.send_quota(); + int quota_limit=p.send_quota_limit(); + if(quota==-1) + quota=std::numeric_limits::max(); + if(quota_limit==-1) + quota_limit=std::numeric_limits::max(); + + sum_quota = saturated_add(sum_quota,quota); + sum_quota_limit = saturated_add(sum_quota_limit,quota_limit); + } + assert(sum_quota == std::min(upload_limit,sum_quota_limit)); + } +#endif } -/* void control_number_of_connections( int connections_limit, libtorrent::detail::session_impl::torrent_map hash_list) @@ -159,6 +180,197 @@ namespace // (r->who())->set_send_quota(r->allowed_use()); } } + +/* + // This struct is used by control_upload_rates() below. It keeps + // track how much bandwidth has been allocated to each connection + // and other relevant information to assist in the allocation process. + struct connection_info + { + libtorrent::peer_connection* p; // which peer_connection this info refers to + int allocated_quota; // bandwidth allocated to this peer connection + int quota_limit; // bandwidth limit + int estimated_upload_capacity; // estimated channel bandwidth + + bool operator < (const connection_info &other) const + { + return estimated_upload_capacity < other.estimated_upload_capacity; + } + + int give(int amount) + { + + // if amount > 0, try to add amount to the allocated quota. + // if amount < 0, try to subtract abs(amount) from the allocated quota + // + // Quota will not go above quota_limit or below 0. This means that + // not all the amount given or taken may be accepted. + // + // return value: how much quota was actually added (or subtracted if negative). + + int old_quota=allocated_quota; + allocated_quota+=amount; + if(quota_limit!=-1) + allocated_quota=std::min(allocated_quota,quota_limit); + allocated_quota=std::max(0,allocated_quota); + return allocated_quota-old_quota; + } + }; + + // adjusts the upload rates of every peer connection + // to make sure the sum of all send quotas equals + // the given upload_limit. An upload limit of -1 means + // unlimited upload rate, but the rates of each peer + // has to be set anyway, since it depends on the download + // rate from the peer. + void control_upload_rates( + int upload_limit + , libtorrent::detail::session_impl::connection_map connections) + { + using namespace libtorrent; + + assert(upload_limit > 0 || upload_limit == -1); + + if (connections.empty()) return; + + + if (upload_limit == -1) + { + for (detail::session_impl::connection_map::iterator i = connections.begin(); + i != connections.end(); + ++i) + { + // there's no limit, set the quota to max + // allowed + peer_connection& p = *i->second; + p.set_send_quota(p.send_quota_limit()); + } + return; + } + else + { + // There's an upload limit, so we need to distribute the available + // upload bandwidth among the peer_connections fairly, but not + // wastefully. + + // For each peer_connection, keep some local data about their + // quota limit and estimated upload capacity, and how much quota + // has been allocated to them. + + std::vector peer_info; + + for (detail::session_impl::connection_map::iterator i = connections.begin(); + i != connections.end(); + ++i) + { + peer_connection& p = *i->second; + connection_info pi; + + pi.p = &p; + pi.allocated_quota = 0; // we haven't given it any bandwith yet + pi.quota_limit = p.send_quota_limit(); + + pi.estimated_upload_capacity= + p.has_data() ? std::max(10,(int)ceil(p.statistics().upload_rate()*1.1f)) + // If there's no data to send, upload capacity is practically 0. + // Here we set it to 1 though, because otherwise it will not be able + // to accept any quota at all, which may upset quota_limit balances. + : 1; + + peer_info.push_back(pi); + } + + // Sum all peer_connections' quota limit to get the total quota limit. + + int sum_total_of_quota_limits=0; + for (int i = 0; i < (int)peer_info.size(); ++i) + { + int quota_limit = peer_info[i].quota_limit; + if (quota_limit == -1) + { + // quota_limit=-1 means infinite, so + // sum_total_of_quota_limits will be infinite too... + sum_total_of_quota_limits = std::numeric_limits::max(); + break; + } + sum_total_of_quota_limits += quota_limit; + } + + // This is how much total bandwidth that can be distributed. + int quota_left_to_distribute = std::min(upload_limit,sum_total_of_quota_limits); + + + // Sort w.r.t. channel capacitiy, lowest channel capacity first. + // Makes it easy to traverse the list in sorted order. + std::sort(peer_info.begin(),peer_info.end()); + + + // Distribute quota until there's nothing more to distribute + + while (quota_left_to_distribute != 0) + { + assert(quota_left_to_distribute > 0); + + for (int i = 0; i < (int)peer_info.size(); ++i) + { + // Traverse the peer list from slowest connection to fastest. + + // In each step, share bandwidth equally between this peer_connection + // and the following faster peer_connections. + // + // Rounds upwards to avoid trying to give 0 bandwidth to someone + // (may get caught in an endless loop otherwise) + + int num_peers_left_to_share_quota = (int)peer_info.size() - i; + int try_to_give_to_this_peer + = (quota_left_to_distribute + num_peers_left_to_share_quota - 1) + / num_peers_left_to_share_quota; + + // But do not allocate more than the estimated upload capacity. + try_to_give_to_this_peer = std::min( + peer_info[i].estimated_upload_capacity + , try_to_give_to_this_peer); + + // Also, when the peer is given quota, it will + // not accept more than it's quota_limit. + int quota_actually_given_to_peer + = peer_info[i].give(try_to_give_to_this_peer); + + quota_left_to_distribute -= quota_actually_given_to_peer; + } + } + + // Finally, inform the peers of how much quota they get. + + for(int i = 0; i < (int)peer_info.size(); ++i) + peer_info[i].p->set_send_quota(peer_info[i].allocated_quota); + } + +#ifndef NDEBUG + { + int sum_quota = 0; + int sum_quota_limit = 0; + for (detail::session_impl::connection_map::iterator i = connections.begin(); + i != connections.end(); + ++i) + { + peer_connection& p = *i->second; + sum_quota += p.send_quota(); + + if(p.send_quota_limit() == -1) + { + sum_quota_limit=std::numeric_limits::max(); + } + + if(sum_quota_limit!=std::numeric_limits::max()) + { + sum_quota_limit += p.send_quota_limit(); + } + } + assert(sum_quota == std::min(upload_limit,sum_quota_limit)); + } +#endif + } */ } @@ -378,7 +590,8 @@ namespace libtorrent ++i) { i->second->abort(); - m_tracker_manager.queue_request(i->second->generate_tracker_request(m_listen_port)); + m_tracker_manager.queue_request( + i->second->generate_tracker_request(m_listen_port)); } m_connections.clear(); m_torrents.clear(); @@ -467,9 +680,9 @@ namespace libtorrent if (*i == listener) { boost::shared_ptr s = (*i)->accept(); + s->set_blocking(false); if (s) { - s->set_blocking(false); // we got a connection request! m_incoming_connection = true; #ifndef NDEBUG @@ -480,12 +693,7 @@ namespace libtorrent boost::shared_ptr c( new peer_connection(*this, m_selector, s)); - if (m_upload_rate != -1) - { - c->upload_bandwidth.given = 0; - c->update_send_quota_left(); - } - + if (m_upload_rate != -1) c->set_send_quota(0); m_connections.insert(std::make_pair(s, c)); m_selector.monitor_readability(s); m_selector.monitor_errors(s); @@ -616,6 +824,7 @@ namespace libtorrent // check each torrent for abortion or // tracker updates + assert(m_disconnect_peer.empty()); for (std::map >::iterator i = m_torrents.begin(); i != m_torrents.end();) @@ -625,6 +834,9 @@ namespace libtorrent m_tracker_manager.queue_request( i->second->generate_tracker_request(m_listen_port)); i->second->disconnect_all(); + // make sure all connection are closed + // before the torrent is removed + purge_connections(); #ifndef NDEBUG sha1_hash i_hash = i->second->torrent_file().info_hash(); #endif @@ -638,7 +850,8 @@ namespace libtorrent { m_tracker_manager.queue_request( i->second->generate_tracker_request(m_listen_port) - , boost::get_pointer(i->second)); + , boost::get_pointer(i->second) + , i->second->tracker_password()); } i->second->second_tick(); @@ -648,11 +861,10 @@ namespace libtorrent // distribute the maximum upload rate among the peers - control_upload_rates( - m_upload_rate == -1 - ? std::numeric_limits::max() - : m_upload_rate - , m_connections); + control_upload_rates(m_upload_rate == -1 + ? std::numeric_limits::max() + : m_upload_rate + ,m_connections); m_tracker_manager.tick(); @@ -725,7 +937,7 @@ namespace libtorrent "peer_connection::has_data() != is_writability_monitored()\n"; error_log << "peer_connection::has_data() " << p->has_data() << "\n"; error_log << "peer_connection::send_quota_left " << p->send_quota_left() << "\n"; - error_log << "peer_connection::upload_bandwidth.given " << p->upload_bandwidth.given << "\n"; + error_log << "peer_connection::send_quota " << p->send_quota() << "\n"; error_log << "peer_connection::get_peer_id " << p->get_peer_id() << "\n"; error_log << "place: " << place << "\n"; error_log.flush(); @@ -893,8 +1105,7 @@ namespace libtorrent = m_impl.m_connections.begin(); i != m_impl.m_connections.end();) { - i->second->upload_bandwidth.given = std::numeric_limits::max(); - i->second->update_send_quota_left(); + i->second->set_send_quota(-1); } }