Increased performance. Fixed a bug. Extended torrent_handle interface.

This commit is contained in:
Arvid Norberg 2003-11-02 21:06:50 +00:00
parent c9bfb8aa36
commit 8b61436561
13 changed files with 436 additions and 133 deletions

View File

@ -53,6 +53,23 @@ The current state includes the following features:
<li>gzipped tracker-responses <li>gzipped tracker-responses
<li>piece picking on block-level (as opposed to piece-level) like in <li>piece picking on block-level (as opposed to piece-level) like in
<a href="http://azureus.sourceforge.net/">Azureus</a> <a href="http://azureus.sourceforge.net/">Azureus</a>
<li>queues torrents for file check, instead of checking all of them in parallel.
<li>uses separate threads for checking files and for main downloader, with a fool-proof
thread-safe library interface. (i.e. There's no way for the user to cause a deadlock).
</ul>
<p>
Functions that are yet to be implemented:
</p>
<ul>
<li>optimistic unchoke
<li>upload speed cap
<li>Snubbing
<li>end game mode
<li>new allocation model
<li>fast resume
<li>file-level piece priority
</ul> </ul>
<p> <p>
@ -153,6 +170,12 @@ want to save the files. The <tt>save_path</tt> will be prepended to the director
structure in the torrent-file. structure in the torrent-file.
</p> </p>
<p>
If the torrent you are trying to add already exists in the session (is either queued
for checking, being checked or downloading) <tt>add_torrent()</tt> will throw
<tt>duplicate_torrent</tt> which derives from <tt>std::exception</tt>.
</p>
<p> <p>
<tt>fingerprint</tt> is a short string that will be used in the peer_id to <tt>fingerprint</tt> is a short string that will be used in the peer_id to
identify the client. If the string is longer than 7 characters it will identify the client. If the string is longer than 7 characters it will
@ -453,28 +476,29 @@ Its declaration looks like this:
struct torrent_handle struct torrent_handle
{ {
torrent_handle(); torrent_handle();
void get_peer_info(std::vector&lt;peer_info&gt;&amp; v);
void abort(); void abort();
enum state_t
{
checking_files,
connecting_to_tracker,
downloading,
seeding
};
torrent_status status() const; torrent_status status() const;
void get_download_queue(std::vector&lt;partial_piece_info&gt;&amp; queue);
void get_peer_info(std::vector&lt;peer_info&gt;&amp; v);
}; };
</pre> </pre>
<p>
The default constructor will initialize the handle to an invalid state. Which means you cannot
perform any operation on it, unless you first assign it a valid handle. If you try to perform
any operation they will simply return.
</p>
<p> <p>
<tt>abort()</tt> will close all peer connections associated with this torrent and tell <tt>abort()</tt> will close all peer connections associated with this torrent and tell
the tracker that we've stopped participating in the swarm. This handle will become invalid the tracker that we've stopped participating in the swarm. This handle will become invalid
shortly after this call has been made. shortly after this call has been made.
</p> </p>
<h3>status()</h3>
<p> <p>
<tt>status()</tt> will return a structure with information about the status of this <tt>status()</tt> will return a structure with information about the status of this
torrent. It contains the following fields: torrent. It contains the following fields:
@ -488,7 +512,6 @@ struct torrent_status
invalid_handle, invalid_handle,
queued_for_checking, queued_for_checking,
checking_files, checking_files,
connecting_to_tracker,
downloading, downloading,
seeding seeding
}; };
@ -507,6 +530,15 @@ current task is in the <tt>state</tt> member, it will be one of the following:
</p> </p>
<table> <table>
<tr>
<td>
<tt>invalid_handle</tt>
</td>
<td>
This will be the state if you called status on an uninitialized handle (a
handle that was constructed with the default constructor).
</td>
</tr>
<tr> <tr>
<td> <td>
<tt>queued_for_checking</tt> <tt>queued_for_checking</tt>
@ -550,6 +582,52 @@ current task is in the <tt>state</tt> member, it will be one of the following:
uploaded to all peers, accumulated. uploaded to all peers, accumulated.
</p> </p>
<h3>get_download_queue()</h3>
<p>
<tt>get_download_queue()</tt> takes a non-const reference to a vector which it will fill
information about pieces that are partially downloaded or not downloaded at all but partially
requested. The entry in the vector (<tt>partial_piece_info</tt>) looks like this:
</p>
<pre>
struct partial_piece_info
{
enum { max_blocks_per_piece = <i>implementation-defined</i> };
int piece_index;
int blocks_in_piece;
std::bitset&lt;max_blocks_per_piece&gt; requested_blocks;
std::bitset&lt;max_blocks_per_piece&gt; finished_blocks;
peer_id peer[max_blocks_per_piece];
int num_downloads[max_blocks_per_piece];
};
</pre>
<p>
<tt>piece_index</tt> is the index of the piece in question. <tt>blocks_in_piece</tt> is the
number of blocks in this particular piece. This number will be the same for most pieces, but
the last piece may have fewer blocks than the standard pieces.
</p>
<p>
<tt>requested_blocks</tt> is a bitset with one bit per block in the piece. If a bit is set, it
means that that block has been requested, but not necessarily fully downloaded yet. To know
from whom the block has been requested, have a look in the <tt>peer</tt> array. The bit-index
in the <tt>requested_blocks</tt> and <tt>finished_blocks</tt> correspons to the array-index into
<tt>peers</tt> and <tt>num_downloads</tt>. The array of peers is contains the id of the
peer the piece was requested from. If a piece hasn't been requested (the bit in
<tt>requested_blocks</tt> is not set) the peer array entry will be undefined.
</p>
<p>
The <tt>finished_blocks</tt> is a bitset where each bit says if the block is fully downloaded
or not. And the <tt>num_downloads</tt> array says how many times that block has been downloaded.
When a piece fails a hash verification, single blocks may be redownloaded to see if the hash teast
may pass then.
</p>
<h3>get_peer_info()</h3>
<p> <p>
<tt>get_peer_info()</tt> takes a reference to a vector that will be cleared and filled <tt>get_peer_info()</tt> takes a reference to a vector that will be cleared and filled
with one entry for each peer connected to this torrent. Each entry contains information about with one entry for each peer connected to this torrent. Each entry contains information about
@ -720,6 +798,16 @@ The sha1-algorithm used was implemented by Steve Reid and released as public dom
For more info, see <tt>src/sha1.c</tt>. For more info, see <tt>src/sha1.c</tt>.
</p> </p>
<h1>Feedback</h1>
<p>
There's a <a href="http://lists.sourceforge.net/lists/listinfo/libtorrent-discuss">mailing list</a>.
</p>
<p>
You can usually find me as hydri in <tt>#btports @ irc.freenode.net</tt>.
</p>
<h1>Credits</h1> <h1>Credits</h1>
<p> <p>

View File

@ -35,12 +35,15 @@ POSSIBILITY OF SUCH DAMAGE.
#include <iterator> #include <iterator>
#include <exception> #include <exception>
#include <boost/format.hpp>
#include "libtorrent/entry.hpp" #include "libtorrent/entry.hpp"
#include "libtorrent/bencode.hpp" #include "libtorrent/bencode.hpp"
#include "libtorrent/session.hpp" #include "libtorrent/session.hpp"
#include "libtorrent/http_settings.hpp" #include "libtorrent/http_settings.hpp"
#ifdef WIN32 #ifdef WIN32
#include <windows.h>
#include <conio.h> #include <conio.h>
bool sleep_and_input(char* c) bool sleep_and_input(char* c)
@ -54,6 +57,21 @@ bool sleep_and_input(char* c)
return false; return false;
}; };
void set_cursor(int x, int y)
{
HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE);
COORD c = {x, y};
SetConsoleCursorPosition(h, c);
}
void clear()
{
HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE);
COORD c = {0, 0};
DWORD n;
FillConsoleOutputCharacter(h, ' ', 80 * 50, c, &n);
}
#else #else
#include <stdlib.h> #include <stdlib.h>
@ -155,6 +173,7 @@ int main(int argc, char* argv[])
} }
std::vector<peer_info> peers; std::vector<peer_info> peers;
std::vector<partial_piece_info> queue;
for (;;) for (;;)
{ {
@ -164,6 +183,8 @@ int main(int argc, char* argv[])
if (c == 'q') break; if (c == 'q') break;
} }
clear();
set_cursor(0, 0);
for (std::vector<torrent_handle>::iterator i = handles.begin(); for (std::vector<torrent_handle>::iterator i = handles.begin();
i != handles.end(); i != handles.end();
++i) ++i)
@ -173,21 +194,19 @@ int main(int argc, char* argv[])
switch(s.state) switch(s.state)
{ {
case torrent_status::queued_for_checking: case torrent_status::queued_for_checking:
std::cout << "queued for checking: "; std::cout << "queued ";
break; break;
case torrent_status::checking_files: case torrent_status::checking_files:
std::cout << "checking files: "; std::cout << "checking ";
break; break;
case torrent_status::downloading: case torrent_status::downloading:
std::cout << "downloading: "; std::cout << "dloading ";
break; break;
case torrent_status::seeding: case torrent_status::seeding:
std::cout << "seeding: "; std::cout << "seeding ";
break; break;
}; };
std::cout << s.progress*100 << "% ";
// calculate download and upload speeds // calculate download and upload speeds
i->get_peer_info(peers); i->get_peer_info(peers);
float down = 0.f; float down = 0.f;
@ -205,14 +224,35 @@ int main(int argc, char* argv[])
total_down += i->total_download; total_down += i->total_download;
total_up += i->total_upload; total_up += i->total_upload;
} }
/*
std::cout << "p:" << num_peers; std::cout << boost::format("%f%% p:%d d:(%s) %s/s u:(%s) %s/s\n")
% (s.progress*100)
std::cout << " d:(" % num_peers
<< add_suffix(total_down) << ") " << add_suffix(down) << "/s up:(" % add_suffix(total_down)
% add_suffix(down)
% add_suffix(total_up)
% add_suffix(up);
*/
std::cout << (s.progress*100) << "% p:" << num_peers << " d:("
<< add_suffix(total_down) << ") " << add_suffix(down) << "/s u:("
<< add_suffix(total_up) << ") " << add_suffix(up) << "/s\n"; << add_suffix(total_up) << ") " << add_suffix(up) << "/s\n";
i->get_download_queue(queue);
for (std::vector<partial_piece_info>::iterator i = queue.begin();
i != queue.end();
++i)
{
std::cout << i->piece_index << ": ";
for (int j = 0; j < i->blocks_in_piece; ++j)
{
if (i->finished_blocks[j]) std::cout << "#";
else if (i->requested_blocks[j]) std::cout << "-";
else std::cout << ".";
}
std::cout << "\n";
}
std::cout << "___________________________________\n";
} }
std::cout << "----\n";
} }
} }
catch (std::exception& e) catch (std::exception& e)

View File

@ -1,50 +0,0 @@
/*
Copyright (c) 2003, Arvid Norberg
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
* Neither the name of the author nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef TORRENT_CONFIG_HPP_INCLUDED
#define TORRENT_CONFIG_HPP_INCLUDED
#if defined(_WINDOWS)
#if defined(_USRDLL)
#define TORRENT_EXPORT __declspec(dllexport)
#else
#define TORRENT_EXPORT __declspec(dllimport)
#endif
#else
#define TORRENT_EXPORT
#endif
#endif

View File

@ -33,6 +33,8 @@ POSSIBILITY OF SUCH DAMAGE.
#ifndef TORRENT_PEER_ID_HPP_INCLUDED #ifndef TORRENT_PEER_ID_HPP_INCLUDED
#define TORRENT_PEER_ID_HPP_INCLUDED #define TORRENT_PEER_ID_HPP_INCLUDED
#include <iostream>
#include <iomanip>
namespace libtorrent namespace libtorrent
{ {
@ -63,11 +65,14 @@ namespace libtorrent
return false; return false;
} }
const unsigned char* begin() const { return m_number; } typedef const unsigned char* const_iterator;
const unsigned char* end() const { return m_number+number_size; } typedef unsigned char* iterator;
unsigned char* begin() { return m_number; } const_iterator begin() const { return m_number; }
unsigned char* end() { return m_number+number_size; } const_iterator end() const { return m_number+number_size; }
iterator begin() { return m_number; }
iterator end() { return m_number+number_size; }
private: private:
@ -78,6 +83,19 @@ namespace libtorrent
typedef big_number peer_id; typedef big_number peer_id;
typedef big_number sha1_hash; typedef big_number sha1_hash;
inline std::ostream& operator<<(std::ostream& os, const big_number& peer)
{
for (big_number::const_iterator i = peer.begin();
i != peer.end();
++i)
{
os << std::hex << std::setw(2) << std::setfill('0')
<< static_cast<unsigned int>(*i);
}
os << std::dec << std::cout << std::setfill(' ');
return os;
}
} }
#endif // TORRENT_PEER_ID_HPP_INCLUDED #endif // TORRENT_PEER_ID_HPP_INCLUDED

View File

@ -37,6 +37,8 @@ POSSIBILITY OF SUCH DAMAGE.
#include <bitset> #include <bitset>
#include <cassert> #include <cassert>
#include "libtorrent/peer_id.hpp"
namespace libtorrent namespace libtorrent
{ {
@ -60,6 +62,37 @@ namespace libtorrent
enum { max_blocks_per_piece = 128 }; enum { max_blocks_per_piece = 128 };
struct block_info
{
block_info(): num_downloads(0) {}
// the peer this block was requested or
// downloaded from
peer_id peer;
// the number of times this block has been downloaded
int num_downloads;
};
struct downloading_piece
{
int index;
// each bit represents a block in the piece
// set to one if the block has been requested
std::bitset<max_blocks_per_piece> requested_blocks;
// the bit is set to one if the block has been acquired
std::bitset<max_blocks_per_piece> finished_blocks;
// info about each block
block_info info[max_blocks_per_piece];
// TODO: store a hash and a peer_connection reference
// for each block. Then if the hash test fails on the
// piece, redownload one block from another peer
// then the first time, and check the hash again.
// also maintain a counter how many times a piece-hash
// has been confirmed. Download blocks that hasn't
// been confirmed (since they are most probably the
// invalid blocks)
};
piece_picker(int blocks_per_piece, piece_picker(int blocks_per_piece,
int total_num_blocks); int total_num_blocks);
@ -92,7 +125,8 @@ namespace libtorrent
// itself, by using the mark_as_downloading() member function. // itself, by using the mark_as_downloading() member function.
// THIS IS DONE BY THE peer_connection::request_piece() MEMBER FUNCTION! // THIS IS DONE BY THE peer_connection::request_piece() MEMBER FUNCTION!
void pick_pieces(const std::vector<bool>& pieces, void pick_pieces(const std::vector<bool>& pieces,
std::vector<piece_block>& interesting_blocks) const; std::vector<piece_block>& interesting_blocks,
int num_pieces) const;
// returns true if any client is currently downloading this // returns true if any client is currently downloading this
// piece-block, or if it's queued for downloading by some client // piece-block, or if it's queued for downloading by some client
@ -100,11 +134,11 @@ namespace libtorrent
bool is_downloading(piece_block block) const; bool is_downloading(piece_block block) const;
// marks this piece-block as queued for downloading // marks this piece-block as queued for downloading
void mark_as_downloading(piece_block block); void mark_as_downloading(piece_block block, const peer_id& peer);
void mark_as_finished(piece_block block); void mark_as_finished(piece_block block);
// if a piece had a hash-failure, it must be restured and // if a piece had a hash-failure, it must be restured and
// made available fro redownloading // made available for redownloading
void restore_piece(int index); void restore_piece(int index);
// clears the given piece's download flag // clears the given piece's download flag
@ -113,9 +147,17 @@ namespace libtorrent
bool is_piece_finished(int index) const; bool is_piece_finished(int index) const;
// returns the number of blocks there is in the goven piece
int blocks_in_piece(int index) const; int blocks_in_piece(int index) const;
// the number of downloaded blocks that hasn't passed
// the hash-check yet
int unverified_blocks() const; int unverified_blocks() const;
void get_downloaders(std::vector<peer_id>& d, int index);
const std::vector<downloading_piece>& get_download_queue() const
{ return m_downloads; }
#ifndef NDEBUG #ifndef NDEBUG
// used in debug mode // used in debug mode
void integrity_check(const torrent* t = 0) const; void integrity_check(const torrent* t = 0) const;
@ -147,20 +189,7 @@ namespace libtorrent
}; };
struct downloading_piece
{
int index;
std::bitset<max_blocks_per_piece> requested_blocks;
std::bitset<max_blocks_per_piece> finished_blocks;
// TODO: store a hash and a peer_connection reference
// for each block. Then if the hash test fails on the
// piece, redownload one block from another peer
// then the first time, and check the hash again.
// also maintain a counter how many times a piece-hash
// has been confirmed. Download blocks that hasn't
// been confirmed (since they are most probably the
// invalid blocks)
};
struct has_index struct has_index
{ {
@ -173,9 +202,10 @@ namespace libtorrent
void move(bool downloading, int vec_index, int elem_index); void move(bool downloading, int vec_index, int elem_index);
void remove(bool downloading, int vec_index, int elem_index); void remove(bool downloading, int vec_index, int elem_index);
bool add_interesting_blocks(const std::vector<int>& piece_list, int add_interesting_blocks(const std::vector<int>& piece_list,
const std::vector<bool>& pieces, const std::vector<bool>& pieces,
std::vector<piece_block>& interesting_pieces) const; std::vector<piece_block>& interesting_pieces,
int num_blocks) const;
// this vector contains all pieces we don't have. // this vector contains all pieces we don't have.
// in the first entry (index 0) is a vector of all pieces // in the first entry (index 0) is a vector of all pieces

View File

@ -238,12 +238,21 @@ namespace libtorrent
void monitor_readability(boost::shared_ptr<socket> s) { m_readable.push_back(s); } void monitor_readability(boost::shared_ptr<socket> s) { m_readable.push_back(s); }
void monitor_writability(boost::shared_ptr<socket> s) { m_writable.push_back(s); } void monitor_writability(boost::shared_ptr<socket> s) { m_writable.push_back(s); }
void monitor_errors(boost::shared_ptr<socket> s) { m_error.push_back(s); } void monitor_errors(boost::shared_ptr<socket> s) { m_error.push_back(s); }
/*
void clear_readable() { m_readable.clear(); } void clear_readable() { m_readable.clear(); }
void clear_writable() { m_writable.clear(); } void clear_writable() { m_writable.clear(); }
*/
void remove(boost::shared_ptr<socket> s); void remove(boost::shared_ptr<socket> s);
void remove_writable(boost::shared_ptr<socket> s)
{ m_writable.erase(std::find(m_writable.begin(), m_writable.end(), s)); }
bool is_writability_monitored(boost::shared_ptr<socket> s)
{
return std::find(m_writable.begin(), m_writable.end(), s)
!= m_writable.end();
}
void wait(int timeout void wait(int timeout
, std::vector<boost::shared_ptr<socket> >& readable , std::vector<boost::shared_ptr<socket> >& readable
, std::vector<boost::shared_ptr<socket> >& writable , std::vector<boost::shared_ptr<socket> >& writable

View File

@ -37,6 +37,7 @@ POSSIBILITY OF SUCH DAMAGE.
#include "libtorrent/peer_id.hpp" #include "libtorrent/peer_id.hpp"
#include "libtorrent/peer_info.hpp" #include "libtorrent/peer_info.hpp"
#include "libtorrent/piece_picker.hpp"
namespace libtorrent namespace libtorrent
@ -47,6 +48,11 @@ namespace libtorrent
struct checker_impl; struct checker_impl;
} }
struct duplicate_torrent: std::exception
{
virtual const char* what() const { return "torrent already exists in session"; }
};
struct torrent_status struct torrent_status
{ {
enum state_t enum state_t
@ -54,7 +60,6 @@ namespace libtorrent
invalid_handle, invalid_handle,
queued_for_checking, queued_for_checking,
checking_files, checking_files,
connecting_to_tracker,
downloading, downloading,
seeding seeding
}; };
@ -65,6 +70,17 @@ namespace libtorrent
std::size_t total_upload; std::size_t total_upload;
}; };
struct partial_piece_info
{
enum { max_blocks_per_piece = piece_picker::max_blocks_per_piece };
int piece_index;
int blocks_in_piece;
std::bitset<max_blocks_per_piece> requested_blocks;
std::bitset<max_blocks_per_piece> finished_blocks;
peer_id peer[max_blocks_per_piece];
int num_downloads[max_blocks_per_piece];
};
struct torrent_handle struct torrent_handle
{ {
friend class session; friend class session;
@ -76,6 +92,8 @@ namespace libtorrent
torrent_status status() const; torrent_status status() const;
void get_download_queue(std::vector<partial_piece_info>& queue) const;
// TODO: add a 'time to next announce' query. // TODO: add a 'time to next announce' query.
private: private:

View File

@ -399,12 +399,21 @@ bool libtorrent::peer_connection::dispatch_message()
} }
else else
{ {
#ifndef NDEBUG
std::cout << "hash-test failed. Some of these peers sent invalid data:\n";
std::vector<peer_id> downloaders;
picker.get_downloaders(downloaders, index);
std::copy(downloaders.begin(), downloaders.end(), std::ostream_iterator<peer_id>(std::cout, "\n"));
#endif
// we have to let the piece_picker know that // we have to let the piece_picker know that
// this piece failed the check as it can restore it // this piece failed the check as it can restore it
// and mark it as being interesting for download // and mark it as being interesting for download
// TODO: do this more intelligently! and keep track // TODO: do this more intelligently! and keep track
// of how much crap (data that failed hash-check) and // of how much crap (data that failed hash-check) and
// how much redundant data we have downloaded // how much redundant data we have downloaded
// if some clients has sent more than one piece
// start with redownloading the pieces that the client
// that has sent the least number of pieces
picker.restore_piece(index); picker.restore_piece(index);
} }
m_torrent->get_policy().piece_finished(*this, index, verified); m_torrent->get_policy().piece_finished(*this, index, verified);
@ -446,7 +455,7 @@ void libtorrent::peer_connection::request_block(piece_block block)
assert(block.piece_index < m_torrent->torrent_file().num_pieces()); assert(block.piece_index < m_torrent->torrent_file().num_pieces());
assert(!m_torrent->picker().is_downloading(block)); assert(!m_torrent->picker().is_downloading(block));
m_torrent->picker().mark_as_downloading(block); m_torrent->picker().mark_as_downloading(block, m_peer_id);
m_download_queue.push_back(block); m_download_queue.push_back(block);
@ -785,6 +794,8 @@ bool libtorrent::peer_connection::has_data() const throw()
// throws exception when the client should be disconnected // throws exception when the client should be disconnected
void libtorrent::peer_connection::send_data() void libtorrent::peer_connection::send_data()
{ {
assert(has_data());
// only add new piece-chunks if the send buffer is empty // only add new piece-chunks if the send buffer is empty
// otherwise there will be no end to how large it will be! // otherwise there will be no end to how large it will be!
if (!m_requests.empty() && m_send_buffer.empty() && m_peer_interested && !m_choked) if (!m_requests.empty() && m_send_buffer.empty() && m_peer_interested && !m_choked)

View File

@ -360,10 +360,16 @@ namespace libtorrent
#endif #endif
} }
void piece_picker::pick_pieces(const std::vector<bool>& pieces, std::vector<piece_block>& interesting_pieces) const void piece_picker::pick_pieces(const std::vector<bool>& pieces,
std::vector<piece_block>& interesting_pieces,
int num_blocks) const
{ {
assert(pieces.size() == m_piece_map.size()); assert(pieces.size() == m_piece_map.size());
#ifndef NDEBUG
integrity_check();
#endif
// free refers to pieces that are free to download, noone else // free refers to pieces that are free to download, noone else
// is downloading them. // is downloading them.
// partial is pieces that are partially being downloaded, and // partial is pieces that are partially being downloaded, and
@ -378,10 +384,8 @@ namespace libtorrent
{ {
for (int i = 0; i < 2; ++i) for (int i = 0; i < 2; ++i)
{ {
if (add_interesting_blocks(*partial, pieces, interesting_pieces)) num_blocks = add_interesting_blocks(*partial, pieces, interesting_pieces, num_blocks);
{ if (num_blocks == 0) return;
return;
}
++partial; ++partial;
if (partial == m_downloading_piece_info.end()) break; if (partial == m_downloading_piece_info.end()) break;
} }
@ -389,18 +393,17 @@ namespace libtorrent
if (free != m_piece_info.end()) if (free != m_piece_info.end())
{ {
if (add_interesting_blocks(*free, pieces, interesting_pieces)) num_blocks = add_interesting_blocks(*free, pieces, interesting_pieces, num_blocks);
{ if (num_blocks == 0) return;
return;
}
++free; ++free;
} }
} }
} }
bool piece_picker::add_interesting_blocks(const std::vector<int>& piece_list, int piece_picker::add_interesting_blocks(const std::vector<int>& piece_list,
const std::vector<bool>& pieces, const std::vector<bool>& pieces,
std::vector<piece_block>& interesting_blocks) const std::vector<piece_block>& interesting_blocks,
int num_blocks) const
{ {
for (std::vector<int>::const_iterator i = piece_list.begin(); for (std::vector<int>::const_iterator i = piece_list.begin();
@ -418,15 +421,20 @@ namespace libtorrent
if (m_piece_map[*i].downloading == 0) if (m_piece_map[*i].downloading == 0)
{ {
interesting_blocks.push_back(piece_block(*i, 0)); int piece_blocks = std::min(blocks_in_piece(*i), num_blocks);
return true; // we have found a piece that's free to download for (int j = 0; j < piece_blocks; ++j)
{
interesting_blocks.push_back(piece_block(*i, 0));
}
num_blocks -= piece_blocks;
if (num_blocks == 0) return num_blocks;
continue;
} }
// calculate the number of blocks in this // calculate the number of blocks in this
// piece. It's always m_blocks_per_piece, except // piece. It's always m_blocks_per_piece, except
// in the last piece. // in the last piece.
int num_blocks_in_piece int num_blocks_in_piece = blocks_in_piece(*i);
= (*i == m_piece_map.size()-1)?m_blocks_in_last_piece:m_blocks_per_piece;
std::vector<downloading_piece>::const_iterator p std::vector<downloading_piece>::const_iterator p
= std::find_if(m_downloads.begin(), m_downloads.end(), has_index(*i)); = std::find_if(m_downloads.begin(), m_downloads.end(), has_index(*i));
@ -436,10 +444,14 @@ namespace libtorrent
{ {
interesting_blocks.push_back(piece_block(*i, j)); interesting_blocks.push_back(piece_block(*i, j));
if (p->requested_blocks[j] == 0) if (p->requested_blocks[j] == 0)
return true; // we have found a piece that's free to download {
// we have found a piece that's free to download
num_blocks--;
if (num_blocks == 0) return num_blocks;
}
} }
} }
return false; return num_blocks;
} }
bool piece_picker::is_piece_finished(int index) const bool piece_picker::is_piece_finished(int index) const
@ -471,7 +483,7 @@ namespace libtorrent
return i->requested_blocks[block.block_index]; return i->requested_blocks[block.block_index];
} }
void piece_picker::mark_as_downloading(piece_block block) void piece_picker::mark_as_downloading(piece_block block, const peer_id& peer)
{ {
#ifndef NDEBUG #ifndef NDEBUG
integrity_check(); integrity_check();
@ -488,6 +500,7 @@ namespace libtorrent
downloading_piece dp; downloading_piece dp;
dp.index = block.piece_index; dp.index = block.piece_index;
dp.requested_blocks[block.block_index] = 1; dp.requested_blocks[block.block_index] = 1;
dp.info[block.block_index].peer = peer;
m_downloads.push_back(dp); m_downloads.push_back(dp);
} }
else else
@ -496,6 +509,7 @@ namespace libtorrent
= std::find_if(m_downloads.begin(), m_downloads.end(), has_index(block.piece_index)); = std::find_if(m_downloads.begin(), m_downloads.end(), has_index(block.piece_index));
assert(i != m_downloads.end()); assert(i != m_downloads.end());
assert(i->requested_blocks[block.block_index] == 0); assert(i->requested_blocks[block.block_index] == 0);
i->info[block.block_index].peer = peer;
i->requested_blocks[block.block_index] = 1; i->requested_blocks[block.block_index] = 1;
} }
#ifndef NDEBUG #ifndef NDEBUG
@ -518,11 +532,24 @@ namespace libtorrent
assert(i != m_downloads.end()); assert(i != m_downloads.end());
assert(i->requested_blocks[block.block_index] == 1); assert(i->requested_blocks[block.block_index] == 1);
i->finished_blocks[block.block_index] = 1; i->finished_blocks[block.block_index] = 1;
i->info[block.block_index].num_downloads++;
#ifndef NDEBUG #ifndef NDEBUG
integrity_check(); integrity_check();
#endif #endif
} }
void piece_picker::get_downloaders(std::vector<peer_id>& d, int index)
{
std::vector<downloading_piece>::iterator i
= std::find_if(m_downloads.begin(), m_downloads.end(), has_index(index));
assert(i != m_downloads.end());
d.clear();
for (int j = 0; j < blocks_in_piece(index); ++j)
{
d.push_back(i->info[j].peer);
}
}
void piece_picker::abort_download(piece_block block) void piece_picker::abort_download(piece_block block)
{ {

View File

@ -39,13 +39,30 @@ POSSIBILITY OF SUCH DAMAGE.
namespace namespace
{ {
enum
{
// we try to maintain 4 requested blocks in the download
// queue
request_queue = 4
};
using namespace libtorrent; using namespace libtorrent;
void request_a_block(torrent& t, peer_connection& c) void request_a_block(torrent& t, peer_connection& c)
{ {
piece_picker& p = t.picker(); piece_picker& p = t.picker();
std::vector<piece_block> interesting_pieces; std::vector<piece_block> interesting_pieces;
interesting_pieces.reserve(100); interesting_pieces.reserve(100);
p.pick_pieces(c.get_bitfield(), interesting_pieces);
int num_requests = request_queue - c.download_queue().size();
if (num_requests <= 0) num_requests = 1;
// picks the interesting pieces from this peer
// the integer is the number of pieces that
// should be guaranteed to be available for download
// (if this number is too big, too many pieces are
// picked and cpu-time is wasted)
p.pick_pieces(c.get_bitfield(), interesting_pieces, num_requests);
// this vector is filled with the interestin pieces // this vector is filled with the interestin pieces
// that some other peer is currently downloading // that some other peer is currently downloading
@ -68,7 +85,8 @@ namespace
// ok, we found a piece that's not being downloaded // ok, we found a piece that's not being downloaded
// by somebody else. request it from this peer // by somebody else. request it from this peer
c.request_block(*i); c.request_block(*i);
return; num_requests++;
if (num_requests >= request_queue) return;
} }
// TODO: compare this peer's bandwidth against the // TODO: compare this peer's bandwidth against the

View File

@ -182,6 +182,8 @@ namespace libtorrent
#if defined(TORRENT_VERBOSE_LOGGING) #if defined(TORRENT_VERBOSE_LOGGING)
m_logger = create_log("main session"); m_logger = create_log("main session");
#endif #endif
try
{
boost::shared_ptr<socket> listener(new socket(socket::tcp, false)); boost::shared_ptr<socket> listener(new socket(socket::tcp, false));
int max_port = m_listen_port + 9; int max_port = m_listen_port + 9;
@ -233,6 +235,11 @@ namespace libtorrent
#endif #endif
for(;;) for(;;)
{ {
#ifndef NDEBUG
std::clock_t time__ = std::clock();
#endif
// if nothing happens within 500000 microseconds (0.5 seconds) // if nothing happens within 500000 microseconds (0.5 seconds)
// do the loop anyway to check if anything else has changed // do the loop anyway to check if anything else has changed
// (*m_logger) << "sleeping\n"; // (*m_logger) << "sleeping\n";
@ -243,6 +250,10 @@ namespace libtorrent
num_loops++; num_loops++;
#endif #endif
assert(readable_clients.size() + writable_clients.size() + error_clients.size() > 0
|| (std::clock() - time__) > CLOCKS_PER_SEC / 3);
// +1 for the listen socket // +1 for the listen socket
assert(m_selector.count_read_monitors() == m_connections.size() + 1); assert(m_selector.count_read_monitors() == m_connections.size() + 1);
@ -262,6 +273,7 @@ namespace libtorrent
break; break;
} }
// ************************ // ************************
// RECEIVE SOCKETS // RECEIVE SOCKETS
// ************************ // ************************
@ -297,7 +309,6 @@ namespace libtorrent
continue; continue;
} }
connection_map::iterator p = m_connections.find(*i); connection_map::iterator p = m_connections.find(*i);
if(p == m_connections.end()) if(p == m_connections.end())
{ {
@ -321,7 +332,6 @@ namespace libtorrent
} }
// ************************ // ************************
// SEND SOCKETS // SEND SOCKETS
// ************************ // ************************
@ -342,8 +352,15 @@ namespace libtorrent
{ {
try try
{ {
assert(m_selector.is_writability_monitored(p->first));
assert(p->second->has_data());
// (*m_logger) << "writable: " << p->first->sender().as_string() << "\n"; // (*m_logger) << "writable: " << p->first->sender().as_string() << "\n";
p->second->send_data(); p->second->send_data();
// if the peer doesn't have
// any data left to send, remove it
// from the writabilty monitor
if (!p->second->has_data())
m_selector.remove_writable(p->first);
} }
catch(network_error&) catch(network_error&)
{ {
@ -374,16 +391,27 @@ namespace libtorrent
if (p != m_connections.end()) m_connections.erase(p); if (p != m_connections.end()) m_connections.erase(p);
} }
#ifndef NDEBUG
for (connection_map::iterator i = m_connections.begin();
i != m_connections.end();
++i)
{
if (m_selector.is_writability_monitored(i->first))
assert(i->second->has_data());
}
#endif
// clear all writablility monitors and add // clear all writablility monitors and add
// the ones who still has data to send // the ones who still has data to send
m_selector.clear_writable(); /* m_selector.clear_writable();
// ************************ // ************************
// BUILD WRITER LIST // BUILD WRITER LIST
// ************************ // ************************
// TODO: REWRITE THIS! DON'T GO THROUGH THIS LOOP EVERY TIME!
// loop over all clients and purge the ones that has timed out // loop over all clients and purge the ones that has timed out
// and check if they have pending data to be sent // and check if they have pending data to be sent
@ -411,7 +439,7 @@ namespace libtorrent
} }
} }
} }
*/
// (*m_logger) << "time: " << std::clock()-timer << "\n"; // (*m_logger) << "time: " << std::clock()-timer << "\n";
boost::posix_time::time_duration d = boost::posix_time::second_clock::local_time() - timer; boost::posix_time::time_duration d = boost::posix_time::second_clock::local_time() - timer;
if (d.seconds() < 1) continue; if (d.seconds() < 1) continue;
@ -422,18 +450,41 @@ namespace libtorrent
// THE SECTION BELOW IS EXECUTED ONCE EVERY SECOND // THE SECTION BELOW IS EXECUTED ONCE EVERY SECOND
// ************************ // ************************
#ifdef TORRENT_DEBUG_SOCKETS #ifdef TORRENT_DEBUG_SOCKETS
std::cout << "\nloops: " << num_loops << "\n"; std::cout << "\nloops: " << num_loops << "\n";
assert(loops < 1300); if (num_loops > 1300)
{
int i = 0;
}
num_loops = 0; num_loops = 0;
#endif #endif
// do the second_tick() on each connection // do the second_tick() on each connection
// this will update their statistics (download and upload speeds) // this will update their statistics (download and upload speeds)
for (connection_map::iterator i = m_connections.begin(); i != m_connections.end(); ++i) // also purge sockets that have timed out
// and keep sockets open by keeping them alive.
for (connection_map::iterator i = m_connections.begin();
i != m_connections.end();)
{ {
i->second->second_tick(); i->second->second_tick();
connection_map::iterator j = i;
++i;
// if this socket has timed out
// close it.
if (j->second->has_timed_out())
{
m_selector.remove(j->first);
m_connections.erase(j);
continue;
}
j->second->keep_alive();
if (j->second->has_data() && !m_selector.is_writability_monitored(j->first))
m_selector.monitor_writability(j->first);
} }
// check each torrent for abortion or // check each torrent for abortion or
@ -484,6 +535,17 @@ namespace libtorrent
t.nsec += 1000000; t.nsec += 1000000;
boost::thread::sleep(t); boost::thread::sleep(t);
} }
}
catch(const std::exception& e)
{
std::cout << e.what() << "\n";
}
catch(...)
{
std::cout << "error\n";
}
} }
// the return value from this function is valid only as long as the // the return value from this function is valid only as long as the
@ -498,6 +560,7 @@ namespace libtorrent
} }
// if the torrent already exists, this will throw duplicate_torrent
torrent_handle session::add_torrent(const torrent_info& ti, torrent_handle session::add_torrent(const torrent_info& ti,
const std::string& save_path) const std::string& save_path)
{ {
@ -507,9 +570,8 @@ namespace libtorrent
boost::mutex::scoped_lock l(m_impl.m_mutex); boost::mutex::scoped_lock l(m_impl.m_mutex);
// is the torrent already active? // is the torrent already active?
// TODO: this should throw
if (m_impl.find_torrent(ti.info_hash())) if (m_impl.find_torrent(ti.info_hash()))
return torrent_handle(&m_impl, &m_checker_impl, ti.info_hash()); throw duplicate_torrent();
} }
{ {
@ -517,9 +579,8 @@ namespace libtorrent
boost::mutex::scoped_lock l(m_checker_impl.m_mutex); boost::mutex::scoped_lock l(m_checker_impl.m_mutex);
// is the torrent currently being checked? // is the torrent currently being checked?
// TODO: This should throw
if (m_checker_impl.find_torrent(ti.info_hash())) if (m_checker_impl.find_torrent(ti.info_hash()))
return torrent_handle(&m_impl, &m_checker_impl, ti.info_hash()); throw duplicate_torrent();
} }
// create the torrent and the data associated with // create the torrent and the data associated with

View File

@ -187,9 +187,9 @@ namespace libtorrent
std::cout << std::hex << std::setw(2) << std::setfill('0') std::cout << std::hex << std::setw(2) << std::setfill('0')
<< static_cast<unsigned int>(*j); << static_cast<unsigned int>(*j);
} }
std::cout << " " << extract_fingerprint(i->id) << "\n"; std::cout << std::dec << " " << extract_fingerprint(i->id) << "\n";
} }
std::cout << std::dec << std::setfill(' '); std::cout << std::setfill(' ');
// for each of the peers we got from the tracker // for each of the peers we got from the tracker

View File

@ -143,11 +143,44 @@ namespace libtorrent
} }
} }
void torrent_handle::get_download_queue(std::vector<partial_piece_info>& queue) const
{
queue.clear();
if (m_ses == 0) return;
boost::mutex::scoped_lock l(m_ses->m_mutex);
torrent* t = m_ses->find_torrent(m_info_hash);
if (t == 0) return;
const piece_picker& p = t->picker();
const std::vector<piece_picker::downloading_piece>& q
= p.get_download_queue();
for (std::vector<piece_picker::downloading_piece>::const_iterator i
= q.begin();
i != q.end();
++i)
{
partial_piece_info pi;
pi.finished_blocks = i->finished_blocks;
pi.requested_blocks = i->requested_blocks;
for (int j = 0; j < partial_piece_info::max_blocks_per_piece; ++j)
{
pi.peer[j] = i->info[j].peer;
pi.num_downloads[j] = i->info[j].num_downloads;
}
pi.piece_index = i->index;
pi.blocks_in_piece = p.blocks_in_piece(i->index);
queue.push_back(pi);
}
}
void torrent_handle::abort() void torrent_handle::abort()
{ {
if (m_ses == 0) return; if (m_ses == 0) return;
{ {
boost::mutex::scoped_lock l(m_ses->m_mutex); boost::mutex::scoped_lock l(m_ses->m_mutex);
torrent* t = m_ses->find_torrent(m_info_hash); torrent* t = m_ses->find_torrent(m_info_hash);