@@ -3717,6 +3718,10 @@ pieces are completed.
ip_block_notification |
Alerts when a peer is blocked by the ip blocker or port blocker. |
+
performance_warning |
+Alerts when some limit is reached that might limit the download
+or upload rate. |
+
all_categories |
The full bitmask, representing all available categories. |
@@ -3747,6 +3752,7 @@ public:
status_notification =
implementation defined,
progress_notification =
implementation defined,
ip_block_notification =
implementation defined,
+ performance_warning =
implementation defined,
all_categories =
implementation defined
};
@@ -4063,6 +4069,25 @@ torrent in question.
There are no additional data members in this alert.
This alert is generated when the metadata has been completely received and the info-hash
failed to match it. i.e. the metadata that was received was corrupt. libtorrent will
diff --git a/docs/manual.rst b/docs/manual.rst
index 6c9e75bee..0c71d35c9 100644
--- a/docs/manual.rst
+++ b/docs/manual.rst
@@ -3793,6 +3793,9 @@ is a bitmask with the following bits:
+--------------------------------+---------------------------------------------------------------------+
| ``ip_block_notification`` | Alerts when a peer is blocked by the ip blocker or port blocker. |
+--------------------------------+---------------------------------------------------------------------+
+| ``performance_warning`` | Alerts when some limit is reached that might limit the download |
+| | or upload rate. |
++--------------------------------+---------------------------------------------------------------------+
| ``all_categories`` | The full bitmask, representing all available categories. |
+--------------------------------+---------------------------------------------------------------------+
@@ -3826,6 +3829,7 @@ is its synopsis:
status_notification = *implementation defined*,
progress_notification = *implementation defined*,
ip_block_notification = *implementation defined*,
+ performance_warning = *implementation defined*,
all_categories = *implementation defined*
};
@@ -4205,6 +4209,28 @@ torrent in question.
There are no additional data members in this alert.
+performance_alert
+-----------------
+
+This alert is generated when a limit is reached that might have a negative impact on
+upload or download rate performance.
+
+::
+
+ struct performance_alert: torrent_alert
+ {
+ // ...
+
+ enum performance_warning_t
+ {
+ outstanding_disk_buffer_limit_reached,
+ outstanding_request_limit_reached,
+ };
+
+ performance_warning_t warning_code;
+ };
+
+
metadata_failed_alert
---------------------
diff --git a/include/libtorrent/alert.hpp b/include/libtorrent/alert.hpp
index f3df27742..9b2c0014f 100644
--- a/include/libtorrent/alert.hpp
+++ b/include/libtorrent/alert.hpp
@@ -82,6 +82,7 @@ namespace libtorrent {
status_notification = 0x40,
progress_notification = 0x80,
ip_block_notification = 0x100,
+ performance_warning = 0x200,
all_categories = 0xffffffff
};
diff --git a/include/libtorrent/alert_types.hpp b/include/libtorrent/alert_types.hpp
index ffa09bbf2..e85f3ed0d 100644
--- a/include/libtorrent/alert_types.hpp
+++ b/include/libtorrent/alert_types.hpp
@@ -151,6 +151,42 @@ namespace libtorrent
int index;
};
+ struct TORRENT_EXPORT performance_alert: torrent_alert
+ {
+ enum performance_warning_t
+ {
+ outstanding_disk_buffer_limit_reached,
+ outstanding_request_limit_reached,
+ };
+
+ performance_alert(torrent_handle const& h
+ , performance_warning_t w)
+ : torrent_alert(h)
+ , warning_code(w)
+ {}
+
+ virtual std::auto_ptr clone() const
+ { return std::auto_ptr(new performance_alert(*this)); }
+
+ virtual char const* what() const { return "performance warning"; }
+ virtual std::string message() const
+ {
+ static char const* warning_str[] =
+ {
+ "max outstanding disk writes reached",
+ "max outstanding piece requests reached",
+ };
+
+ return torrent_alert::message() + ": performance warning: "
+ + warning_str[warning_code];
+ }
+
+ const static int static_category = alert::performance_warning;
+ virtual int category() const { return static_category; }
+
+ performance_warning_t warning_code;
+ };
+
struct TORRENT_EXPORT state_changed_alert: torrent_alert
{
state_changed_alert(torrent_handle const& h
diff --git a/src/peer_connection.cpp b/src/peer_connection.cpp
index e8451d036..0977a32ba 100644
--- a/src/peer_connection.cpp
+++ b/src/peer_connection.cpp
@@ -1643,6 +1643,13 @@ namespace libtorrent
TORRENT_ASSERT(m_channel_state[download_channel] == peer_info::bw_idle);
m_download_queue.erase(b);
+ if (m_outstanding_writing_bytes >= m_ses.settings().max_outstanding_disk_bytes_per_connection
+ && t->alerts().should_post())
+ {
+ t->alerts().post_alert(performance_alert(t->get_handle()
+ , performance_alert::outstanding_disk_buffer_limit_reached));
+ }
+
if (!m_download_queue.empty())
{
m_timeout_extend = (std::max)(m_timeout_extend
@@ -2765,6 +2772,13 @@ namespace libtorrent
m_desired_queue_size = m_max_out_request_queue;
if (m_desired_queue_size < min_request_queue)
m_desired_queue_size = min_request_queue;
+
+ if (m_desired_queue_size == m_max_out_request_queue
+ && t->alerts().should_post())
+ {
+ t->alerts().post_alert(performance_alert(t->get_handle()
+ , performance_alert::outstanding_request_limit_reached));
+ }
}
if (!m_download_queue.empty()
diff --git a/test/setup_transfer.cpp b/test/setup_transfer.cpp
index ad708221f..115702833 100644
--- a/test/setup_transfer.cpp
+++ b/test/setup_transfer.cpp
@@ -196,14 +196,14 @@ boost::intrusive_ptr clone_ptr(boost::intrusive_ptr const& ptr)
return boost::intrusive_ptr(new T(*ptr));
}
-boost::intrusive_ptr create_torrent(std::ostream* file, int piece_size)
+boost::intrusive_ptr create_torrent(std::ostream* file, int piece_size, int num_pieces)
{
char const* tracker_url = "http://non-existent-name.com/announce";
using namespace boost::filesystem;
file_storage fs;
- int total_size = 2 * 1024 * 1024;
+ int total_size = piece_size * num_pieces;
fs.add_file(path("temporary"), total_size);
libtorrent::create_torrent t(fs, piece_size);
t.add_tracker(tracker_url);
@@ -253,7 +253,7 @@ setup_transfer(session* ses1, session* ses2, session* ses3
{
create_directory("./tmp1" + suffix);
std::ofstream file(("./tmp1" + suffix + "/temporary").c_str());
- t = ::create_torrent(&file, piece_size);
+ t = ::create_torrent(&file, piece_size, 1024 / 8);
file.close();
if (clear_files)
{
diff --git a/test/setup_transfer.hpp b/test/setup_transfer.hpp
index ed2684d2d..4585e4c5b 100644
--- a/test/setup_transfer.hpp
+++ b/test/setup_transfer.hpp
@@ -42,7 +42,7 @@ void print_alerts(libtorrent::session& ses, char const* name
, bool allow_no_torrents = false);
void test_sleep(int millisec);
-boost::intrusive_ptr create_torrent(std::ostream* file = 0, int piece_size = 16 * 1024);
+boost::intrusive_ptr create_torrent(std::ostream* file = 0, int piece_size = 16 * 1024, int num_pieces = 1024 / 8);
boost::tuple
diff --git a/test/test_transfer.cpp b/test/test_transfer.cpp
index 98425f819..71280266a 100644
--- a/test/test_transfer.cpp
+++ b/test/test_transfer.cpp
@@ -45,12 +45,64 @@ POSSIBILITY OF SUCH DAMAGE.
using boost::filesystem::remove_all;
using boost::filesystem::exists;
using boost::filesystem::create_directory;
+using namespace libtorrent;
+using boost::tuples::ignore;
+
+// test the maximum transfer rate
+void test_rate()
+{
+ session ses1(fingerprint("LT", 0, 1, 0, 0), std::make_pair(48575, 49000));
+ session ses2(fingerprint("LT", 0, 1, 0, 0), std::make_pair(49575, 50000));
+
+ torrent_handle tor1;
+ torrent_handle tor2;
+
+ create_directory("./tmp1_transfer");
+ std::ofstream file("./tmp1_transfer/temporary");
+ boost::intrusive_ptr t = ::create_torrent(&file, 4 * 1024 * 1024, 50);
+ file.close();
+
+ boost::tie(tor1, tor2, ignore) = setup_transfer(&ses1, &ses2, 0
+ , true, false, true, "_transfer", 0, &t);
+
+ ses1.set_alert_mask(alert::all_categories & ~alert::progress_notification);
+ ses2.set_alert_mask(alert::all_categories & ~alert::progress_notification);
+
+ ptime start = time_now();
+
+ for (int i = 0; i < 40; ++i)
+ {
+ print_alerts(ses1, "ses1");
+ print_alerts(ses2, "ses2");
+
+ torrent_status st1 = tor1.status();
+ torrent_status st2 = tor2.status();
+
+ std::cerr
+ << "up: \033[33m" << st1.upload_payload_rate / 1000000.f << "MB/s "
+ << " down: \033[32m" << st2.download_payload_rate / 1000000.f << "MB/s "
+ << "\033[0m" << int(st2.progress * 100) << "% "
+ << std::endl;
+
+ if (st1.paused) break;
+ if (tor2.is_seed()) break;
+ test_sleep(1000);
+ }
+
+ TEST_CHECK(tor2.is_seed());
+
+ time_duration dt = time_now() - start;
+
+ std::cerr << "downloaded " << t->total_size() << " bytes "
+ "in " << (total_milliseconds(dt) / 1000.f) << " seconds" << std::endl;
+
+ std::cerr << "average download rate: " << (t->total_size() / total_milliseconds(dt))
+ << " kB/s" << std::endl;
+
+}
void test_transfer()
{
- using namespace libtorrent;
- using boost::tuples::ignore;
-
session ses1(fingerprint("LT", 0, 1, 0, 0), std::make_pair(48075, 49000));
session ses2(fingerprint("LT", 0, 1, 0, 0), std::make_pair(49075, 50000));
@@ -72,7 +124,7 @@ void test_transfer()
// test using piece sizes smaller than 16kB
boost::tie(tor1, tor2, ignore) = setup_transfer(&ses1, &ses2, 0
- , true, false, true, "_transfer", 8 * 1024, &t);
+ , true, false, true, "_transfer", 8 * 1024, &t);
// set half of the pieces to priority 0
int num_pieces = tor2.get_torrent_info().num_pieces();
@@ -83,9 +135,6 @@ void test_transfer()
ses1.set_alert_mask(alert::all_categories & ~alert::progress_notification);
ses2.set_alert_mask(alert::all_categories & ~alert::progress_notification);
- tor1.resume();
- tor2.resume();
-
for (int i = 0; i < 30; ++i)
{
print_alerts(ses1, "ses1");
@@ -158,6 +207,7 @@ void test_transfer()
p.save_path = "./tmp2_transfer";
p.resume_data = &resume_data;
tor2 = ses2.add_torrent(p);
+ ses2.set_alert_mask(alert::all_categories & ~alert::progress_notification);
tor2.prioritize_pieces(priorities);
std::cout << "resetting priorities" << std::endl;
tor2.resume();
@@ -224,8 +274,19 @@ int test_main()
try { remove_all("./tmp1_transfer"); } catch (std::exception&) {}
try { remove_all("./tmp2_transfer"); } catch (std::exception&) {}
+#ifdef NDEBUG
+ // test rate only makes sense in release mode
+ test_rate();
+
+ try { remove_all("./tmp1_transfer"); } catch (std::exception&) {}
+ try { remove_all("./tmp2_transfer"); } catch (std::exception&) {}
+#endif
+
test_transfer();
+ try { remove_all("./tmp1_transfer"); } catch (std::exception&) {}
+ try { remove_all("./tmp2_transfer"); } catch (std::exception&) {}
+
return 0;
}