~ubuntu-branches/debian/experimental/libtorrent/experimental

« back to all changes in this revision

Viewing changes to src/torrent/data/transfer_list.cc

  • Committer: Bazaar Package Importer
  • Author(s): Jose Luis Rivas
  • Date: 2007-03-31 10:31:05 UTC
  • mto: (4.1.4 gutsy) (6.2.1 squeeze) (1.3.1 upstream)
  • mto: This revision was merged to the branch mainline in revision 6.
  • Revision ID: james.westby@ubuntu.com-20070331103105-jzpp1rml6ud0ff75
Tags: upstream-0.11.4
ImportĀ upstreamĀ versionĀ 0.11.4

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// libTorrent - BitTorrent library
 
2
// Copyright (C) 2005-2006, Jari Sundell
 
3
//
 
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.
 
8
// 
 
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.
 
13
// 
 
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
 
17
//
 
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
 
22
// including the two.
 
23
//
 
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.
 
31
//
 
32
// Contact:  Jari Sundell <jaris@ifi.uio.no>
 
33
//
 
34
//           Skomakerveien 33
 
35
//           3185 Skoppum, NORWAY
 
36
 
 
37
#include "config.h"
 
38
 
 
39
#include <algorithm>
 
40
#include <functional>
 
41
#include <set>
 
42
#include <rak/functional.h>
 
43
 
 
44
#include "data/chunk.h"
 
45
#include "peer/peer_info.h"
 
46
 
 
47
#include "block_failed.h"
 
48
#include "block_transfer.h"
 
49
#include "block_list.h"
 
50
#include "exceptions.h"
 
51
#include "piece.h"
 
52
 
 
53
#include "transfer_list.h"
 
54
 
 
55
namespace torrent {
 
56
 
 
57
TransferList::iterator
 
58
TransferList::find(uint32_t index) {
 
59
  return std::find_if(begin(), end(), rak::equal(index, std::mem_fun(&BlockList::index)));
 
60
}
 
61
 
 
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)));
 
65
}
 
66
 
 
67
void
 
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>());
 
71
 
 
72
  base_type::clear();
 
73
}
 
74
 
 
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.");
 
79
 
 
80
  BlockList* blockList = new BlockList(piece, blockSize);
 
81
  
 
82
  m_slotQueued(piece.index());
 
83
 
 
84
  return base_type::insert(end(), blockList);
 
85
}
 
86
 
 
87
TransferList::iterator
 
88
TransferList::erase(iterator itr) {
 
89
  if (itr == end())
 
90
    throw internal_error("TransferList::erase(...) itr == m_chunks.end().");
 
91
 
 
92
  delete *itr;
 
93
 
 
94
  return base_type::erase(itr);
 
95
}
 
96
 
 
97
void
 
98
TransferList::finished(BlockTransfer* transfer) {
 
99
  if (!transfer->is_valid())
 
100
    throw internal_error("TransferList::finished(...) got transfer with wrong state.");
 
101
 
 
102
  uint32_t index = transfer->block()->index();
 
103
 
 
104
  // Marks the transfer as complete and erases it.
 
105
  if (transfer->block()->completed(transfer))
 
106
    m_slotCompleted(index);
 
107
}
 
108
 
 
109
void
 
110
TransferList::hash_succeded(uint32_t index) {
 
111
  iterator blockListItr = find(index);
 
112
 
 
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.");
 
115
 
 
116
  if ((*blockListItr)->failed() != 0)
 
117
    mark_failed_peers(*blockListItr);
 
118
 
 
119
  erase(blockListItr);
 
120
}
 
121
 
 
122
struct transfer_list_compare_data {
 
123
  transfer_list_compare_data(Chunk* chunk, const Piece& p) : m_chunk(chunk), m_piece(p) { }
 
124
 
 
125
  bool operator () (const BlockFailed::reference failed) {
 
126
    return m_chunk->compare_buffer(failed.first, m_piece.offset(), m_piece.length());
 
127
  }
 
128
 
 
129
  Chunk* m_chunk;
 
130
  Piece  m_piece;
 
131
};
 
132
 
 
133
void
 
134
TransferList::hash_failed(uint32_t index, Chunk* chunk) {
 
135
  iterator blockListItr = find(index);
 
136
 
 
137
  if (blockListItr == end())
 
138
    throw internal_error("TransferList::hash_failed(...) Could not find index.");
 
139
 
 
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.");
 
142
 
 
143
  // Could propably also check promoted against size of the block
 
144
  // list.
 
145
 
 
146
  if ((*blockListItr)->attempt() == 0) {
 
147
    unsigned int promoted = update_failed(*blockListItr, chunk);
 
148
 
 
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);
 
153
 
 
154
      // Also consider various other schemes, like using blocks from
 
155
      // only/mainly one peer.
 
156
 
 
157
      return;
 
158
    }
 
159
  }
 
160
 
 
161
  // Should we check if there's any peers whom have sent us bad data
 
162
  // before, and just clear those first?
 
163
 
 
164
  // Re-download the blocks.
 
165
  (*blockListItr)->clear_finished();
 
166
  (*blockListItr)->set_attempt(0);
 
167
 
 
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));
 
170
}
 
171
 
 
172
// update_failed(...) either increments the reference count of a
 
173
// failed entry, or creates a new one if the data differs.
 
174
unsigned int
 
175
TransferList::update_failed(BlockList* blockList, Chunk* chunk) {
 
176
  unsigned int promoted = 0;
 
177
 
 
178
  blockList->inc_failed();
 
179
 
 
180
  for (BlockList::iterator itr = blockList->begin(), last = blockList->end(); itr != last; ++itr) {
 
181
    
 
182
    if (itr->failed_list() == NULL)
 
183
      itr->set_failed_list(new BlockFailed());
 
184
 
 
185
    BlockFailed::iterator failedItr = std::find_if(itr->failed_list()->begin(), itr->failed_list()->end(),
 
186
                                                   transfer_list_compare_data(chunk, itr->piece()));
 
187
 
 
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()];
 
191
 
 
192
      chunk->to_buffer(buffer, itr->piece().offset(), itr->piece().length());
 
193
 
 
194
      itr->failed_list()->push_back(BlockFailed::value_type(buffer, 1));
 
195
 
 
196
      // Count how many new data sets?
 
197
 
 
198
    } else {
 
199
      // Increment promoted when the entry's reference count becomes
 
200
      // larger than others, but not if it previously was the largest.
 
201
 
 
202
      BlockFailed::iterator maxItr = itr->failed_list()->max_element();
 
203
 
 
204
      if (maxItr->second == failedItr->second && maxItr != --itr->failed_list()->reverse_max_element().base())
 
205
        promoted++;
 
206
 
 
207
      failedItr->second++;
 
208
    }
 
209
 
 
210
    itr->failed_list()->set_current(failedItr);
 
211
    itr->leader()->set_failed_index(failedItr - itr->failed_list()->begin());
 
212
  }
 
213
 
 
214
  return promoted;
 
215
}
 
216
 
 
217
void
 
218
TransferList::mark_failed_peers(BlockList* blockList) {
 
219
  std::set<PeerInfo*> badPeers;
 
220
 
 
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());
 
225
 
 
226
  std::for_each(badPeers.begin(), badPeers.end(), m_slotCorrupt);
 
227
}
 
228
 
 
229
// Copy the stored data to the chunk from the failed entries with
 
230
// largest reference counts.
 
231
void
 
232
TransferList::retry_most_popular(BlockList* blockList, Chunk* chunk) {
 
233
  for (BlockList::iterator itr = blockList->begin(), last = blockList->end(); itr != last; ++itr) {
 
234
    
 
235
    BlockFailed::reverse_iterator failedItr = itr->failed_list()->reverse_max_element();
 
236
 
 
237
    if (failedItr == itr->failed_list()->rend())
 
238
      throw internal_error("TransferList::retry_most_popular(...) No failed list entry found.");
 
239
 
 
240
    // The data is the same, so no need to copy.
 
241
    if (failedItr == itr->failed_list()->current_reverse_iterator())
 
242
      continue;
 
243
 
 
244
    // Change the leader to the currently held buffer?
 
245
 
 
246
    chunk->from_buffer(failedItr->first, itr->piece().offset(), itr->piece().length());
 
247
 
 
248
    itr->failed_list()->set_current(failedItr);
 
249
  }
 
250
 
 
251
  m_slotCompleted(blockList->index());
 
252
}
 
253
 
 
254
}