From 8b61436561e8dafa63909e9a887795c17e6e3a5b Mon Sep 17 00:00:00 2001 From: Arvid Norberg Date: Sun, 2 Nov 2003 21:06:50 +0000 Subject: [PATCH] Increased performance. Fixed a bug. Extended torrent_handle interface. --- docs/index.html | 110 +++++++++++++++++++++++--- examples/client_test.cpp | 64 ++++++++++++--- include/libtorrent/config.hpp | 50 ------------ include/libtorrent/peer_id.hpp | 26 +++++- include/libtorrent/piece_picker.hpp | 68 +++++++++++----- include/libtorrent/socket.hpp | 13 ++- include/libtorrent/torrent_handle.hpp | 20 ++++- src/peer_connection.cpp | 13 ++- src/piece_picker.cpp | 63 ++++++++++----- src/policy.cpp | 22 +++++- src/session.cpp | 81 ++++++++++++++++--- src/torrent.cpp | 4 +- src/torrent_handle.cpp | 35 +++++++- 13 files changed, 436 insertions(+), 133 deletions(-) delete mode 100755 include/libtorrent/config.hpp diff --git a/docs/index.html b/docs/index.html index 31bfbeac9..2ce241c0e 100755 --- a/docs/index.html +++ b/docs/index.html @@ -53,6 +53,23 @@ The current state includes the following features:
  • gzipped tracker-responses
  • piece picking on block-level (as opposed to piece-level) like in Azureus +
  • queues torrents for file check, instead of checking all of them in parallel. +
  • 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). + + +

    +Functions that are yet to be implemented: +

    + +

    @@ -153,6 +170,12 @@ want to save the files. The save_path will be prepended to the director structure in the torrent-file.

    +

    +If the torrent you are trying to add already exists in the session (is either queued +for checking, being checked or downloading) add_torrent() will throw +duplicate_torrent which derives from std::exception. +

    +

    fingerprint 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 @@ -453,28 +476,29 @@ Its declaration looks like this: struct torrent_handle { torrent_handle(); - - void get_peer_info(std::vector<peer_info>& v); void abort(); - enum state_t - { - checking_files, - connecting_to_tracker, - downloading, - seeding - }; - torrent_status status() const; + void get_download_queue(std::vector<partial_piece_info>& queue); + void get_peer_info(std::vector<peer_info>& v); }; +

    +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. +

    + +

    abort() 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 shortly after this call has been made.

    +

    status()

    +

    status() will return a structure with information about the status of this torrent. It contains the following fields: @@ -488,7 +512,6 @@ struct torrent_status invalid_handle, queued_for_checking, checking_files, - connecting_to_tracker, downloading, seeding }; @@ -507,6 +530,15 @@ current task is in the state member, it will be one of the following:

    + + + +
    + invalid_handle + + This will be the state if you called status on an uninitialized handle (a + handle that was constructed with the default constructor). +
    queued_for_checking @@ -550,6 +582,52 @@ current task is in the state member, it will be one of the following: uploaded to all peers, accumulated.

    +

    get_download_queue()

    + +

    +get_download_queue() 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 (partial_piece_info) looks like this: +

    + +
    +struct partial_piece_info
    +{
    +	enum { max_blocks_per_piece = implementation-defined };
    +	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];
    +};
    +
    + +

    +piece_index is the index of the piece in question. blocks_in_piece 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. +

    + +

    +requested_blocks 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 peer array. The bit-index +in the requested_blocks and finished_blocks correspons to the array-index into +peers and num_downloads. 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 +requested_blocks is not set) the peer array entry will be undefined. +

    + +

    +The finished_blocks is a bitset where each bit says if the block is fully downloaded +or not. And the num_downloads 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. +

    + +

    get_peer_info()

    +

    get_peer_info() 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 @@ -720,6 +798,16 @@ The sha1-algorithm used was implemented by Steve Reid and released as public dom For more info, see src/sha1.c.

    +

    Feedback

    + +

    +There's a mailing list. +

    + +

    +You can usually find me as hydri in #btports @ irc.freenode.net. +

    +

    Credits

    diff --git a/examples/client_test.cpp b/examples/client_test.cpp index 73f0f16be..8d24534bd 100755 --- a/examples/client_test.cpp +++ b/examples/client_test.cpp @@ -35,12 +35,15 @@ POSSIBILITY OF SUCH DAMAGE. #include #include +#include + #include "libtorrent/entry.hpp" #include "libtorrent/bencode.hpp" #include "libtorrent/session.hpp" #include "libtorrent/http_settings.hpp" #ifdef WIN32 +#include #include bool sleep_and_input(char* c) @@ -54,6 +57,21 @@ bool sleep_and_input(char* c) 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 #include @@ -155,6 +173,7 @@ int main(int argc, char* argv[]) } std::vector peers; + std::vector queue; for (;;) { @@ -164,6 +183,8 @@ int main(int argc, char* argv[]) if (c == 'q') break; } + clear(); + set_cursor(0, 0); for (std::vector::iterator i = handles.begin(); i != handles.end(); ++i) @@ -173,21 +194,19 @@ int main(int argc, char* argv[]) switch(s.state) { case torrent_status::queued_for_checking: - std::cout << "queued for checking: "; + std::cout << "queued "; break; case torrent_status::checking_files: - std::cout << "checking files: "; + std::cout << "checking "; break; case torrent_status::downloading: - std::cout << "downloading: "; + std::cout << "dloading "; break; case torrent_status::seeding: - std::cout << "seeding: "; + std::cout << "seeding "; break; }; - std::cout << s.progress*100 << "% "; - // calculate download and upload speeds i->get_peer_info(peers); float down = 0.f; @@ -205,14 +224,35 @@ int main(int argc, char* argv[]) total_down += i->total_download; total_up += i->total_upload; } - - std::cout << "p:" << num_peers; - - std::cout << " d:(" - << add_suffix(total_down) << ") " << add_suffix(down) << "/s up:(" +/* + std::cout << boost::format("%f%% p:%d d:(%s) %s/s u:(%s) %s/s\n") + % (s.progress*100) + % num_peers + % 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"; + + i->get_download_queue(queue); + for (std::vector::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) diff --git a/include/libtorrent/config.hpp b/include/libtorrent/config.hpp deleted file mode 100755 index 109f02d6f..000000000 --- a/include/libtorrent/config.hpp +++ /dev/null @@ -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 diff --git a/include/libtorrent/peer_id.hpp b/include/libtorrent/peer_id.hpp index 43776dbc2..c9c15a616 100755 --- a/include/libtorrent/peer_id.hpp +++ b/include/libtorrent/peer_id.hpp @@ -33,6 +33,8 @@ POSSIBILITY OF SUCH DAMAGE. #ifndef TORRENT_PEER_ID_HPP_INCLUDED #define TORRENT_PEER_ID_HPP_INCLUDED +#include +#include namespace libtorrent { @@ -63,11 +65,14 @@ namespace libtorrent return false; } - const unsigned char* begin() const { return m_number; } - const unsigned char* end() const { return m_number+number_size; } + typedef const unsigned char* const_iterator; + typedef unsigned char* iterator; - unsigned char* begin() { return m_number; } - unsigned char* end() { return m_number+number_size; } + const_iterator begin() const { return m_number; } + const_iterator end() const { return m_number+number_size; } + + iterator begin() { return m_number; } + iterator end() { return m_number+number_size; } private: @@ -78,6 +83,19 @@ namespace libtorrent typedef big_number peer_id; 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(*i); + } + os << std::dec << std::cout << std::setfill(' '); + return os; + } + } #endif // TORRENT_PEER_ID_HPP_INCLUDED diff --git a/include/libtorrent/piece_picker.hpp b/include/libtorrent/piece_picker.hpp index 7828c1bde..e368b2489 100755 --- a/include/libtorrent/piece_picker.hpp +++ b/include/libtorrent/piece_picker.hpp @@ -37,6 +37,8 @@ POSSIBILITY OF SUCH DAMAGE. #include #include +#include "libtorrent/peer_id.hpp" + namespace libtorrent { @@ -60,6 +62,37 @@ namespace libtorrent 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 requested_blocks; + // the bit is set to one if the block has been acquired + std::bitset 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, int total_num_blocks); @@ -92,7 +125,8 @@ namespace libtorrent // itself, by using the mark_as_downloading() member function. // THIS IS DONE BY THE peer_connection::request_piece() MEMBER FUNCTION! void pick_pieces(const std::vector& pieces, - std::vector& interesting_blocks) const; + std::vector& interesting_blocks, + int num_pieces) const; // returns true if any client is currently downloading this // 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; // 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); // 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); // clears the given piece's download flag @@ -113,9 +147,17 @@ namespace libtorrent 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; + + // the number of downloaded blocks that hasn't passed + // the hash-check yet int unverified_blocks() const; + void get_downloaders(std::vector& d, int index); + const std::vector& get_download_queue() const + { return m_downloads; } + #ifndef NDEBUG // used in debug mode void integrity_check(const torrent* t = 0) const; @@ -147,20 +189,7 @@ namespace libtorrent }; - struct downloading_piece - { - int index; - std::bitset requested_blocks; - std::bitset 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 { @@ -173,9 +202,10 @@ namespace libtorrent void move(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& piece_list, + int add_interesting_blocks(const std::vector& piece_list, const std::vector& pieces, - std::vector& interesting_pieces) const; + std::vector& interesting_pieces, + int num_blocks) const; // this vector contains all pieces we don't have. // in the first entry (index 0) is a vector of all pieces diff --git a/include/libtorrent/socket.hpp b/include/libtorrent/socket.hpp index 56d2039bf..3649e7582 100755 --- a/include/libtorrent/socket.hpp +++ b/include/libtorrent/socket.hpp @@ -238,12 +238,21 @@ namespace libtorrent void monitor_readability(boost::shared_ptr s) { m_readable.push_back(s); } void monitor_writability(boost::shared_ptr s) { m_writable.push_back(s); } void monitor_errors(boost::shared_ptr s) { m_error.push_back(s); } - +/* void clear_readable() { m_readable.clear(); } void clear_writable() { m_writable.clear(); } - +*/ void remove(boost::shared_ptr s); + void remove_writable(boost::shared_ptr s) + { m_writable.erase(std::find(m_writable.begin(), m_writable.end(), s)); } + + bool is_writability_monitored(boost::shared_ptr s) + { + return std::find(m_writable.begin(), m_writable.end(), s) + != m_writable.end(); + } + void wait(int timeout , std::vector >& readable , std::vector >& writable diff --git a/include/libtorrent/torrent_handle.hpp b/include/libtorrent/torrent_handle.hpp index ec6c8ee37..a20e2f7c6 100755 --- a/include/libtorrent/torrent_handle.hpp +++ b/include/libtorrent/torrent_handle.hpp @@ -37,6 +37,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/peer_id.hpp" #include "libtorrent/peer_info.hpp" +#include "libtorrent/piece_picker.hpp" namespace libtorrent @@ -47,6 +48,11 @@ namespace libtorrent struct checker_impl; } + struct duplicate_torrent: std::exception + { + virtual const char* what() const { return "torrent already exists in session"; } + }; + struct torrent_status { enum state_t @@ -54,7 +60,6 @@ namespace libtorrent invalid_handle, queued_for_checking, checking_files, - connecting_to_tracker, downloading, seeding }; @@ -65,6 +70,17 @@ namespace libtorrent 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 requested_blocks; + std::bitset finished_blocks; + peer_id peer[max_blocks_per_piece]; + int num_downloads[max_blocks_per_piece]; + }; + struct torrent_handle { friend class session; @@ -76,6 +92,8 @@ namespace libtorrent torrent_status status() const; + void get_download_queue(std::vector& queue) const; + // TODO: add a 'time to next announce' query. private: diff --git a/src/peer_connection.cpp b/src/peer_connection.cpp index 3db5d4a86..e75926633 100755 --- a/src/peer_connection.cpp +++ b/src/peer_connection.cpp @@ -399,12 +399,21 @@ bool libtorrent::peer_connection::dispatch_message() } else { +#ifndef NDEBUG + std::cout << "hash-test failed. Some of these peers sent invalid data:\n"; + std::vector downloaders; + picker.get_downloaders(downloaders, index); + std::copy(downloaders.begin(), downloaders.end(), std::ostream_iterator(std::cout, "\n")); +#endif // we have to let the piece_picker know that // this piece failed the check as it can restore it // and mark it as being interesting for download // TODO: do this more intelligently! and keep track // of how much crap (data that failed hash-check) and // 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); } 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(!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); @@ -785,6 +794,8 @@ bool libtorrent::peer_connection::has_data() const throw() // throws exception when the client should be disconnected void libtorrent::peer_connection::send_data() { + assert(has_data()); + // only add new piece-chunks if the send buffer is empty // 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) diff --git a/src/piece_picker.cpp b/src/piece_picker.cpp index 4570b34b1..d24e5d7ec 100755 --- a/src/piece_picker.cpp +++ b/src/piece_picker.cpp @@ -360,10 +360,16 @@ namespace libtorrent #endif } - void piece_picker::pick_pieces(const std::vector& pieces, std::vector& interesting_pieces) const + void piece_picker::pick_pieces(const std::vector& pieces, + std::vector& interesting_pieces, + int num_blocks) const { assert(pieces.size() == m_piece_map.size()); +#ifndef NDEBUG + integrity_check(); +#endif + // free refers to pieces that are free to download, noone else // is downloading them. // partial is pieces that are partially being downloaded, and @@ -378,10 +384,8 @@ namespace libtorrent { for (int i = 0; i < 2; ++i) { - if (add_interesting_blocks(*partial, pieces, interesting_pieces)) - { - return; - } + num_blocks = add_interesting_blocks(*partial, pieces, interesting_pieces, num_blocks); + if (num_blocks == 0) return; ++partial; if (partial == m_downloading_piece_info.end()) break; } @@ -389,18 +393,17 @@ namespace libtorrent if (free != m_piece_info.end()) { - if (add_interesting_blocks(*free, pieces, interesting_pieces)) - { - return; - } + num_blocks = add_interesting_blocks(*free, pieces, interesting_pieces, num_blocks); + if (num_blocks == 0) return; ++free; } } } - bool piece_picker::add_interesting_blocks(const std::vector& piece_list, + int piece_picker::add_interesting_blocks(const std::vector& piece_list, const std::vector& pieces, - std::vector& interesting_blocks) const + std::vector& interesting_blocks, + int num_blocks) const { for (std::vector::const_iterator i = piece_list.begin(); @@ -418,15 +421,20 @@ namespace libtorrent if (m_piece_map[*i].downloading == 0) { - interesting_blocks.push_back(piece_block(*i, 0)); - return true; // we have found a piece that's free to download + int piece_blocks = std::min(blocks_in_piece(*i), num_blocks); + 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 // piece. It's always m_blocks_per_piece, except // in the last piece. - int num_blocks_in_piece - = (*i == m_piece_map.size()-1)?m_blocks_in_last_piece:m_blocks_per_piece; + int num_blocks_in_piece = blocks_in_piece(*i); std::vector::const_iterator p = 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)); 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 @@ -471,7 +483,7 @@ namespace libtorrent 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 integrity_check(); @@ -488,6 +500,7 @@ namespace libtorrent downloading_piece dp; dp.index = block.piece_index; dp.requested_blocks[block.block_index] = 1; + dp.info[block.block_index].peer = peer; m_downloads.push_back(dp); } else @@ -496,6 +509,7 @@ namespace libtorrent = std::find_if(m_downloads.begin(), m_downloads.end(), has_index(block.piece_index)); assert(i != m_downloads.end()); assert(i->requested_blocks[block.block_index] == 0); + i->info[block.block_index].peer = peer; i->requested_blocks[block.block_index] = 1; } #ifndef NDEBUG @@ -518,11 +532,24 @@ namespace libtorrent assert(i != m_downloads.end()); assert(i->requested_blocks[block.block_index] == 1); i->finished_blocks[block.block_index] = 1; + i->info[block.block_index].num_downloads++; #ifndef NDEBUG integrity_check(); #endif } + void piece_picker::get_downloaders(std::vector& d, int index) + { + std::vector::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) { diff --git a/src/policy.cpp b/src/policy.cpp index 0b8f36dbd..130646dd5 100755 --- a/src/policy.cpp +++ b/src/policy.cpp @@ -39,13 +39,30 @@ POSSIBILITY OF SUCH DAMAGE. namespace { + enum + { + // we try to maintain 4 requested blocks in the download + // queue + request_queue = 4 + }; + + using namespace libtorrent; void request_a_block(torrent& t, peer_connection& c) { piece_picker& p = t.picker(); std::vector interesting_pieces; 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 // that some other peer is currently downloading @@ -68,7 +85,8 @@ namespace // ok, we found a piece that's not being downloaded // by somebody else. request it from this peer c.request_block(*i); - return; + num_requests++; + if (num_requests >= request_queue) return; } // TODO: compare this peer's bandwidth against the diff --git a/src/session.cpp b/src/session.cpp index 11910a636..ab2fa41d3 100755 --- a/src/session.cpp +++ b/src/session.cpp @@ -182,6 +182,8 @@ namespace libtorrent #if defined(TORRENT_VERBOSE_LOGGING) m_logger = create_log("main session"); #endif + try + { boost::shared_ptr listener(new socket(socket::tcp, false)); int max_port = m_listen_port + 9; @@ -233,6 +235,11 @@ namespace libtorrent #endif for(;;) { +#ifndef NDEBUG + std::clock_t time__ = std::clock(); +#endif + + // if nothing happens within 500000 microseconds (0.5 seconds) // do the loop anyway to check if anything else has changed // (*m_logger) << "sleeping\n"; @@ -243,6 +250,10 @@ namespace libtorrent num_loops++; #endif + + assert(readable_clients.size() + writable_clients.size() + error_clients.size() > 0 + || (std::clock() - time__) > CLOCKS_PER_SEC / 3); + // +1 for the listen socket assert(m_selector.count_read_monitors() == m_connections.size() + 1); @@ -262,6 +273,7 @@ namespace libtorrent break; } + // ************************ // RECEIVE SOCKETS // ************************ @@ -297,7 +309,6 @@ namespace libtorrent continue; } - connection_map::iterator p = m_connections.find(*i); if(p == m_connections.end()) { @@ -321,7 +332,6 @@ namespace libtorrent } - // ************************ // SEND SOCKETS // ************************ @@ -342,8 +352,15 @@ namespace libtorrent { try { + assert(m_selector.is_writability_monitored(p->first)); + assert(p->second->has_data()); // (*m_logger) << "writable: " << p->first->sender().as_string() << "\n"; 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&) { @@ -374,16 +391,27 @@ namespace libtorrent 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 // the ones who still has data to send - m_selector.clear_writable(); +/* m_selector.clear_writable(); // ************************ // 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 // and check if they have pending data to be sent @@ -411,7 +439,7 @@ namespace libtorrent } } } - +*/ // (*m_logger) << "time: " << std::clock()-timer << "\n"; boost::posix_time::time_duration d = boost::posix_time::second_clock::local_time() - timer; if (d.seconds() < 1) continue; @@ -422,18 +450,41 @@ namespace libtorrent // THE SECTION BELOW IS EXECUTED ONCE EVERY SECOND // ************************ + #ifdef TORRENT_DEBUG_SOCKETS std::cout << "\nloops: " << num_loops << "\n"; - assert(loops < 1300); + if (num_loops > 1300) + { + int i = 0; + } num_loops = 0; #endif // do the second_tick() on each connection // 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(); + + 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 @@ -484,6 +535,17 @@ namespace libtorrent t.nsec += 1000000; 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 @@ -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, const std::string& save_path) { @@ -507,9 +570,8 @@ namespace libtorrent boost::mutex::scoped_lock l(m_impl.m_mutex); // is the torrent already active? - // TODO: this should throw 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); // is the torrent currently being checked? - // TODO: This should throw 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 diff --git a/src/torrent.cpp b/src/torrent.cpp index 1b41efec2..109cd76f1 100755 --- a/src/torrent.cpp +++ b/src/torrent.cpp @@ -187,9 +187,9 @@ namespace libtorrent std::cout << std::hex << std::setw(2) << std::setfill('0') << static_cast(*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 diff --git a/src/torrent_handle.cpp b/src/torrent_handle.cpp index b64cf0251..9bc1a25d3 100755 --- a/src/torrent_handle.cpp +++ b/src/torrent_handle.cpp @@ -143,11 +143,44 @@ namespace libtorrent } } + void torrent_handle::get_download_queue(std::vector& 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& q + = p.get_download_queue(); + + for (std::vector::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() { if (m_ses == 0) return; - { boost::mutex::scoped_lock l(m_ses->m_mutex); torrent* t = m_ses->find_torrent(m_info_hash);