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* 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()
{
using boost::noncopyable;
@ -62,6 +68,7 @@ void bind_alert()
.def("__str__", &alert::message, alert_msg_doc)
;
#ifndef TORRENT_NO_DEPRECATE
enum_<alert::severity_t>("severity_levels")
.value("debug", alert::debug)
.value("info", alert::info)
@ -70,6 +77,7 @@ void bind_alert()
.value("fatal", alert::fatal)
.value("none", alert::none)
;
#endif
enum_<alert::category_t>("category_t")
.value("error_notification", alert::error_notification)
@ -99,6 +107,14 @@ void bind_alert()
.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>(
"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
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()
----------
@ -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
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();
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_);
``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_.
``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 };
void add_piece(int piece, char const* data, int flags = 0) const;
void read_piece(int piece) 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
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()
------------------
@ -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.
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
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:
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
-----------------

View File

@ -97,6 +97,33 @@ namespace libtorrent
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
{
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
, 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_interface* get_storage()
{

View File

@ -316,6 +316,7 @@ namespace libtorrent
enum flags_t { overwrite_existing = 1 };
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_peer_info(std::vector<peer_info>& v) const;

View File

@ -33,7 +33,6 @@ POSSIBILITY OF SUCH DAMAGE.
#include "libtorrent/pch.hpp"
#include <ctime>
#include <iostream>
#include <fstream>
#include <iomanip>
#include <iterator>
@ -42,6 +41,10 @@ POSSIBILITY OF SUCH DAMAGE.
#include <cctype>
#include <numeric>
#ifdef TORRENT_DEBUG
#include <iostream>
#endif
#ifdef _MSC_VER
#pragma warning(push, 1)
#endif
@ -375,7 +378,66 @@ namespace libtorrent
if (!m_connections.empty())
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)
{
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);
char* buffer = m_ses.allocate_disk_buffer();
// out of memory
if (buffer == 0) return;
if (buffer == 0)
{
picker().dec_refcount(piece);
return;
}
disk_buffer_holder holder(m_ses, buffer);
std::memcpy(buffer, data + p.start, p.length);
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));
}
void torrent_handle::read_piece(int piece) const
{
INVARIANT_CHECK;
TORRENT_FORWARD(read_piece(piece));
}
storage_interface* torrent_handle::get_storage_impl() const
{
INVARIANT_CHECK;

View File

@ -2,6 +2,7 @@
#include "libtorrent/session_settings.hpp"
#include "libtorrent/hasher.hpp"
#include "libtorrent/create_torrent.hpp"
#include "libtorrent/alert_types.hpp"
#include <boost/thread.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)
{
session ses(fingerprint("LT", 0, 1, 0, 0), std::make_pair(48130, 48140));
ses.set_alert_mask(alert::storage_notification);
add_torrent_params p;
p.ti = info;
@ -62,6 +64,29 @@ void test_running_torrent(boost::intrusive_ptr<torrent_info> info, size_type fil
test_sleep(10000);
st = h.status();
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);
}
}