graceful disconnect mode which finishes transactions before disconnecting peers

This commit is contained in:
Arvid Norberg 2010-10-30 08:36:18 +00:00
parent 0dbef9103a
commit e4de1fc8b1
11 changed files with 118 additions and 31 deletions

View File

@ -1,3 +1,4 @@
* graceful peer disconnect mode which finishes transactions before disconnecting peers
* support chunked encoding for web seeds (only for BEP 19, web seeds)
* optimized session startup time
* support SSL for web seeds, through all proxies

View File

@ -299,7 +299,7 @@ void bind_torrent_handle()
.def("is_seed", _(&torrent_handle::is_seed))
.def("is_finished", _(&torrent_handle::is_finished))
.def("is_paused", _(&torrent_handle::is_paused))
.def("pause", _(&torrent_handle::pause))
.def("pause", _(&torrent_handle::pause), arg("flags") = 0)
.def("resume", _(&torrent_handle::resume))
.def("clear_error", _(&torrent_handle::clear_error))
.def("set_priority", _(&torrent_handle::set_priority))
@ -366,6 +366,10 @@ void bind_torrent_handle()
#endif
;
enum_<torrent_handle::save_resume_flags_t>("pause_flags_t")
.value("graceful_pause", torrent_handle::graceful_pause)
;
enum_<torrent_handle::save_resume_flags_t>("save_resume_flags_t")
.value("flush_disk_cache", torrent_handle::flush_disk_cache)
;

View File

@ -1955,7 +1955,8 @@ Its declaration looks like this::
void use_interface(char const* net_interface) const;
void pause() const;
enum pause_flags_t { graceful_pause = 1 };
void pause(int flags = 0) const;
void resume() const;
bool is_paused() const;
bool is_seed() const;
@ -2393,7 +2394,8 @@ pause() resume() is_paused()
::
void pause() const;
enum pause_flags_t { graceful_pause = 1 };
void pause(int flags) const;
void resume() const;
bool is_paused() const;
@ -2403,6 +2405,12 @@ all potential (not connected) peers. You can use ``is_paused()`` to determine if
is currently paused. Torrents may be paused automatically if there is a file error (e.g. disk full)
or something similar. See file_error_alert_.
The ``flags`` argument to pause can be set to ``torrent_handle::graceful_pause`` which will
delay the disconnect of peers that we're still downloading outstanding requests from. The torrent
will not accept any more requests and will disconnect all idle peers. As soon as a peer is
done transferring the blocks that were requested from it, it is disconnected. This is a graceful
shut down of the torrent in the sense that no downloaded bytes are wasted.
torrents that are auto-managed may be automatically resumed again. It does not make sense to
pause an auto-managed torrent without making it not automanaged first. Torrents are auto-managed
by default when added to the session. For more information, see queuing_.

View File

@ -1222,7 +1222,7 @@ int main(int argc, char* argv[])
else
{
h.auto_managed(false);
h.pause();
h.pause(torrent_handle::graceful_pause);
}
// the alert handler for save_resume_data_alert
// will save it to disk
@ -1336,7 +1336,9 @@ int main(int argc, char* argv[])
out += str;
}
if (h.is_paused()) out += esc("34");
torrent_status s = h.status();
if (s.paused) out += esc("34");
else out += esc("37");
std::string name = h.name();
@ -1344,9 +1346,6 @@ int main(int argc, char* argv[])
snprintf(str, sizeof(str), "%-40s %s ", name.c_str(), term);
out += str;
torrent_status s = h.status();
bool paused = h.is_paused();
bool auto_managed = h.is_auto_managed();
bool sequential_download = h.is_sequential_download();
@ -1373,7 +1372,7 @@ int main(int argc, char* argv[])
{
snprintf(str, sizeof(str), "%-13s down: (%s%s%s) up: %s%s%s (%s%s%s) swarm: %4d:%4d"
" bw queue: (%d|%d) all-time (Rx: %s%s%s Tx: %s%s%s) seed rank: %x %c%s\n"
, (paused && !auto_managed)?"paused":(paused && auto_managed)?"queued":state_str[s.state]
, (s.paused && !auto_managed)?"paused":(s.paused && auto_managed)?"queued":state_str[s.state]
, esc("32"), add_suffix(s.total_download).c_str(), term
, esc("31"), add_suffix(s.upload_rate, "/s").c_str(), term
, esc("31"), add_suffix(s.total_upload).c_str(), term

View File

@ -295,6 +295,8 @@ namespace libtorrent
std::vector<pending_block> const& request_queue() const;
std::vector<peer_request> const& upload_queue() const;
void clear_request_queue();
// estimate of how long it will take until we have
// received all piece requests that we have sent
// if extra_bytes is specified, it will include those
@ -539,6 +541,8 @@ namespace libtorrent
bool has_country() const { return m_country[0] != 0; }
#endif
int outstanding_bytes() const { return m_outstanding_bytes; }
int send_buffer_size() const
{ return m_send_buffer.size(); }

View File

@ -151,8 +151,10 @@ namespace libtorrent
void set_share_mode(bool s);
bool share_mode() const { return m_share_mode; }
bool graceful_pause() const { return m_graceful_pause_mode; }
void set_upload_mode(bool b);
bool upload_mode() const { return m_upload_mode; }
bool upload_mode() const { return m_upload_mode || m_graceful_pause_mode; }
bool is_upload_only() const
{ return (is_finished() || upload_mode()) && !super_seeding(); }
@ -222,9 +224,9 @@ namespace libtorrent
bool has_error() const { return m_error; }
void flush_cache();
void pause();
void pause(bool graceful = false);
void resume();
void set_allow_peers(bool b);
void set_allow_peers(bool b, bool graceful_pause = false);
void set_announce_to_dht(bool b) { m_announce_to_dht = b; }
void set_announce_to_trackers(bool b) { m_announce_to_trackers = b; }
void set_announce_to_lsd(bool b) { m_announce_to_lsd = b; }
@ -235,7 +237,7 @@ namespace libtorrent
bool is_paused() const;
bool allows_peers() const { return m_allow_peers; }
bool is_torrent_paused() const { return !m_allow_peers; }
bool is_torrent_paused() const { return !m_allow_peers || m_graceful_pause_mode; }
void force_recheck();
void save_resume_data(int flags);
@ -1224,6 +1226,11 @@ namespace libtorrent
// round-robin index into m_interfaces
mutable boost::uint8_t m_interface_index;
// set to true when this torrent has been paused but
// is waiting to finish all current download requests
// before actually closing all connections
bool m_graceful_pause_mode:1;
};
}

View File

@ -469,7 +469,8 @@ namespace libtorrent
bool is_seed() const;
bool is_finished() const;
bool is_paused() const;
void pause() const;
enum pause_flags_t { graceful_pause = 1 };
void pause(int flags = 0) const;
void resume() const;
void set_upload_mode(bool b) const;
void set_share_mode(bool b) const;

View File

@ -1235,9 +1235,6 @@ namespace libtorrent
{
INVARIANT_CHECK;
boost::shared_ptr<torrent> t = m_torrent.lock();
TORRENT_ASSERT(t);
#ifndef TORRENT_DISABLE_EXTENSIONS
for (extension_list_t::iterator i = m_extensions.begin()
, end(m_extensions.end()); i != end; ++i)
@ -1252,6 +1249,14 @@ namespace libtorrent
#endif
m_peer_choked = true;
clear_request_queue();
}
void peer_connection::clear_request_queue()
{
boost::shared_ptr<torrent> t = m_torrent.lock();
TORRENT_ASSERT(t);
// clear the requests that haven't been sent yet
if (peer_info_struct() == 0 || !peer_info_struct()->on_parole)
{
@ -1269,7 +1274,6 @@ namespace libtorrent
m_request_queue.clear();
m_queued_time_critical = 0;
}
}
bool match_request(peer_request const& r, piece_block const& b, int block_size)
@ -3108,6 +3112,15 @@ namespace libtorrent
if (m_disconnecting) return;
if (t->graceful_pause() && m_outstanding_bytes == 0)
{
#ifdef TORRENT_VERBOSE_LOGGING
(*m_logger) << time_now_string() << " *** GRACEFUL PAUSE [ NO MORE DOWNLOAD ]\n";
#endif
disconnect(errors::torrent_paused);
return;
}
if ((int)m_download_queue.size() >= m_desired_queue_size
|| t->upload_mode()) return;

View File

@ -2647,7 +2647,8 @@ namespace aux {
#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING || defined TORRENT_LOGGING
t->log_to_all_peers(("AUTO MANAGER PAUSING TORRENT: " + t->torrent_file().name()).c_str());
#endif
t->set_allow_peers(false);
// use graceful pause for auto-managed torrents
t->set_allow_peers(false, true);
}
}
}
@ -2754,6 +2755,7 @@ namespace aux {
if (!pi) continue;
torrent* t = p->associated_torrent().lock().get();
if (!t) continue;
if (t->is_paused()) continue;
if (pi->optimistically_unchoked)
{
@ -2849,7 +2851,7 @@ namespace aux {
torrent* t = p->associated_torrent().lock().get();
policy::peer* pi = p->peer_info_struct();
if (p->ignore_unchoke_slots() || t == 0 || pi == 0) continue;
if (p->ignore_unchoke_slots() || t == 0 || pi == 0 || t->is_paused()) continue;
if (m_settings.choking_algorithm == session_settings::bittyrant_choker)
{

View File

@ -389,6 +389,7 @@ namespace libtorrent
, m_last_upload(0)
, m_downloaders(0xffffff)
, m_interface_index(0)
, m_graceful_pause_mode(false)
{
#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING
(*m_ses.m_logger) << time_now_string() << " creating torrent: "
@ -3233,6 +3234,7 @@ namespace libtorrent
{
INVARIANT_CHECK;
TORRENT_ASSERT(!m_graceful_pause_mode);
TORRENT_ASSERT(c.is_choked());
TORRENT_ASSERT(!c.ignore_unchoke_slots());
// when we're unchoking the optimistic slots, we might
@ -5008,7 +5010,7 @@ namespace libtorrent
void torrent::check_invariant() const
{
TORRENT_ASSERT(m_ses.is_network_thread());
if (is_paused()) TORRENT_ASSERT(num_peers() == 0);
if (is_paused()) TORRENT_ASSERT(num_peers() == 0 || m_graceful_pause_mode);
if (!should_check_files())
TORRENT_ASSERT(m_state != torrent_status::checking_files);
@ -5526,7 +5528,8 @@ namespace libtorrent
|| m_state == torrent_status::queued_for_checking)
&& (m_allow_peers || m_auto_managed)
&& !has_error()
&& !m_abort;
&& !m_abort
&& !m_graceful_pause_mode;
}
void torrent::flush_cache()
@ -5546,20 +5549,24 @@ namespace libtorrent
bool torrent::is_paused() const
{
TORRENT_ASSERT(m_ses.is_network_thread());
return !m_allow_peers || m_ses.is_paused();
return !m_allow_peers || m_ses.is_paused() || m_graceful_pause_mode;
}
void torrent::pause()
void torrent::pause(bool graceful)
{
TORRENT_ASSERT(m_ses.is_network_thread());
INVARIANT_CHECK;
if (!m_allow_peers) return;
bool checking_files = should_check_files();
m_allow_peers = false;
if (!graceful) m_allow_peers = false;
m_announce_to_dht = false;
m_announce_to_trackers = false;
m_announce_to_lsd = false;
// mark torrent as upload only to make peers stop request more pieces
m_graceful_pause_mode = graceful;
if (!m_ses.is_paused())
do_pause();
if (checking_files && !should_check_files())
@ -5609,7 +5616,44 @@ namespace libtorrent
alerts().post_alert(torrent_paused_alert(get_handle()));
}
disconnect_all(errors::torrent_paused);
if (!m_graceful_pause_mode)
{
disconnect_all(errors::torrent_paused);
}
else
{
// disconnect all peers with no outstanding data to receive
// and choke all remaining peers to prevent responding to new
// requests
for (std::set<peer_connection*>::iterator i = m_connections.begin()
, end(m_connections.end()); i != end;)
{
std::set<peer_connection*>::iterator j = i++;
peer_connection* p = *j;
TORRENT_ASSERT(p->associated_torrent().lock().get() == this);
if (p->is_disconnecting())
m_connections.erase(j);
if (p->outstanding_bytes() > 0)
{
#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING
(*p->m_logger) << "*** CHOKING PEER: torrent graceful paused\n";
#endif
// remove any un-sent requests from the queue
p->clear_request_queue();
// don't accept new requests from the peer
if (!p->is_choked()) m_ses.choke_peer(*p);
continue;
}
#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING
(*p->m_logger) << "*** CLOSING CONNECTION: torrent_paused\n";
#endif
p->disconnect(errors::torrent_paused);
}
}
stop_announcing();
}
@ -5628,14 +5672,17 @@ namespace libtorrent
}
#endif
void torrent::set_allow_peers(bool b)
void torrent::set_allow_peers(bool b, bool graceful)
{
TORRENT_ASSERT(m_ses.is_network_thread());
if (m_allow_peers == b) return;
if (m_allow_peers == b
&& m_graceful_pause_mode == graceful) return;
bool checking_files = should_check_files();
m_allow_peers = b;
m_graceful_pause_mode = graceful;
if (!b)
{
@ -5667,6 +5714,7 @@ namespace libtorrent
m_announce_to_dht = true;
m_announce_to_trackers = true;
m_announce_to_lsd = true;
m_graceful_pause_mode = false;
do_resume();
if (!checking_files && should_check_files())
queue_torrent_check();
@ -6706,7 +6754,7 @@ namespace libtorrent
st.num_complete = (m_complete == 0xffffff) ? -1 : m_complete;
st.num_incomplete = (m_incomplete == 0xffffff) ? -1 : m_incomplete;
st.paused = !m_allow_peers;
st.paused = !m_allow_peers || m_graceful_pause_mode;
bytes_done(st, flags & torrent_handle::query_accurate_download_counters);
TORRENT_ASSERT(st.total_wanted_done >= 0);
TORRENT_ASSERT(st.total_done >= st.total_wanted_done);

View File

@ -371,10 +371,10 @@ namespace libtorrent
return r;
}
void torrent_handle::pause() const
void torrent_handle::pause(int flags) const
{
INVARIANT_CHECK;
TORRENT_ASYNC_CALL(pause);
TORRENT_ASYNC_CALL1(pause, bool(flags & graceful_pause));
}
void torrent_handle::set_share_mode(bool b) const