added initial support for share-mode

This commit is contained in:
Arvid Norberg
2010-09-05 16:01:36 +00:00
parent a8cc326df6
commit b6f92aa981
16 changed files with 377 additions and 17 deletions

View File

@@ -378,6 +378,7 @@ namespace libtorrent
, m_allow_peers(!p.paused)
, m_upload_mode(p.upload_mode)
, m_auto_managed(p.auto_managed)
, m_share_mode(p.share_mode)
, m_num_verified(0)
, m_last_scrape(0)
, m_last_download(0)
@@ -418,7 +419,6 @@ namespace libtorrent
m_trackers.back().source = announce_entry::source_magnet_link;
m_torrent_file->add_tracker(p.tracker_url);
}
}
void torrent::start()
@@ -533,6 +533,19 @@ namespace libtorrent
}
}
void torrent::send_share_mode()
{
#ifndef TORRENT_DISABLE_EXTENSIONS
for (std::set<peer_connection*>::iterator i = m_connections.begin()
, end(m_connections.end()); i != end; ++i)
{
if ((*i)->type() != peer_connection::bittorrent_connection) continue;
bt_peer_connection* p = (bt_peer_connection*)*i;
p->write_share_mode();
}
#endif
}
void torrent::send_upload_only()
{
#ifndef TORRENT_DISABLE_EXTENSIONS
@@ -546,6 +559,20 @@ namespace libtorrent
#endif
}
void torrent::set_share_mode(bool s)
{
if (s == m_share_mode) return;
m_share_mode = s;
// in share mode, all pieces have their priorities initialized to 0
std::fill(m_file_priority.begin(), m_file_priority.end(), !m_share_mode);
update_piece_priorities();
if (m_share_mode) recalc_share_mode();
}
void torrent::set_upload_mode(bool b)
{
if (b == m_upload_mode) return;
@@ -855,6 +882,16 @@ namespace libtorrent
m_picker->init(blocks_per_piece, blocks_in_last_piece, m_torrent_file->num_pieces());
}
if (m_share_mode)
{
// in share mode, all pieces have their priorities initialized to 0
std::fill(m_file_priority.begin(), m_file_priority.end(), 0);
}
// in case file priorities were passed in via the add_torrent_params
// ans also in the case of share mode, we need to update the priorities
update_piece_priorities();
std::vector<std::string> const& url_seeds = m_torrent_file->url_seeds();
for (std::vector<std::string>::const_iterator i = url_seeds.begin()
, end(url_seeds.end()); i != end; ++i)
@@ -2335,6 +2372,9 @@ namespace libtorrent
}
m_last_download = 0;
if (m_share_mode)
recalc_share_mode();
}
void torrent::piece_failed(int index)
@@ -3744,6 +3784,7 @@ namespace libtorrent
m_file_priority[i] = file_priority->list_int_value_at(i, 1);
update_piece_priorities();
}
lazy_entry const* piece_priority = rd.dict_find_string("piece_priority");
if (piece_priority && piece_priority->string_length()
== m_torrent_file->num_pieces())
@@ -4365,6 +4406,9 @@ namespace libtorrent
}
#endif
if (m_share_mode)
recalc_share_mode();
return peerinfo->connection;
}
@@ -4494,6 +4538,10 @@ namespace libtorrent
#if defined TORRENT_DEBUG && !defined TORRENT_DISABLE_INVARIANT_CHECKS
m_policy.check_invariant();
#endif
if (m_share_mode)
recalc_share_mode();
return true;
}
@@ -4961,7 +5009,7 @@ namespace libtorrent
if (!i->not_wanted && !i->timed_out) ++num_requests[i->block];
if (!p.is_choked() && !p.ignore_unchoke_slots()) ++num_uploads;
torrent* associated_torrent = p.associated_torrent().lock().get();
if (associated_torrent != this)
if (associated_torrent != this && associated_torrent != 0)
TORRENT_ASSERT(false);
}
TORRENT_ASSERT(num_uploads == m_num_uploads);
@@ -5862,6 +5910,152 @@ namespace libtorrent
m_stat.second_tick(tick_interval_ms);
}
void torrent::recalc_share_mode()
{
TORRENT_ASSERT(share_mode());
if (is_seed()) return;
int pieces_in_torrent = m_torrent_file->num_pieces();
int num_seeds = 0;
int num_peers = 0;
int num_downloaders = 0;
int missing_pieces = 0;
int num_interested = 0;
for (std::set<peer_connection*>::iterator i = m_connections.begin()
, end(m_connections.end()); i != end; ++i)
{
peer_connection* p = *i;
if (p->is_connecting()) continue;
++num_peers;
if (p->is_seed())
{
++num_seeds;
continue;
}
if (p->share_mode()) continue;
if ((*i)->is_peer_interested()) ++num_interested;
++num_downloaders;
missing_pieces += pieces_in_torrent - p->num_have_pieces();
}
if (num_peers == 0) return;
if (num_seeds * 100 / num_peers > 50
&& (num_peers * 100 / m_max_connections > 90
|| num_peers > 20))
{
// we are connected to more than 90% seeds (and we're beyond
// 90% of the max number of connections). That will
// limit our ability to upload. We need more downloaders.
// disconnect some seeds so that we don't have more than 50%
int to_disconnect = num_seeds - num_peers / 2;
std::vector<peer_connection*> seeds;
seeds.reserve(num_seeds);
for (std::set<peer_connection*>::iterator i = m_connections.begin()
, end(m_connections.end()); i != end; ++i)
{
peer_connection* p = *i;
if (p->is_seed()) seeds.push_back(p);
}
std::random_shuffle(seeds.begin(), seeds.end());
TORRENT_ASSERT(to_disconnect <= int(seeds.size()));
for (int i = 0; i < to_disconnect; ++i)
seeds[i]->disconnect(errors::upload_upload_connection);
}
if (num_downloaders == 0) return;
// assume that the seeds are about as fast as us. During the time
// we can download one piece, and upload one piece, each seed
// can upload two pieces.
missing_pieces -= 2 * num_seeds;
if (missing_pieces <= 0) return;
// missing_pieces represents our opportunity to download pieces
// and share them more than once each
// now, download at least one piece, otherwise download one more
// piece if our downloaded (and downloading) pieces is less than 50%
// of the uploaded bytes
int num_downloaded_pieces = (std::max)(m_picker->num_have()
, pieces_in_torrent - m_picker->num_filtered());
if (num_downloaded_pieces * m_torrent_file->piece_length()
* settings().share_mode_target > m_total_uploaded
&& num_downloaded_pieces > 0)
return;
// don't have more pieces downloading in parallel than 5% of the total
// number of pieces we have downloaded
if (m_picker->get_download_queue().size() > num_downloaded_pieces / 20)
return;
// one more important property is that there are enough pieces
// that more than one peer wants to download
// make sure that there are enough downloaders for the rarest
// piece. Go through all pieces, figure out which one is the rarest
// and how many peers that has that piece
std::vector<int> rarest_pieces;
int num_pieces = m_torrent_file->num_pieces();
int rarest_rarity = INT_MAX;
bool prio_updated = false;
for (int i = 0; i < num_pieces; ++i)
{
piece_picker::piece_pos const& pp = m_picker->piece_stats(i);
if (pp.peer_count == 0) continue;
if (pp.filtered() && (pp.have() || pp.downloading))
{
m_picker->set_piece_priority(i, 1);
prio_updated = true;
continue;
}
// don't count pieces we already have or are downloading
if (!pp.filtered() || pp.have()) continue;
if (pp.peer_count > rarest_rarity) continue;
if (pp.peer_count == rarest_rarity)
{
rarest_pieces.push_back(i);
continue;
}
rarest_pieces.clear();
rarest_rarity = pp.peer_count;
rarest_pieces.push_back(i);
}
if (prio_updated)
m_policy.recalculate_connect_candidates();
// now, rarest_pieces is a list of all pieces that are the rarest ones.
// and rarest_rarity is the number of peers that have the rarest pieces
// if there's only a single peer that doesn't have the rarest piece
// it's impossible for us to download one piece and upload it
// twice. i.e. we cannot get a positive share ratio
if (num_peers - rarest_rarity < settings().share_mode_target) return;
// we might be able to do better than a share ratio of 2 if there are
// enough downloaders of the pieces we already have.
// TODO: go through the pieces we have and count the total number
// of downloaders we have. Only count peers that are interested in us
// since some peers might not send have messages for pieces we have
// it num_interested == 0, we need to pick a new piece
// now, pick one of the rarest pieces to download
int pick = rand() % rarest_pieces.size();
bool was_finished = is_finished();
m_picker->set_piece_priority(rarest_pieces[pick], 1);
update_peer_interest(was_finished);
m_policy.recalculate_connect_candidates();
}
void torrent::refresh_explicit_cache(int cache_size)
{
TORRENT_ASSERT(m_ses.is_network_thread());
@@ -6404,6 +6598,7 @@ namespace libtorrent
st.completed_time = m_completed_time;
st.last_scrape = m_last_scrape;
st.share_mode = m_share_mode;
st.upload_mode = m_upload_mode;
st.up_bandwidth_queue = 0;
st.down_bandwidth_queue = 0;