initial super seeding support
This commit is contained in:
parent
14c142d997
commit
b41cdd6469
|
@ -1,4 +1,4 @@
|
|||
|
||||
* added super seeding
|
||||
* added add_piece() function to inject data from external sources
|
||||
* add_tracker() function added to torrent_handle
|
||||
* if there is no working tracker, current_tracker is the
|
||||
|
|
|
@ -1717,6 +1717,9 @@ Its declaration looks like this::
|
|||
void rename_file(int index, boost::filesystem::wpath) const;
|
||||
storage_interface* get_storage_impl() const;
|
||||
|
||||
bool super_seeding() const;
|
||||
void super_seeding(bool on) const;
|
||||
|
||||
enum flags_t { overwrite_existing = 1 };
|
||||
void add_piece(int piece, char const* data, int flags = 0) const;
|
||||
|
||||
|
@ -1871,6 +1874,18 @@ get_storage_impl()
|
|||
Returns the storage implementation for this torrent. This depends on the
|
||||
storage contructor function that was passed to ``session::add_torrent``.
|
||||
|
||||
super_seeding()
|
||||
---------------
|
||||
|
||||
::
|
||||
|
||||
bool super_seeding() const;
|
||||
void super_seeding(bool on) const;
|
||||
|
||||
Enables or disabled super seeding/initial seeding for this torrent. The torrent
|
||||
needs to be a seed for this to take effect. The overload that returns a bool
|
||||
tells you of super seeding is enabled or not.
|
||||
|
||||
add_piece()
|
||||
-----------
|
||||
|
||||
|
|
|
@ -210,6 +210,11 @@ namespace libtorrent
|
|||
// this adds an announcement in the announcement queue
|
||||
// it will let the peer know that we have the given piece
|
||||
void announce_piece(int index);
|
||||
|
||||
// this will tell the peer to announce the given piece
|
||||
// and only allow it to request that piece
|
||||
void superseed_piece(int index);
|
||||
int superseed_piece() const { return m_superseed_piece; }
|
||||
|
||||
// tells if this connection has data it want to send
|
||||
// and has enough upload bandwidth quota left to send it.
|
||||
|
@ -758,7 +763,14 @@ namespace libtorrent
|
|||
// so that it can be removed from the queue
|
||||
// once the connection completes
|
||||
int m_connection_ticket;
|
||||
|
||||
|
||||
// if this is -1, superseeding is not active. If it is >= 0
|
||||
// this is the piece that is available to this peer. Only
|
||||
// this piece can be downloaded from us by this peer.
|
||||
// This will remain the current piece for this peer until
|
||||
// another peer sends us a have message for this piece
|
||||
int m_superseed_piece;
|
||||
|
||||
// bytes downloaded since last second
|
||||
// timer timeout; used for determining
|
||||
// approx download rate
|
||||
|
|
|
@ -422,6 +422,12 @@ namespace libtorrent
|
|||
// --------------------------------------------
|
||||
// PIECE MANAGEMENT
|
||||
|
||||
bool super_seeding() const
|
||||
{ return m_super_seeding; }
|
||||
|
||||
void super_seeding(bool on);
|
||||
int get_piece_to_super_seed(bitfield const&);
|
||||
|
||||
// returns true if we have downloaded the given piece
|
||||
bool have_piece(int index) const
|
||||
{
|
||||
|
@ -967,6 +973,10 @@ namespace libtorrent
|
|||
// has been initialized with files_checked().
|
||||
bool m_connections_initialized:1;
|
||||
|
||||
// if this is true, we're currently super seeding this
|
||||
// torrent.
|
||||
bool m_super_seeding:1;
|
||||
|
||||
// is set to true every time there is an incoming
|
||||
// connection to this torrent
|
||||
bool m_has_incoming:1;
|
||||
|
|
|
@ -479,6 +479,9 @@ namespace libtorrent
|
|||
void rename_file(int index, fs::path const& new_name) const;
|
||||
void rename_file(int index, fs::wpath const& new_name) const;
|
||||
|
||||
bool super_seeding() const;
|
||||
void super_seeding(bool on) const;
|
||||
|
||||
sha1_hash info_hash() const;
|
||||
|
||||
bool operator==(const torrent_handle& h) const
|
||||
|
|
|
@ -987,7 +987,7 @@ namespace libtorrent
|
|||
r.piece = detail::read_int32(ptr);
|
||||
r.start = detail::read_int32(ptr);
|
||||
r.length = detail::read_int32(ptr);
|
||||
|
||||
|
||||
incoming_request(r);
|
||||
}
|
||||
|
||||
|
@ -1452,7 +1452,22 @@ namespace libtorrent
|
|||
// in this case, have_all or have_none should be sent instead
|
||||
TORRENT_ASSERT(!m_supports_fast || !t->is_seed() || t->num_have() != 0);
|
||||
|
||||
if (m_supports_fast && t->is_seed())
|
||||
if (t->super_seeding())
|
||||
{
|
||||
if (m_supports_fast) write_have_none();
|
||||
|
||||
// if we are super seeding, pretend to not have any piece
|
||||
// and don't send a bitfield
|
||||
#ifdef TORRENT_DEBUG
|
||||
m_sent_bitfield = true;
|
||||
#endif
|
||||
|
||||
// bootstrap superseeding by sending one have message
|
||||
superseed_piece(t->get_piece_to_super_seed(
|
||||
get_bitfield()));
|
||||
return;
|
||||
}
|
||||
else if (m_supports_fast && t->is_seed())
|
||||
{
|
||||
write_have_all();
|
||||
send_allowed_set();
|
||||
|
@ -1477,6 +1492,7 @@ namespace libtorrent
|
|||
}
|
||||
|
||||
int num_pieces = t->torrent_file().num_pieces();
|
||||
|
||||
int lazy_pieces[50];
|
||||
int num_lazy_pieces = 0;
|
||||
int lazy_piece = 0;
|
||||
|
@ -1553,6 +1569,7 @@ namespace libtorrent
|
|||
<< " ==> HAVE [ piece: " << lazy_pieces[i] << "]\n";
|
||||
#endif
|
||||
}
|
||||
// TODO: if we're finished, send upload_only message
|
||||
}
|
||||
|
||||
if (m_supports_fast)
|
||||
|
@ -1588,7 +1605,11 @@ namespace libtorrent
|
|||
handshake["reqq"] = m_ses.settings().max_allowed_in_request_queue;
|
||||
boost::shared_ptr<torrent> t = associated_torrent().lock();
|
||||
TORRENT_ASSERT(t);
|
||||
if (t->is_finished()) handshake["upload_only"] = 1;
|
||||
|
||||
// if we're using lazy bitfields or if we're super seeding, don't say
|
||||
// we're upload only, since it might make peers disconnect
|
||||
if (t->is_finished() && !t->super_seeding() && !m_ses.settings().lazy_bitfields)
|
||||
handshake["upload_only"] = 1;
|
||||
|
||||
tcp::endpoint ep = m_ses.get_ipv6_interface();
|
||||
if (!is_any(ep.address()))
|
||||
|
|
|
@ -106,6 +106,7 @@ namespace libtorrent
|
|||
, m_peer_info(peerinfo)
|
||||
, m_speed(slow)
|
||||
, m_connection_ticket(-1)
|
||||
, m_superseed_piece(-1)
|
||||
, m_remote_bytes_dled(0)
|
||||
, m_remote_dl_rate(0)
|
||||
, m_outstanding_writing_bytes(0)
|
||||
|
@ -214,6 +215,7 @@ namespace libtorrent
|
|||
, m_peer_info(peerinfo)
|
||||
, m_speed(slow)
|
||||
, m_connection_ticket(-1)
|
||||
, m_superseed_piece(-1)
|
||||
, m_remote_bytes_dled(0)
|
||||
, m_remote_dl_rate(0)
|
||||
, m_outstanding_writing_bytes(0)
|
||||
|
@ -395,6 +397,15 @@ namespace libtorrent
|
|||
boost::shared_ptr<torrent> t = m_torrent.lock();
|
||||
TORRENT_ASSERT(t);
|
||||
|
||||
if (t->super_seeding())
|
||||
{
|
||||
#ifdef TORRENT_VERBOSE_LOGGING
|
||||
(*m_logger) << time_now_string()
|
||||
<< " *** SKIPPING ALLOWED SET BECAUSE OF SUPER SEEDING\n";
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
int num_allowed_pieces = m_ses.settings().allowed_fast_set_size;
|
||||
int num_pieces = t->torrent_file().num_pieces();
|
||||
|
||||
|
@ -1157,6 +1168,13 @@ namespace libtorrent
|
|||
}
|
||||
|
||||
t->get_policy().not_interested(*this);
|
||||
|
||||
if (t->super_seeding() && m_superseed_piece != -1)
|
||||
{
|
||||
// assume the peer has the piece we're superseeding to it
|
||||
// and give it another one
|
||||
if (!m_have_piece[m_superseed_piece]) incoming_have(m_superseed_piece);
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------
|
||||
|
@ -1216,6 +1234,20 @@ namespace libtorrent
|
|||
return;
|
||||
}
|
||||
|
||||
if (t->super_seeding())
|
||||
{
|
||||
// if we're superseeding and the peer just told
|
||||
// us that it completed the piece we're superseeding
|
||||
// to it, change the superseeding piece for this peer
|
||||
// if the peer optimizes out redundant have messages
|
||||
// this will be handled when the peer sends not-interested
|
||||
// instead.
|
||||
if (m_superseed_piece == index)
|
||||
{
|
||||
superseed_piece(t->get_piece_to_super_seed(m_have_piece));
|
||||
}
|
||||
}
|
||||
|
||||
if (m_have_piece[index])
|
||||
{
|
||||
#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING
|
||||
|
@ -1408,6 +1440,31 @@ namespace libtorrent
|
|||
boost::shared_ptr<torrent> t = m_torrent.lock();
|
||||
TORRENT_ASSERT(t);
|
||||
|
||||
if (m_superseed_piece != -1
|
||||
&& r.piece != m_superseed_piece)
|
||||
{
|
||||
++m_num_invalid_requests;
|
||||
#ifdef TORRENT_VERBOSE_LOGGING
|
||||
(*m_logger) << time_now_string()
|
||||
<< " <== INVALID_SUPER_SEED_REQUEST [ "
|
||||
"piece: " << r.piece << " | "
|
||||
"s: " << r.start << " | "
|
||||
"l: " << r.length << " | "
|
||||
"i: " << m_peer_interested << " | "
|
||||
"t: " << (int)t->torrent_file().piece_size(r.piece) << " | "
|
||||
"n: " << t->torrent_file().num_pieces() << " | "
|
||||
"h: " << t->have_piece(r.piece) << " | "
|
||||
"ss: " << m_superseed_piece << " ]\n";
|
||||
#endif
|
||||
|
||||
if (t->alerts().should_post<invalid_request_alert>())
|
||||
{
|
||||
t->alerts().post_alert(invalid_request_alert(
|
||||
t->get_handle(), m_remote, m_peer_id, r));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// if we haven't received a bitfield, it was
|
||||
// probably omitted, which is the same as 'have_none'
|
||||
if (!m_bitfield_received) incoming_have_none();
|
||||
|
@ -2755,6 +2812,42 @@ namespace libtorrent
|
|||
m_packet_size = packet_size;
|
||||
}
|
||||
|
||||
void peer_connection::superseed_piece(int index)
|
||||
{
|
||||
if (index == -1)
|
||||
{
|
||||
if (m_superseed_piece == -1) return;
|
||||
m_superseed_piece = -1;
|
||||
|
||||
#ifdef TORRENT_VERBOSE_LOGGING
|
||||
(*m_logger) << time_now_string()
|
||||
<< " *** ending super seed mode\n";
|
||||
#endif
|
||||
boost::shared_ptr<torrent> t = m_torrent.lock();
|
||||
assert(t);
|
||||
|
||||
for (int i = 0; i < m_have_piece.size(); ++i)
|
||||
{
|
||||
if (m_have_piece[i] || !t->have_piece(i)) continue;
|
||||
#ifdef TORRENT_VERBOSE_LOGGING
|
||||
(*m_logger) << " ==> HAVE [ piece: " << i << "] (ending super seed)\n";
|
||||
#endif
|
||||
write_have(i);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
assert(!has_piece(index));
|
||||
|
||||
#ifdef TORRENT_VERBOSE_LOGGING
|
||||
(*m_logger) << time_now_string()
|
||||
<< " ==> HAVE [ piece: " << index << "] (super seed)\n";
|
||||
#endif
|
||||
write_have(index);
|
||||
m_superseed_piece = index;
|
||||
}
|
||||
|
||||
void peer_connection::second_tick(float tick_interval)
|
||||
{
|
||||
ptime now(time_now());
|
||||
|
|
|
@ -184,6 +184,7 @@ namespace libtorrent
|
|||
, m_sequential_download(false)
|
||||
, m_got_tracker_response(false)
|
||||
, m_connections_initialized(true)
|
||||
, m_super_seeding(false)
|
||||
, m_has_incoming(false)
|
||||
, m_files_checked(false)
|
||||
, m_announcing(false)
|
||||
|
@ -258,6 +259,7 @@ namespace libtorrent
|
|||
, m_sequential_download(false)
|
||||
, m_got_tracker_response(false)
|
||||
, m_connections_initialized(false)
|
||||
, m_super_seeding(false)
|
||||
, m_has_incoming(false)
|
||||
, m_files_checked(false)
|
||||
, m_announcing(false)
|
||||
|
@ -1741,6 +1743,74 @@ namespace libtorrent
|
|||
m_host_resolver.cancel();
|
||||
}
|
||||
|
||||
void torrent::super_seeding(bool on)
|
||||
{
|
||||
if (on == m_super_seeding) return;
|
||||
|
||||
// don't turn on super seeding if we're not a seed
|
||||
TORRENT_ASSERT(!on || is_seed() || !m_files_checked);
|
||||
if (on && !is_seed() && m_files_checked) return;
|
||||
m_super_seeding = on;
|
||||
|
||||
if (m_super_seeding) return;
|
||||
|
||||
// disable super seeding for all peers
|
||||
for (peer_iterator i = begin(); i != end(); ++i)
|
||||
{
|
||||
(*i)->superseed_piece(-1);
|
||||
}
|
||||
}
|
||||
|
||||
int torrent::get_piece_to_super_seed(bitfield const& bits)
|
||||
{
|
||||
// return a piece with low availability that is not in
|
||||
// the bitfield and that is not currently being super
|
||||
// seeded by any peer
|
||||
TORRENT_ASSERT(m_super_seeding);
|
||||
|
||||
// do a linear search from the first piece
|
||||
int min_availability = 9999;
|
||||
std::vector<int> avail_vec;
|
||||
for (int i = 0; i < m_torrent_file->num_pieces(); ++i)
|
||||
{
|
||||
if (bits[i]) continue;
|
||||
|
||||
int availability = 0;
|
||||
for (const_peer_iterator j = begin(); j != end(); ++j)
|
||||
{
|
||||
if ((*j)->superseed_piece() == i)
|
||||
{
|
||||
// avoid superseeding the same piece to more than one
|
||||
// peer if we can avoid it. Do this by artificially
|
||||
// increase the availability
|
||||
availability = 999;
|
||||
break;
|
||||
}
|
||||
if ((*j)->has_piece(i)) ++availability;
|
||||
}
|
||||
if (availability > min_availability) continue;
|
||||
if (availability == min_availability)
|
||||
{
|
||||
avail_vec.push_back(i);
|
||||
continue;
|
||||
}
|
||||
TORRENT_ASSERT(availability < min_availability);
|
||||
min_availability = availability;
|
||||
avail_vec.clear();
|
||||
avail_vec.push_back(i);
|
||||
}
|
||||
|
||||
if (min_availability > 1)
|
||||
{
|
||||
// if the minimum availability is 2 or more,
|
||||
// we shouldn't be super seeding any more
|
||||
super_seeding(false);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return avail_vec[rand() % avail_vec.size()];
|
||||
}
|
||||
|
||||
void torrent::on_files_deleted(int ret, disk_io_job const& j)
|
||||
{
|
||||
session_impl::mutex_t::scoped_lock l(m_ses.m_mutex);
|
||||
|
@ -3581,6 +3651,9 @@ namespace libtorrent
|
|||
|
||||
if (!is_seed())
|
||||
{
|
||||
// turn off super seeding if we're not a seed
|
||||
if (m_super_seeding) m_super_seeding = false;
|
||||
|
||||
// if we just finished checking and we're not a seed, we are
|
||||
// likely to be unpaused
|
||||
if (m_ses.m_auto_manage_time_scaler > 1)
|
||||
|
|
|
@ -632,6 +632,18 @@ namespace libtorrent
|
|||
TORRENT_FORWARD(scrape_tracker());
|
||||
}
|
||||
|
||||
bool torrent_handle::super_seeding() const
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
TORRENT_FORWARD_RETURN(super_seeding(), false);
|
||||
}
|
||||
|
||||
void torrent_handle::super_seeding(bool on) const
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
TORRENT_FORWARD(super_seeding(on));
|
||||
}
|
||||
|
||||
void torrent_handle::set_ratio(float ratio) const
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
|
|
|
@ -241,7 +241,7 @@ boost::tuple<torrent_handle, torrent_handle, torrent_handle>
|
|||
setup_transfer(session* ses1, session* ses2, session* ses3
|
||||
, bool clear_files, bool use_metadata_transfer, bool connect_peers
|
||||
, std::string suffix, int piece_size
|
||||
, boost::intrusive_ptr<torrent_info>* torrent)
|
||||
, boost::intrusive_ptr<torrent_info>* torrent, bool super_seeding)
|
||||
{
|
||||
using namespace boost::filesystem;
|
||||
|
||||
|
@ -286,6 +286,7 @@ setup_transfer(session* ses1, session* ses2, session* ses3
|
|||
// use the same files
|
||||
sha1_hash info_hash = t->info_hash();
|
||||
torrent_handle tor1 = ses1->add_torrent(clone_ptr(t), "./tmp1" + suffix);
|
||||
tor1.super_seeding(super_seeding);
|
||||
TEST_CHECK(!ses1->get_torrents().empty());
|
||||
torrent_handle tor2;
|
||||
torrent_handle tor3;
|
||||
|
|
|
@ -50,7 +50,7 @@ boost::tuple<libtorrent::torrent_handle, libtorrent::torrent_handle
|
|||
setup_transfer(libtorrent::session* ses1, libtorrent::session* ses2
|
||||
, libtorrent::session* ses3, bool clear_files, bool use_metadata_transfer = true
|
||||
, bool connect = true, std::string suffix = "", int piece_size = 16 * 1024
|
||||
, boost::intrusive_ptr<libtorrent::torrent_info>* torrent = 0);
|
||||
, boost::intrusive_ptr<libtorrent::torrent_info>* torrent = 0, bool super_seeding = false);
|
||||
|
||||
void start_web_server(int port, bool ssl = false);
|
||||
void stop_web_server(int port);
|
||||
|
|
|
@ -44,10 +44,15 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
using boost::filesystem::remove_all;
|
||||
using boost::filesystem::exists;
|
||||
|
||||
void test_swarm()
|
||||
void test_swarm(bool super_seeding = false)
|
||||
{
|
||||
using namespace libtorrent;
|
||||
|
||||
// in case the previous run was terminated
|
||||
try { remove_all("./tmp1_swarm"); } catch (std::exception&) {}
|
||||
try { remove_all("./tmp2_swarm"); } catch (std::exception&) {}
|
||||
try { remove_all("./tmp3_swarm"); } catch (std::exception&) {}
|
||||
|
||||
session ses1(fingerprint("LT", 0, 1, 0, 0), std::make_pair(48000, 49000));
|
||||
ses1.set_alert_mask(alert::all_categories & ~alert::progress_notification);
|
||||
session ses2(fingerprint("LT", 0, 1, 0, 0), std::make_pair(49000, 50000));
|
||||
|
@ -87,7 +92,8 @@ void test_swarm()
|
|||
torrent_handle tor3;
|
||||
|
||||
// test using piece sizes smaller than 16kB
|
||||
boost::tie(tor1, tor2, tor3) = setup_transfer(&ses1, &ses2, &ses3, true, false, true, "_swarm", 8 * 1024);
|
||||
boost::tie(tor1, tor2, tor3) = setup_transfer(&ses1, &ses2, &ses3, true
|
||||
, false, true, "_swarm", 8 * 1024, 0, super_seeding);
|
||||
|
||||
float sum_dl_rate2 = 0.f;
|
||||
float sum_dl_rate3 = 0.f;
|
||||
|
@ -171,7 +177,7 @@ void test_swarm()
|
|||
// about 2 seconds
|
||||
ptime start = time_now();
|
||||
alert const* ret;
|
||||
while (ret = ses1.wait_for_alert(seconds(2)))
|
||||
while ((ret = ses1.wait_for_alert(seconds(2))))
|
||||
{
|
||||
a = ses1.pop_alert();
|
||||
std::cerr << ret->message() << std::endl;
|
||||
|
@ -179,21 +185,7 @@ void test_swarm()
|
|||
}
|
||||
TEST_CHECK(time_now() - start < seconds(3));
|
||||
TEST_CHECK(time_now() - start >= seconds(2));
|
||||
}
|
||||
|
||||
int test_main()
|
||||
{
|
||||
using namespace libtorrent;
|
||||
using namespace boost::filesystem;
|
||||
|
||||
// in case the previous run was terminated
|
||||
try { remove_all("./tmp1_swarm"); } catch (std::exception&) {}
|
||||
try { remove_all("./tmp2_swarm"); } catch (std::exception&) {}
|
||||
try { remove_all("./tmp3_swarm"); } catch (std::exception&) {}
|
||||
|
||||
test_swarm();
|
||||
|
||||
test_sleep(2000);
|
||||
TEST_CHECK(!exists("./tmp1_swarm/temporary"));
|
||||
TEST_CHECK(!exists("./tmp2_swarm/temporary"));
|
||||
TEST_CHECK(!exists("./tmp3_swarm/temporary"));
|
||||
|
@ -201,7 +193,18 @@ int test_main()
|
|||
remove_all("./tmp1_swarm");
|
||||
remove_all("./tmp2_swarm");
|
||||
remove_all("./tmp3_swarm");
|
||||
}
|
||||
|
||||
int test_main()
|
||||
{
|
||||
using namespace libtorrent;
|
||||
using namespace boost::filesystem;
|
||||
|
||||
test_swarm();
|
||||
|
||||
// with super seeding
|
||||
test_swarm(true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue