added torrent_handle::read_piece(). #441

This commit is contained in:
Arvid Norberg 2008-12-14 19:47:02 +00:00
parent 3cb61809ce
commit b47f965c85
8 changed files with 209 additions and 9 deletions

View File

@ -47,6 +47,12 @@ extern char const* udp_error_alert_doc;
extern char const* external_ip_alert_doc; extern char const* external_ip_alert_doc;
extern char const* save_resume_data_alert_doc; extern char const* save_resume_data_alert_doc;
std::string get_buffer(read_piece_alert const& rpa)
{
return rpa.buffer ? std::string(rpa.buffer.get(), rpa.size)
: std::string();
}
void bind_alert() void bind_alert()
{ {
using boost::noncopyable; using boost::noncopyable;
@ -62,6 +68,7 @@ void bind_alert()
.def("__str__", &alert::message, alert_msg_doc) .def("__str__", &alert::message, alert_msg_doc)
; ;
#ifndef TORRENT_NO_DEPRECATE
enum_<alert::severity_t>("severity_levels") enum_<alert::severity_t>("severity_levels")
.value("debug", alert::debug) .value("debug", alert::debug)
.value("info", alert::info) .value("info", alert::info)
@ -70,6 +77,7 @@ void bind_alert()
.value("fatal", alert::fatal) .value("fatal", alert::fatal)
.value("none", alert::none) .value("none", alert::none)
; ;
#endif
enum_<alert::category_t>("category_t") enum_<alert::category_t>("category_t")
.value("error_notification", alert::error_notification) .value("error_notification", alert::error_notification)
@ -99,6 +107,14 @@ void bind_alert()
.def_readonly("url", &tracker_alert::url) .def_readonly("url", &tracker_alert::url)
; ;
class_<read_piece_alert, bases<torrent_alert>, noncopyable>(
"read_piece_alert", 0, no_init
)
.add_property("buffer", get_buffer)
.def_readonly("piece", &read_piece_alert::piece)
.def_readonly("size", &read_piece_alert::size)
;
class_<peer_alert, bases<torrent_alert>, noncopyable>( class_<peer_alert, bases<torrent_alert>, noncopyable>(
"peer_alert", no_init "peer_alert", no_init
) )

View File

@ -211,7 +211,7 @@ The flags paramater can be used to start default features (upnp & nat-pmp) and d
(ut_metadata, ut_pex and smart_ban). The default is to start those things. If you do not want (ut_metadata, ut_pex and smart_ban). The default is to start those things. If you do not want
them to start, pass 0 as the flags parameter. them to start, pass 0 as the flags parameter.
The ``alert_mask`` is the same mask that you would send to ``set_alert_mask``. The ``alert_mask`` is the same mask that you would send to `set_alert_mask()`_.
~session() ~session()
---------- ----------
@ -765,18 +765,29 @@ with a DHT ping packet, and connect to those that responds first. On windows one
can only connect to a few peers at a time because of a built in limitation (in XP can only connect to a few peers at a time because of a built in limitation (in XP
Service pack 2). Service pack 2).
pop_alert() set_alert_mask() wait_for_alert() set_alert_queue_size_limit() set_alert_mask()
-------------------------------------------------------------------------- ----------------
::
void set_alert_mask(int m);
Changes the mask of which alerts to receive. By default only errors are reported.
``m`` is a bitmask where each bit represents a category of alerts.
See alerts_ for mor information on the alert categories.
pop_alert() wait_for_alert() set_alert_queue_size_limit()
---------------------------------------------------------
:: ::
std::auto_ptr<alert> pop_alert(); std::auto_ptr<alert> pop_alert();
alert const* wait_for_alert(time_duration max_wait); alert const* wait_for_alert(time_duration max_wait);
void set_alert_mask(int m);
size_t set_alert_queue_size_limit(size_t queue_size_limit_); size_t set_alert_queue_size_limit(size_t queue_size_limit_);
``pop_alert()`` is used to ask the session if any errors or events has occurred. With ``pop_alert()`` is used to ask the session if any errors or events has occurred. With
``set_alert_mask()`` you can filter which alerts to receive through ``pop_alert()``. `set_alert_mask()`_ you can filter which alerts to receive through ``pop_alert()``.
For information about the alert categories, see alerts_. For information about the alert categories, see alerts_.
``wait_for_alert`` blocks until an alert is available, or for no more than ``max_wait`` ``wait_for_alert`` blocks until an alert is available, or for no more than ``max_wait``
@ -1722,6 +1733,7 @@ Its declaration looks like this::
enum flags_t { overwrite_existing = 1 }; enum flags_t { overwrite_existing = 1 };
void add_piece(int piece, char const* data, int flags = 0) const; void add_piece(int piece, char const* data, int flags = 0) const;
void read_piece(int piece) const;
sha1_hash info_hash() const; sha1_hash info_hash() const;
@ -1907,6 +1919,24 @@ may already have been downloaded with this data.
Since the data is written asynchronously, you may know that is passed or failed the Since the data is written asynchronously, you may know that is passed or failed the
hash check by waiting for ``piece_finished_alert`` or ``has_failed_alert``. hash check by waiting for ``piece_finished_alert`` or ``has_failed_alert``.
read_piece()
------------
::
void read_piece(int piece) const;
This function starts an asynchronous read operation of the specified piece from
this torrent. You must have completed the download of the specified piece before
calling this function.
When the read operation is completed, it is passed back through an alert,
read_piece_alert_. In order to receive this alert, you must enable
``alert::storage_notification`` in your alert mask (see `set_alert_mask()`_).
Note that if you read multiple pieces, the read operations are not guaranteed to
finish in the same order as you initiated them.
force_reannounce() force_reannounce()
------------------ ------------------
@ -4046,7 +4076,7 @@ been posted by libtorrent ``pop_alert()`` will return a default initialized
from the front of the queue is popped and returned. from the front of the queue is popped and returned.
You can then use the alert object and query You can then use the alert object and query
By default, only errors are reported. ``session::set_alert_mask()`` can be By default, only errors are reported. `set_alert_mask()`_ can be
used to specify which kinds of events should be reported. The alert mask used to specify which kinds of events should be reported. The alert mask
is a bitmask with the following bits: is a bitmask with the following bits:
@ -4166,6 +4196,26 @@ There's also a base class for all alerts referring to tracker events::
The specific alerts are: The specific alerts are:
read_piece_alert
----------------
This alert is posted when the asynchronous read operation initiated by
a call to `read_piece()`_ is completed. If the read failed, the torrent
is paused and an error state is set and the buffer member of the alert
is 0. If successful, ``buffer`` points to a buffer containing all the data
of the piece. ``piece`` is the piece index that was read. ``size`` is the
number of bytes that was read.
::
struct read_piece_alert: torrent_alert
{
// ...
boost::shared_ptr<char> buffer;
int piece;
int size;
};
external_ip_alert external_ip_alert
----------------- -----------------

View File

@ -97,6 +97,33 @@ namespace libtorrent
std::string url; std::string url;
}; };
struct TORRENT_EXPORT read_piece_alert: torrent_alert
{
read_piece_alert(torrent_handle const& h
, int p, boost::shared_array<char> d, int s)
: torrent_alert(h)
, buffer(d)
, piece(p)
, size(s)
{}
virtual std::auto_ptr<alert> clone() const
{ return std::auto_ptr<alert>(new read_piece_alert(*this)); }
const static int static_category = alert::storage_notification;
virtual int category() const { return static_category; }
virtual char const* what() const { return "read piece"; }
virtual std::string message() const
{
std::stringstream ret;
ret << torrent_alert::message() << ": piece " << (buffer ? "successful " : "failed ") << piece;
return ret.str();
}
boost::shared_array<char> buffer;
int piece;
int size;
};
struct TORRENT_EXPORT file_renamed_alert: torrent_alert struct TORRENT_EXPORT file_renamed_alert: torrent_alert
{ {
file_renamed_alert(torrent_handle const& h file_renamed_alert(torrent_handle const& h

View File

@ -174,6 +174,15 @@ namespace libtorrent
void on_disk_write_complete(int ret, disk_io_job const& j void on_disk_write_complete(int ret, disk_io_job const& j
, peer_request p); , peer_request p);
struct read_piece_struct
{
boost::shared_array<char> piece_data;
int blocks_left;
bool fail;
};
void read_piece(int piece);
void on_disk_read_complete(int ret, disk_io_job const& j, peer_request r, read_piece_struct* rp);
storage_mode_t storage_mode() const { return m_storage_mode; } storage_mode_t storage_mode() const { return m_storage_mode; }
storage_interface* get_storage() storage_interface* get_storage()
{ {

View File

@ -316,6 +316,7 @@ namespace libtorrent
enum flags_t { overwrite_existing = 1 }; enum flags_t { overwrite_existing = 1 };
void add_piece(int piece, char const* data, int flags = 0) const; void add_piece(int piece, char const* data, int flags = 0) const;
void read_piece(int piece) const;
void get_full_peer_list(std::vector<peer_list_entry>& v) const; void get_full_peer_list(std::vector<peer_list_entry>& v) const;
void get_peer_info(std::vector<peer_info>& v) const; void get_peer_info(std::vector<peer_info>& v) const;

View File

@ -33,7 +33,6 @@ POSSIBILITY OF SUCH DAMAGE.
#include "libtorrent/pch.hpp" #include "libtorrent/pch.hpp"
#include <ctime> #include <ctime>
#include <iostream>
#include <fstream> #include <fstream>
#include <iomanip> #include <iomanip>
#include <iterator> #include <iterator>
@ -42,6 +41,10 @@ POSSIBILITY OF SUCH DAMAGE.
#include <cctype> #include <cctype>
#include <numeric> #include <numeric>
#ifdef TORRENT_DEBUG
#include <iostream>
#endif
#ifdef _MSC_VER #ifdef _MSC_VER
#pragma warning(push, 1) #pragma warning(push, 1)
#endif #endif
@ -376,6 +379,65 @@ namespace libtorrent
disconnect_all(); disconnect_all();
} }
void torrent::read_piece(int piece)
{
TORRENT_ASSERT(piece >= 0 && piece < m_torrent_file->num_pieces());
int piece_size = m_torrent_file->piece_size(piece);
int blocks_in_piece = (piece_size + m_block_size - 1) / m_block_size;
read_piece_struct* rp = new read_piece_struct;
rp->piece_data.reset(new (std::nothrow) char[piece_size]);
rp->blocks_left = 0;
rp->fail = false;
peer_request r;
r.piece = piece;
r.start = 0;
for (int i = 0; i < blocks_in_piece; ++i, r.start += m_block_size)
{
r.length = (std::min)(piece_size - r.start, m_block_size);
filesystem().async_read(r, bind(&torrent::on_disk_read_complete
, shared_from_this(), _1, _2, r, rp));
++rp->blocks_left;
}
}
void torrent::on_disk_read_complete(int ret, disk_io_job const& j, peer_request r, read_piece_struct* rp)
{
session_impl::mutex_t::scoped_lock l(m_ses.m_mutex);
disk_buffer_holder buffer(m_ses, j.buffer);
--rp->blocks_left;
if (ret != r.length)
{
rp->fail = true;
set_error(j.str);
pause();
}
else
{
std::memcpy(rp->piece_data.get() + r.start, j.buffer, r.length);
}
if (rp->blocks_left == 0)
{
int size = m_torrent_file->piece_size(r.piece);
if (rp->fail)
{
rp->piece_data.reset();
size = 0;
}
if (m_ses.m_alerts.should_post<read_piece_alert>())
{
m_ses.m_alerts.post_alert(read_piece_alert(
get_handle(), r.piece, rp->piece_data, size));
}
delete rp;
}
}
void torrent::add_piece(int piece, char const* data, int flags) void torrent::add_piece(int piece, char const* data, int flags)
{ {
TORRENT_ASSERT(piece >= 0 && piece < m_torrent_file->num_pieces()); TORRENT_ASSERT(piece >= 0 && piece < m_torrent_file->num_pieces());
@ -395,7 +457,11 @@ namespace libtorrent
p.length = (std::min)(piece_size - p.start, m_block_size); p.length = (std::min)(piece_size - p.start, m_block_size);
char* buffer = m_ses.allocate_disk_buffer(); char* buffer = m_ses.allocate_disk_buffer();
// out of memory // out of memory
if (buffer == 0) return; if (buffer == 0)
{
picker().dec_refcount(piece);
return;
}
disk_buffer_holder holder(m_ses, buffer); disk_buffer_holder holder(m_ses, buffer);
std::memcpy(buffer, data + p.start, p.length); std::memcpy(buffer, data + p.start, p.length);
filesystem().async_write(p, holder, bind(&torrent::on_disk_write_complete filesystem().async_write(p, holder, bind(&torrent::on_disk_write_complete

View File

@ -541,6 +541,12 @@ namespace libtorrent
TORRENT_FORWARD(add_piece(piece, data, flags)); TORRENT_FORWARD(add_piece(piece, data, flags));
} }
void torrent_handle::read_piece(int piece) const
{
INVARIANT_CHECK;
TORRENT_FORWARD(read_piece(piece));
}
storage_interface* torrent_handle::get_storage_impl() const storage_interface* torrent_handle::get_storage_impl() const
{ {
INVARIANT_CHECK; INVARIANT_CHECK;

View File

@ -2,6 +2,7 @@
#include "libtorrent/session_settings.hpp" #include "libtorrent/session_settings.hpp"
#include "libtorrent/hasher.hpp" #include "libtorrent/hasher.hpp"
#include "libtorrent/create_torrent.hpp" #include "libtorrent/create_torrent.hpp"
#include "libtorrent/alert_types.hpp"
#include <boost/thread.hpp> #include <boost/thread.hpp>
#include <boost/tuple/tuple.hpp> #include <boost/tuple/tuple.hpp>
@ -13,6 +14,7 @@ using namespace libtorrent;
void test_running_torrent(boost::intrusive_ptr<torrent_info> info, size_type file_size) void test_running_torrent(boost::intrusive_ptr<torrent_info> info, size_type file_size)
{ {
session ses(fingerprint("LT", 0, 1, 0, 0), std::make_pair(48130, 48140)); session ses(fingerprint("LT", 0, 1, 0, 0), std::make_pair(48130, 48140));
ses.set_alert_mask(alert::storage_notification);
add_torrent_params p; add_torrent_params p;
p.ti = info; p.ti = info;
@ -62,6 +64,29 @@ void test_running_torrent(boost::intrusive_ptr<torrent_info> info, size_type fil
test_sleep(10000); test_sleep(10000);
st = h.status(); st = h.status();
TEST_CHECK(st.pieces[0] == true); TEST_CHECK(st.pieces[0] == true);
std::cout << "reading piece 0" << std::endl;
h.read_piece(0);
alert const* a = ses.wait_for_alert(seconds(10));
bool passed = false;
while (a)
{
std::auto_ptr<alert> al = ses.pop_alert();
assert(al.get());
std::cout << " " << al->message() << std::endl;
if (read_piece_alert* rpa = dynamic_cast<read_piece_alert*>(al.get()))
{
std::cout << "SUCCEEDED!" << std::endl;
passed = true;
TEST_CHECK(memcmp(&piece[0], rpa->buffer.get(), piece.size()) == 0);
TEST_CHECK(rpa->size == info->piece_size(0));
TEST_CHECK(rpa->piece == 0);
break;
}
a = ses.wait_for_alert(seconds(10));
TEST_CHECK(a);
}
TEST_CHECK(passed);
} }
} }