added torrent_handle::read_piece(). #441
This commit is contained in:
parent
3cb61809ce
commit
b47f965c85
|
@ -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
|
||||
)
|
||||
|
|
|
@ -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
|
||||
-----------------
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue