1
// libTorrent - BitTorrent library
2
// Copyright (C) 2005-2006, Jari Sundell
4
// This program is free software; you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation; either version 2 of the License, or
7
// (at your option) any later version.
9
// This program is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
// GNU General Public License for more details.
14
// You should have received a copy of the GNU General Public License
15
// along with this program; if not, write to the Free Software
16
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
// In addition, as a special exception, the copyright holders give
19
// permission to link the code of portions of this program with the
20
// OpenSSL library under certain conditions as described in each
21
// individual source file, and distribute linked combinations
24
// You must obey the GNU General Public License in all respects for
25
// all of the code used other than OpenSSL. If you modify file(s)
26
// with this exception, you may extend this exception to your version
27
// of the file(s), but you are not obligated to do so. If you do not
28
// wish to do so, delete this exception statement from your version.
29
// If you delete this exception statement from all source files in the
30
// program, then also delete it here.
32
// Contact: Jari Sundell <jaris@ifi.uio.no>
35
// 3185 Skoppum, NORWAY
42
#include <rak/functional.h>
44
#include "data/chunk.h"
45
#include "peer/peer_info.h"
47
#include "block_failed.h"
48
#include "block_transfer.h"
49
#include "block_list.h"
50
#include "exceptions.h"
53
#include "transfer_list.h"
57
TransferList::iterator
58
TransferList::find(uint32_t index) {
59
return std::find_if(begin(), end(), rak::equal(index, std::mem_fun(&BlockList::index)));
62
TransferList::const_iterator
63
TransferList::find(uint32_t index) const {
64
return std::find_if(begin(), end(), rak::equal(index, std::mem_fun(&BlockList::index)));
68
TransferList::clear() {
69
std::for_each(begin(), end(), rak::on(std::mem_fun(&BlockList::index), m_slotCanceled));
70
std::for_each(begin(), end(), rak::call_delete<BlockList>());
75
TransferList::iterator
76
TransferList::insert(const Piece& piece, uint32_t blockSize) {
77
if (find(piece.index()) != end())
78
throw internal_error("Delegator::new_chunk(...) received an index that is already delegated.");
80
BlockList* blockList = new BlockList(piece, blockSize);
82
m_slotQueued(piece.index());
84
return base_type::insert(end(), blockList);
87
TransferList::iterator
88
TransferList::erase(iterator itr) {
90
throw internal_error("TransferList::erase(...) itr == m_chunks.end().");
94
return base_type::erase(itr);
98
TransferList::finished(BlockTransfer* transfer) {
99
if (!transfer->is_valid())
100
throw internal_error("TransferList::finished(...) got transfer with wrong state.");
102
uint32_t index = transfer->block()->index();
104
// Marks the transfer as complete and erases it.
105
if (transfer->block()->completed(transfer))
106
m_slotCompleted(index);
110
TransferList::hash_succeded(uint32_t index) {
111
iterator blockListItr = find(index);
113
if ((Block::size_type)std::count_if((*blockListItr)->begin(), (*blockListItr)->end(), std::mem_fun_ref(&Block::is_finished)) != (*blockListItr)->size())
114
throw internal_error("TransferList::hash_succeded(...) Finished blocks does not match size.");
116
if ((*blockListItr)->failed() != 0)
117
mark_failed_peers(*blockListItr);
122
struct transfer_list_compare_data {
123
transfer_list_compare_data(Chunk* chunk, const Piece& p) : m_chunk(chunk), m_piece(p) { }
125
bool operator () (const BlockFailed::reference failed) {
126
return m_chunk->compare_buffer(failed.first, m_piece.offset(), m_piece.length());
134
TransferList::hash_failed(uint32_t index, Chunk* chunk) {
135
iterator blockListItr = find(index);
137
if (blockListItr == end())
138
throw internal_error("TransferList::hash_failed(...) Could not find index.");
140
if ((Block::size_type)std::count_if((*blockListItr)->begin(), (*blockListItr)->end(), std::mem_fun_ref(&Block::is_finished)) != (*blockListItr)->size())
141
throw internal_error("TransferList::hash_failed(...) Finished blocks does not match size.");
143
// Could propably also check promoted against size of the block
146
if ((*blockListItr)->attempt() == 0) {
147
unsigned int promoted = update_failed(*blockListItr, chunk);
149
if (promoted > 0 || promoted < (*blockListItr)->size()) {
150
// Retry with the most popular blocks.
151
(*blockListItr)->set_attempt(1);
152
retry_most_popular(*blockListItr, chunk);
154
// Also consider various other schemes, like using blocks from
155
// only/mainly one peer.
161
// Should we check if there's any peers whom have sent us bad data
162
// before, and just clear those first?
164
// Re-download the blocks.
165
(*blockListItr)->clear_finished();
166
(*blockListItr)->set_attempt(0);
168
// Clear leaders when we want to redownload the chunk.
169
std::for_each((*blockListItr)->begin(), (*blockListItr)->end(), std::mem_fun_ref(&Block::failed_leader));
172
// update_failed(...) either increments the reference count of a
173
// failed entry, or creates a new one if the data differs.
175
TransferList::update_failed(BlockList* blockList, Chunk* chunk) {
176
unsigned int promoted = 0;
178
blockList->inc_failed();
180
for (BlockList::iterator itr = blockList->begin(), last = blockList->end(); itr != last; ++itr) {
182
if (itr->failed_list() == NULL)
183
itr->set_failed_list(new BlockFailed());
185
BlockFailed::iterator failedItr = std::find_if(itr->failed_list()->begin(), itr->failed_list()->end(),
186
transfer_list_compare_data(chunk, itr->piece()));
188
if (failedItr == itr->failed_list()->end()) {
189
// We've never encountered this data before, make a new entry.
190
char* buffer = new char[itr->piece().length()];
192
chunk->to_buffer(buffer, itr->piece().offset(), itr->piece().length());
194
itr->failed_list()->push_back(BlockFailed::value_type(buffer, 1));
196
// Count how many new data sets?
199
// Increment promoted when the entry's reference count becomes
200
// larger than others, but not if it previously was the largest.
202
BlockFailed::iterator maxItr = itr->failed_list()->max_element();
204
if (maxItr->second == failedItr->second && maxItr != --itr->failed_list()->reverse_max_element().base())
210
itr->failed_list()->set_current(failedItr);
211
itr->leader()->set_failed_index(failedItr - itr->failed_list()->begin());
218
TransferList::mark_failed_peers(BlockList* blockList) {
219
std::set<PeerInfo*> badPeers;
221
for (BlockList::iterator itr = blockList->begin(), last = blockList->end(); itr != last; ++itr)
222
for (Block::transfer_list_type::const_iterator itr2 = itr->transfers()->begin(), last2 = itr->transfers()->end(); itr2 != last2; ++itr2)
223
if ((*itr2)->failed_index() != itr->failed_list()->current())
224
badPeers.insert((*itr2)->peer_info());
226
std::for_each(badPeers.begin(), badPeers.end(), m_slotCorrupt);
229
// Copy the stored data to the chunk from the failed entries with
230
// largest reference counts.
232
TransferList::retry_most_popular(BlockList* blockList, Chunk* chunk) {
233
for (BlockList::iterator itr = blockList->begin(), last = blockList->end(); itr != last; ++itr) {
235
BlockFailed::reverse_iterator failedItr = itr->failed_list()->reverse_max_element();
237
if (failedItr == itr->failed_list()->rend())
238
throw internal_error("TransferList::retry_most_popular(...) No failed list entry found.");
240
// The data is the same, so no need to copy.
241
if (failedItr == itr->failed_list()->current_reverse_iterator())
244
// Change the leader to the currently held buffer?
246
chunk->from_buffer(failedItr->first, itr->piece().offset(), itr->piece().length());
248
itr->failed_list()->set_current(failedItr);
251
m_slotCompleted(blockList->index());