diff --git a/docs/manual.html b/docs/manual.html index 0987cfb2f..da57ca8db 100755 --- a/docs/manual.html +++ b/docs/manual.html @@ -1305,10 +1305,8 @@ tells which piece is on which slot. If piece index is -2 it means it is free, that there's no piece there. If it is -1, means the slot isn't allocated on disk yet. The pieces have to meet the following requirement:

- +

If there's a slot at the position of the piece index, +the piece must be located in that slot.

peers diff --git a/docs/manual.rst b/docs/manual.rst index e2a104283..7c7d122a8 100755 --- a/docs/manual.rst +++ b/docs/manual.rst @@ -1104,6 +1104,29 @@ as severity level ``debug``. }; +invalid_request_alert +--------------------- + +Thie is a debug alert that is generated by an incoming invalid piece request. + +:: + + struct invalid_request_alert: alert + { + invalid_request_alert( + const peer_request& r + , const torrent_handle& h + , const peer_id& send + , const std::string& msg); + + virtual std::auto_ptr clone() const; + + torrent_handle handle; + peer_id sender; + peer_request request; + }; + + chat_message_alert ------------------ @@ -1372,8 +1395,8 @@ The file format is a bencoded dictionary containing the following fields: | | means the slot isn't allocated on disk yet. The pieces have | | | to meet the following requirement: | | | | -| | * if there's a slot at the position of the piece index, | -| | the piece must be located in that slot. | +| | If there's a slot at the position of the piece index, | +| | the piece must be located in that slot. | +----------------------+--------------------------------------------------------------+ | ``peers`` | list of dictionaries. Each dictionary has the following | | | layout: | @@ -1398,6 +1421,7 @@ The file format is a bencoded dictionary containing the following fields: | | | | blocks that have been downloaded in this | | | | | | piece. | | | | +-------------+--------------------------------------------+ | +| | | +----------------------+--------------------------------------------------------------+ diff --git a/examples/client_test.cpp b/examples/client_test.cpp index c84ab5443..a01dded64 100755 --- a/examples/client_test.cpp +++ b/examples/client_test.cpp @@ -37,6 +37,7 @@ POSSIBILITY OF SUCH DAMAGE. #include #include +#include #include "libtorrent/entry.hpp" #include "libtorrent/bencode.hpp" @@ -186,9 +187,9 @@ std::string add_suffix(float val) int i; for (i = 0; i < num_prefix; ++i) { - if (fabs(val) < 1024.f) + if (fabs(val) < 1000.f) return to_string(val, i==0?7:6) + prefix[i]; - val /= 1024.f; + val /= 1000.f; } return to_string(val, 6) + prefix[i]; } @@ -233,11 +234,13 @@ int main(int argc, char* argv[]) torrent_info t(e); t.print(std::cout); + boost::filesystem::path save_path(""); entry resume_data; try { - // TODO: use a torrent-specific name here - std::ifstream resume_file("test.fastresume", std::ios_base::binary); + std::stringstream s; + s << t.name() << ".fastresume"; + boost::filesystem::ifstream resume_file(save_path / s.str(), std::ios_base::binary); resume_file.unsetf(std::ios_base::skipws); resume_data = bdecode(std::istream_iterator(resume_file) , std::istream_iterator()); @@ -245,7 +248,7 @@ int main(int argc, char* argv[]) catch (invalid_encoding&) {} - handles.push_back(ses.add_torrent(t, "", resume_data)); + handles.push_back(ses.add_torrent(t, save_path, resume_data)); handles.back().set_max_uploads(7); handles.back().set_ratio(1); } @@ -265,11 +268,18 @@ int main(int argc, char* argv[]) { if (c == 'q') { - entry data = handles.front().write_resume_data(); - // TODO: use a torrent-specific name here - std::ofstream out("test.fastresume", std::ios_base::binary); - out.unsetf(std::ios_base::skipws); - bencode(std::ostream_iterator(out), data); + for (std::vector::iterator i = handles.begin(); + i != handles.end(); + ++i) + { + torrent_handle h = *i; + entry data = h.write_resume_data(); + std::stringstream s; + s << h.get_torrent_info().name() << ".fastresume"; + boost::filesystem::ofstream out(h.save_path() / s.str(), std::ios_base::binary); + out.unsetf(std::ios_base::skipws); + bencode(std::ostream_iterator(out), data); + } break; } } diff --git a/include/libtorrent/peer_connection.hpp b/include/libtorrent/peer_connection.hpp index 8912ed608..92cc3401d 100755 --- a/include/libtorrent/peer_connection.hpp +++ b/include/libtorrent/peer_connection.hpp @@ -168,7 +168,6 @@ namespace libtorrent peer_id sender; }; - // TODO: document struct invalid_request_alert: alert { invalid_request_alert( diff --git a/include/libtorrent/piece_picker.hpp b/include/libtorrent/piece_picker.hpp index fef150c2a..b49c72147 100755 --- a/include/libtorrent/piece_picker.hpp +++ b/include/libtorrent/piece_picker.hpp @@ -37,6 +37,8 @@ POSSIBILITY OF SUCH DAMAGE. #include #include +#include + #include "libtorrent/peer_id.hpp" #include "libtorrent/socket.hpp" @@ -166,10 +168,13 @@ namespace libtorrent // the hash-check yet int unverified_blocks() const; - void get_downloaders(std::vector
& d, int index); + void get_downloaders(std::vector
& d, int index) const; + const std::vector& get_download_queue() const { return m_downloads; } + boost::optional
get_downloader(piece_block block) const; + #ifndef NDEBUG // used in debug mode void integrity_check(const torrent* t = 0) const; diff --git a/include/libtorrent/storage.hpp b/include/libtorrent/storage.hpp index 035fe2013..9680c3423 100755 --- a/include/libtorrent/storage.hpp +++ b/include/libtorrent/storage.hpp @@ -55,7 +55,7 @@ namespace libtorrent { file_allocation_failed(const char* error_msg): m_msg(error_msg) {} virtual const char* what() const throw() { return m_msg.c_str(); } - virtual ~file_allocation_failed() {} + virtual ~file_allocation_failed() throw() {} std::string m_msg; }; diff --git a/include/libtorrent/torrent.hpp b/include/libtorrent/torrent.hpp index 3a589ac3d..12060a01f 100755 --- a/include/libtorrent/torrent.hpp +++ b/include/libtorrent/torrent.hpp @@ -173,6 +173,13 @@ namespace libtorrent // decreased in the piece_picker void remove_peer(peer_connection* p); + peer_connection* connection_for(const address& a) + { + peer_iterator i = m_connections.find(a); + if (i == m_connections.end()) return 0; + return i->second; + } + // the number of peers that belong to this torrent int num_peers() const { return m_connections.size(); } @@ -180,11 +187,11 @@ namespace libtorrent // to a peer with the given peer_id bool has_peer(const peer_id& id) const; - typedef std::vector::iterator peer_iterator; - typedef std::vector::const_iterator peer_const_iterator; + typedef std::map::iterator peer_iterator; + typedef std::map::const_iterator const_peer_iterator; - peer_const_iterator begin() const { return m_connections.begin(); } - peer_const_iterator end() const { return m_connections.end(); } + const_peer_iterator begin() const { return m_connections.begin(); } + const_peer_iterator end() const { return m_connections.end(); } peer_iterator begin() { return m_connections.begin(); } peer_iterator end() { return m_connections.end(); } @@ -311,7 +318,10 @@ namespace libtorrent // from the tracker int m_duration; - std::vector m_connections; + // TODO: this should be a map, mapping address + // to peer_connection* + std::map m_connections; +// std::vector m_connections; // this is the upload and download statistics for the whole torrent. // it's updated from all its peers once every second. diff --git a/src/peer_connection.cpp b/src/peer_connection.cpp index 7572d5690..c9995a560 100755 --- a/src/peer_connection.cpp +++ b/src/peer_connection.cpp @@ -33,7 +33,6 @@ POSSIBILITY OF SUCH DAMAGE. #include #include #include - #include #include "libtorrent/peer_connection.hpp" @@ -620,8 +619,8 @@ namespace libtorrent && i->block_index == p.start / m_torrent->block_size()) break; - (*m_logger) << " <== SKIPPED_PIECE [ piece: " << i->piece_index << " | " - "b: " << i->block_index << " ]\n"; + (*m_logger) << " *** SKIPPED_PIECE [ piece: " << i->piece_index << " | " + "b: " << i->block_index << " ] ***\n"; if (m_torrent->alerts().should_post(alert::debug)) { std::stringstream s; @@ -640,6 +639,9 @@ namespace libtorrent piece_picker& picker = m_torrent->picker(); piece_block block_finished(p.piece, p.start / m_torrent->block_size()); + // if the block we got is already finished, then ignore it + if (picker.is_finished(block_finished)) return; + std::deque::iterator b = std::find( m_download_queue.begin() @@ -654,13 +656,32 @@ namespace libtorrent } else { - // TODO: cancel the block from the + // cancel the block from the // peer that has taken over it. + boost::optional
peer = m_torrent->picker().get_downloader(block_finished); + if (peer) + { + peer_connection* pc = m_torrent->connection_for(*peer); + if (pc && pc != this) + { + pc->send_cancel(block_finished); + } + } + else + { + if (m_torrent->alerts().should_post(alert::debug)) + { + m_torrent->alerts().post_alert( + peer_error_alert( + m_peer_id + , "got a block that was not requested")); + } +#ifndef NDEBUG + (*m_logger) << " *** The block we just got was not requested ***\n"; +#endif + } } - // if the block we got is already finished, then ignore it - if (picker.is_finished(block_finished)) return; - m_torrent->filesystem().write(&m_recv_buffer[9], p.piece, p.start, p.length); picker.mark_as_finished(block_finished, m_socket->sender()); @@ -1026,7 +1047,6 @@ namespace libtorrent send_buffer_updated(); } - // TODO: rename to send_choke? void peer_connection::send_choke() { if (m_choked) return; diff --git a/src/piece_picker.cpp b/src/piece_picker.cpp index 8662a8e5f..f1a4174c1 100755 --- a/src/piece_picker.cpp +++ b/src/piece_picker.cpp @@ -152,11 +152,11 @@ namespace libtorrent if (t != 0) { int actual_peer_count = 0; - for (std::vector::const_iterator peer = t->begin(); + for (torrent::const_peer_iterator peer = t->begin(); peer != t->end(); ++peer) { - if ((*peer)->has_piece(index)) actual_peer_count++; + if (peer->second->has_piece(index)) actual_peer_count++; } assert(i->peer_count == actual_peer_count); @@ -518,7 +518,11 @@ namespace libtorrent if (m_piece_map[block.piece_index].downloading == 0) return false; std::vector::const_iterator i - = 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()); return i->requested_blocks[block.block_index]; } @@ -632,9 +636,9 @@ namespace libtorrent #endif } */ - void piece_picker::get_downloaders(std::vector
& d, int index) + void piece_picker::get_downloaders(std::vector
& d, int index) const { - std::vector::iterator i + std::vector::const_iterator i = std::find_if(m_downloads.begin(), m_downloads.end(), has_index(index)); assert(i != m_downloads.end()); @@ -645,6 +649,26 @@ namespace libtorrent } } + boost::optional
piece_picker::get_downloader(piece_block block) const + { + std::vector::const_iterator i = std::find_if( + m_downloads.begin() + , m_downloads.end() + , has_index(block.piece_index)); + + if (i == m_downloads.end()) + return boost::optional
(); + + assert(block.block_index < max_blocks_per_piece); + assert(block.block_index >= 0); + + if (i->requested_blocks[block.block_index] == false + || i->finished_blocks[block.block_index] == true) + return boost::optional
(); + + return boost::optional
(i->info[block.block_index].peer); + } + void piece_picker::abort_download(piece_block block) { #ifndef NDEBUG diff --git a/src/policy.cpp b/src/policy.cpp index 87589a959..4e15e156e 100755 --- a/src/policy.cpp +++ b/src/policy.cpp @@ -168,15 +168,15 @@ namespace i != t.end(); ++i) { - const std::deque& queue = (*i)->download_queue(); - if ((*i)->statistics().down_peak() > down_speed + const std::deque& queue = i->second->download_queue(); + if (i->second->statistics().down_peak() > down_speed && has_intersection(busy_pieces.begin(), busy_pieces.end(), queue.begin(), queue.end())) { - peer = *i; - down_speed = (*i)->statistics().down_peak(); + peer = i->second; + down_speed = peer->statistics().down_peak(); } } @@ -209,12 +209,12 @@ namespace // want to trade it's surplus uploads for downloads itself // (and we should consider it free). If the share diff is // negative, there's no free download to get from this peer. - int diff = (*i)->share_diff(); - if ((*i)->is_peer_interested() || diff <= 0) + int diff = i->second->share_diff(); + if (i->second->is_peer_interested() || diff <= 0) continue; assert(diff > 0); - (*i)->add_free_upload(-diff); + i->second->add_free_upload(-diff); accumulator += diff; assert(accumulator > 0); } @@ -235,8 +235,8 @@ namespace int total_diff = 0; for (torrent::peer_iterator i = start; i != end; ++i) { - total_diff += (*i)->share_diff(); - if (!(*i)->is_peer_interested() || (*i)->share_diff() >= 0) continue; + total_diff += i->second->share_diff(); + if (!i->second->is_peer_interested() || i->second->share_diff() >= 0) continue; ++num_peers; } @@ -254,8 +254,9 @@ namespace for (torrent::peer_iterator i = start; i != end; ++i) { - if (!(*i)->is_peer_interested() || (*i)->share_diff() >= 0) continue; - (*i)->add_free_upload(upload_share); + peer_connection* p = i->second; + if (!p->is_peer_interested() || p->share_diff() >= 0) continue; + p->add_free_upload(upload_share); free_upload -= upload_share; } return free_upload; diff --git a/src/storage.cpp b/src/storage.cpp index 17914efc4..d103dd291 100755 --- a/src/storage.cpp +++ b/src/storage.cpp @@ -43,7 +43,6 @@ POSSIBILITY OF SUCH DAMAGE. #include #include #include -#include #include #include "libtorrent/storage.hpp" @@ -437,6 +436,8 @@ namespace libtorrent { boost::recursive_mutex::scoped_lock lock(m_mutex); // ---------------------------------------------------------------------- + check_invariant(); + p.clear(); std::vector::const_reverse_iterator last; for (last = m_slot_to_piece.rbegin(); @@ -453,6 +454,9 @@ namespace libtorrent { { p.push_back(*i); } + + check_invariant(); + } void piece_manager::export_piece_map( @@ -462,22 +466,30 @@ namespace libtorrent { } // TODO: daniel, make sure this function does everything it needs to do - void piece_manager::impl::mark_failed(int index) + void piece_manager::impl::mark_failed(int piece_index) { // synchronization ------------------------------------------------------ boost::recursive_mutex::scoped_lock lock(m_mutex); // ---------------------------------------------------------------------- - assert(index >= 0 && index < m_piece_to_slot.size()); - assert(m_piece_to_slot[index] >= 0); +#ifndef NDEBUG + check_invariant(); +#endif + assert(piece_index >= 0 && piece_index < m_piece_to_slot.size()); + assert(m_piece_to_slot[piece_index] >= 0); - int slot = m_slot_to_piece[m_piece_to_slot[index]]; + int slot_index = m_piece_to_slot[piece_index]; - assert(slot >= 0); + assert(slot_index >= 0); + + m_slot_to_piece[slot_index] = -2; + m_piece_to_slot[piece_index] = -1; + m_free_slots.push_back(slot_index); + +#ifndef NDEBUG + check_invariant(); +#endif - m_slot_to_piece[m_piece_to_slot[index]] = -2; - m_piece_to_slot[index] = -1; - m_free_slots.push_back(slot); } void piece_manager::mark_failed(int index) @@ -809,6 +821,9 @@ namespace libtorrent { { assert(slot_index >= 0); assert(slot_index < m_slot_to_piece.size()); + + check_invariant(); + return slot_index; } diff --git a/src/torrent.cpp b/src/torrent.cpp index d39647c6a..86fe8ad49 100755 --- a/src/torrent.cpp +++ b/src/torrent.cpp @@ -167,16 +167,6 @@ namespace return true; } - bool operator()(const peer_connection* p) const - { - if (p->get_peer_id() != id) return false; - if (tor != p->associated_torrent()) return false; - // have a special case for all zeros. We can have any number - // of peers with that id, since it's used to indicate no id. - if (std::count(id.begin(), id.end(), 0) == 20) return false; - return true; - } - const peer_id& id; const torrent* tor; }; @@ -196,6 +186,23 @@ namespace const torrent* tor; }; + + struct peer_by_id + { + peer_by_id(const peer_id& i): id(i) {} + + bool operator()(const std::pair& p) const + { + if (p.second->get_peer_id() != id) return false; + // have a special case for all zeros. We can have any number + // of peers with that id, since it's used to indicate no id. + if (std::count(id.begin(), id.end(), 0) == 20) return false; + return true; + } + + const peer_id& id; + }; + } namespace libtorrent @@ -313,7 +320,7 @@ namespace libtorrent { assert(std::count_if(m_connections.begin() , m_connections.end() - , find_peer_by_id(id, this)) <= 1); + , peer_by_id(id)) <= 1); // pretend that we are connected to // ourself to avoid real connections @@ -323,7 +330,7 @@ namespace libtorrent return std::find_if( m_connections.begin() , m_connections.end() - , find_peer_by_id(id, this)) + , peer_by_id(id)) != m_connections.end(); } @@ -360,20 +367,19 @@ namespace libtorrent // decrease the trust point of all peers that sent // parts of this piece. - // TODO: implement this loop more efficient - for (std::vector::iterator i = m_connections.begin(); - i != m_connections.end(); + for (std::vector
::iterator i = downloaders.begin(); + i != downloaders.end(); ++i) { - if (std::find(downloaders.begin(), downloaders.end(), (*i)->get_socket()->sender()) - == downloaders.end()) continue; + peer_iterator p = m_connections.find(*i); + if (p == m_connections.end()) continue; + p->second->received_invalid_data(); - (*i)->received_invalid_data(); - if ((*i)->trust_points() <= -5) + if (p->second->trust_points() <= -5) { // we don't trust this peer anymore // ban it. - m_policy->ban_peer(*(*i)); + m_policy->ban_peer(*p->second); } } @@ -401,22 +407,20 @@ namespace libtorrent // increase the trust point of all peers that sent // parts of this piece. - // TODO: implement this loop more efficient - for (std::vector::iterator i = m_connections.begin(); - i != m_connections.end(); + for (std::vector
::iterator i = downloaders.begin(); + i != downloaders.end(); ++i) { - if (std::find(downloaders.begin(), downloaders.end(), (*i)->get_socket()->sender()) - != downloaders.end()) - { - (*i)->received_valid_data(); - } + peer_iterator p = m_connections.find(*i); + if (p == m_connections.end()) continue; + p->second->received_valid_data(); } - m_picker.we_have(index); - for (std::vector::iterator i = m_connections.begin(); i != m_connections.end(); ++i) - (*i)->announce_piece(index); + for (peer_iterator i = m_connections.begin(); i != m_connections.end(); ++i) + i->second->announce_piece(index); + + // TODO: if we became a seed, disconnect from all other seeds } std::string torrent::generate_tracker_request(int port) @@ -489,7 +493,7 @@ namespace libtorrent void torrent::remove_peer(peer_connection* p) { - std::vector::iterator i = std::find(m_connections.begin(), m_connections.end(), p); + peer_iterator i = m_connections.find(p->get_socket()->sender()); assert(i != m_connections.end()); // if the peer_connection was downloading any pieces @@ -540,18 +544,20 @@ namespace libtorrent , this , s , id)); + if (m_ses.m_upload_rate != -1) c->set_send_quota(0); + detail::session_impl::connection_map::iterator p = m_ses.m_connections.insert(std::make_pair(s, c)).first; // add the newly connected peer to this torrent's peer list - assert(std::find( - m_connections.begin() - , m_connections.end() - , boost::get_pointer(p->second)) + assert(m_connections.find(p->second->get_socket()->sender()) == m_connections.end()); - m_connections.push_back(boost::get_pointer(p->second)); + m_connections.insert( + std::make_pair( + p->second->get_socket()->sender() + , boost::get_pointer(p->second))); m_ses.m_selector.monitor_readability(s); m_ses.m_selector.monitor_errors(s); @@ -561,8 +567,10 @@ namespace libtorrent void torrent::attach_peer(peer_connection* p) { - assert(std::find(m_connections.begin(), m_connections.end(), p) == m_connections.end()); - m_connections.push_back(p); + assert(m_connections.find(p->get_socket()->sender()) == m_connections.end()); + + m_connections.insert(std::make_pair(p->get_socket()->sender(), p)); + detail::session_impl::connection_map::iterator i = m_ses.m_connections.find(p->get_socket()); assert(i != m_ses.m_connections.end()); @@ -572,29 +580,25 @@ namespace libtorrent void torrent::close_all_connections() { - for (detail::session_impl::connection_map::iterator i = m_ses.m_connections.begin(); - i != m_ses.m_connections.end();) + for (peer_iterator i = m_connections.begin(); + i != m_connections.end();) { - if (i->second->associated_torrent() == this) - { - #ifndef NDEBUG - std::size_t num_connections = m_connections.size(); - peer_connection* pc = boost::get_pointer(i->second); - #endif - assert(std::find(m_connections.begin(), m_connections.end(), pc) != m_connections.end()); - detail::session_impl::connection_map::iterator j = i; - ++i; - m_ses.m_connections.erase(j); - assert(m_connections.size() + 1 == num_connections); - assert(std::find(m_connections.begin(), m_connections.end(), pc) == m_connections.end()); - } - else - { - assert(std::find(m_connections.begin(), m_connections.end(), boost::get_pointer(i->second)) == m_connections.end()); - ++i; - } + assert(i->second->associated_torrent() == this); + + detail::session_impl::connection_map::iterator j = + m_ses.m_connections.find(i->second->get_socket()); + + assert(j != m_ses.m_connections.end()); + + // in the destructor of the peer_connection + // it will remove itself from this torrent + // and from the list we're iterating over. + // so we need to increment the iterator riht + // away. + ++i; + + m_ses.m_connections.erase(j); } - assert(m_connections.empty()); } @@ -659,11 +663,11 @@ namespace libtorrent m_policy->pulse(); } - for (std::vector::iterator i = m_connections.begin(); + for (peer_iterator i = m_connections.begin(); i != m_connections.end(); ++i) { - peer_connection* p = (*i); + peer_connection* p = i->second; const stat& s = p->statistics(); m_stat += s; p->second_tick(); diff --git a/src/torrent_handle.cpp b/src/torrent_handle.cpp index d6c79fcaa..4809bdd58 100755 --- a/src/torrent_handle.cpp +++ b/src/torrent_handle.cpp @@ -243,15 +243,15 @@ namespace libtorrent ret.dict()["peers"] = entry::list_type(); entry::list_type& peer_list = ret.dict()["peers"].list(); - for (torrent::peer_const_iterator i = t->begin(); + for (torrent::const_peer_iterator i = t->begin(); i != t->end(); ++i) { // we cannot save remote connection // since we don't know their listen port - if (!(*i)->is_local()) continue; + if (!i->second->is_local()) continue; - address ip = (*i)->get_socket()->sender(); + address ip = i->second->get_socket()->sender(); entry::dictionary_type peer; peer["ip"] = ip.as_string(); peer["port"] = ip.port(); @@ -360,11 +360,11 @@ namespace libtorrent const torrent* t = m_ses->find_torrent(m_info_hash); if (t == 0) return; - for (std::vector::const_iterator i = t->begin(); + for (torrent::const_peer_iterator i = t->begin(); i != t->end(); ++i) { - peer_connection* peer = *i; + peer_connection* peer = i->second; // peers that hasn't finished the handshake should // not be included in this list