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

« back to all changes in this revision

Viewing changes to src/torrent/data/block.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 <rak/functional.h>
 
42
 
 
43
#include "peer/peer_info.h"
 
44
#include "protocol/peer_connection_base.h"
 
45
 
 
46
#include "block.h"
 
47
#include "block_failed.h"
 
48
#include "block_list.h"
 
49
#include "block_transfer.h"
 
50
#include "exceptions.h"
 
51
 
 
52
namespace torrent {
 
53
 
 
54
Block::~Block() {
 
55
  m_leader = NULL;
 
56
 
 
57
  std::for_each(m_queued.begin(), m_queued.end(), std::bind1st(std::mem_fun(&Block::invalidate_transfer), this));
 
58
  m_queued.clear();
 
59
 
 
60
  std::for_each(m_transfers.begin(), m_transfers.end(), std::bind1st(std::mem_fun(&Block::invalidate_transfer), this));
 
61
  m_transfers.clear();
 
62
 
 
63
  if (m_notStalled != 0)
 
64
    throw internal_error("Block::clear() m_stalled != 0.");
 
65
 
 
66
  delete m_failedList;
 
67
}
 
68
 
 
69
BlockTransfer*
 
70
Block::insert(PeerInfo* peerInfo) {
 
71
  if (find_queued(peerInfo) || find_transfer(peerInfo))
 
72
    throw internal_error("Block::insert(...) find_queued(peerInfo) || find_transfer(peerInfo).");
 
73
 
 
74
  m_notStalled++;
 
75
 
 
76
  transfer_list_type::iterator itr = m_queued.insert(m_queued.end(), new BlockTransfer());
 
77
 
 
78
  (*itr)->set_peer_info(peerInfo);
 
79
  (*itr)->set_block(this);
 
80
  (*itr)->set_piece(m_piece);
 
81
  (*itr)->set_state(BlockTransfer::STATE_QUEUED);
 
82
  (*itr)->set_position(0);
 
83
  (*itr)->set_stall(0);
 
84
  (*itr)->set_failed_index(BlockFailed::invalid_index);
 
85
 
 
86
  peerInfo->set_transfer_counter(peerInfo->transfer_counter() + 1);
 
87
 
 
88
  return (*itr);
 
89
}
 
90
  
 
91
void
 
92
Block::erase(BlockTransfer* transfer) {
 
93
  if (transfer->is_erased())
 
94
    throw internal_error("Block::erase(...) transfer already erased.");
 
95
 
 
96
  m_notStalled -= transfer->stall() == 0;
 
97
 
 
98
  if (transfer->is_queued()) {
 
99
    transfer_list_type::iterator itr = std::find(m_queued.begin(), m_queued.end(), transfer);
 
100
 
 
101
    if (itr == m_queued.end())
 
102
      throw internal_error("Block::erase(...) Could not find transfer.");
 
103
 
 
104
    m_queued.erase(itr);
 
105
 
 
106
  } else if (!transfer->is_finished()) {
 
107
    transfer_list_type::iterator itr = std::find(m_transfers.begin(), m_transfers.end(), transfer);
 
108
 
 
109
    if (itr == m_transfers.end())
 
110
      throw internal_error("Block::erase(...) Could not find transfer.");
 
111
 
 
112
    // Need to do something different here for now, i think.
 
113
    m_transfers.erase(itr);
 
114
 
 
115
    if (transfer == m_leader) {
 
116
 
 
117
      // When the leader is erased then any non-leading transfer must
 
118
      // be promoted. These non-leading transfers are guaranteed to
 
119
      // have the same data up to their position. PeerConnectionBase
 
120
      // assumes that a Block with non-leaders have a leader.
 
121
 
 
122
      // Create a range containing transfers with
 
123
      // is_not_leader(). Erased transfer will end up in the back.
 
124
 
 
125
      transfer_list_type::iterator first = std::find_if(m_transfers.begin(), m_transfers.end(), std::not1(std::mem_fun(&BlockTransfer::is_leader)));
 
126
      transfer_list_type::iterator last = std::stable_partition(first, m_transfers.end(), std::mem_fun(&BlockTransfer::is_not_leader));
 
127
 
 
128
      transfer_list_type::iterator newLeader = std::max_element(first, last, rak::less2(std::mem_fun(&BlockTransfer::position), std::mem_fun(&BlockTransfer::position)));
 
129
 
 
130
      if (newLeader != last) {
 
131
        m_leader = *newLeader;
 
132
        m_leader->set_state(BlockTransfer::STATE_LEADER);
 
133
      } else {
 
134
        m_leader = NULL;
 
135
 
 
136
        // If we have no new leader, remove the erased (dissimilar)
 
137
        // transfers so they can get another shot. They cannot be
 
138
        // removed when found dissimilar as that would result in them
 
139
        // being queued immediately.
 
140
        remove_erased_transfers();
 
141
      }
 
142
    }
 
143
 
 
144
  } else {
 
145
    throw internal_error("Block::erase(...) Transfer is finished.");
 
146
  }
 
147
 
 
148
  if (transfer->peer_info() != NULL)
 
149
    transfer->peer_info()->set_transfer_counter(transfer->peer_info()->transfer_counter() - 1);
 
150
 
 
151
  transfer->set_block(NULL);
 
152
  delete transfer;
 
153
}
 
154
 
 
155
bool
 
156
Block::transfering(BlockTransfer* transfer) {
 
157
  if (!transfer->is_valid())
 
158
    throw internal_error("Block::transfering(...) transfer->block() == NULL.");
 
159
 
 
160
  transfer_list_type::iterator itr = std::find(m_queued.begin(), m_queued.end(), transfer);
 
161
 
 
162
  if (itr == m_queued.end())
 
163
    throw internal_error("Block::transfering(...) not queued.");
 
164
 
 
165
  m_queued.erase(itr);
 
166
  m_transfers.insert(m_transfers.end(), transfer);
 
167
 
 
168
  // If this block already has an active transfer, make this transfer
 
169
  // skip the piece. If this transfer gets ahead of the currently
 
170
  // transfering, it will (a) take over as the leader if the data is
 
171
  // the same or (b) erase itself from this block if the data does not
 
172
  // match.
 
173
  if (m_leader != NULL) {
 
174
    transfer->set_state(BlockTransfer::STATE_NOT_LEADER);
 
175
    return false;
 
176
 
 
177
  } else {
 
178
    m_leader = transfer;
 
179
 
 
180
    transfer->set_state(BlockTransfer::STATE_LEADER);
 
181
    return true;
 
182
  }
 
183
}
 
184
 
 
185
bool
 
186
Block::completed(BlockTransfer* transfer) {
 
187
  if (!transfer->is_valid())
 
188
    throw internal_error("Block::completed(...) transfer->block() == NULL.");
 
189
 
 
190
  if (!transfer->is_leader())
 
191
    throw internal_error("Block::completed(...) transfer is not the leader.");
 
192
 
 
193
  // Special case where another ignored transfer finished before the
 
194
  // leader?
 
195
  //
 
196
  // Perhaps do magic to the transfer, erase it or something.
 
197
  if (!is_finished())
 
198
    throw internal_error("Block::completed(...) !is_finished().");
 
199
 
 
200
  if (transfer != m_leader)
 
201
    throw internal_error("Block::completed(...) transfer != m_leader.");
 
202
 
 
203
  m_parent->inc_finished();
 
204
 
 
205
  if ((Block::size_type)std::count_if(m_parent->begin(), m_parent->end(), std::mem_fun_ref(&Block::is_finished)) < m_parent->finished())
 
206
    throw internal_error("Block::completed(...) Finished blocks too large.");
 
207
 
 
208
  m_notStalled -= transfer->stall() == 0;
 
209
 
 
210
  transfer->set_block(NULL);
 
211
  transfer->set_stall(~uint32_t());
 
212
 
 
213
  // Currently just throw out the queued transfers. In case the hash
 
214
  // check fails, we might consider telling pcb during the call to
 
215
  // Block::transfering(...). But that would propably not be correct
 
216
  // as we want to trigger cancel messages from here, as hash fail is
 
217
  // a rare occurrence.
 
218
  std::for_each(m_queued.begin(), m_queued.end(), std::bind1st(std::mem_fun(&Block::invalidate_transfer), this));
 
219
  m_queued.clear();
 
220
 
 
221
  // We need to invalidate those unfinished and keep the one that
 
222
  // finished for later reference.
 
223
  remove_non_leader_transfers();
 
224
  
 
225
  if (m_transfers.empty() || m_transfers.back() != transfer)
 
226
    throw internal_error("Block::completed(...) m_transfers.empty() || m_transfers.back() != transfer.");
 
227
 
 
228
  return m_parent->is_all_finished();
 
229
}
 
230
 
 
231
// Mark a non-leading transfer as having received dissimilar data to
 
232
// the leader. It is then marked as erased so that we know its data
 
233
// was not used, yet keep it in m_transfers so as not to cause a
 
234
// re-download.
 
235
void
 
236
Block::transfer_dissimilar(BlockTransfer* transfer) {
 
237
  if (!transfer->is_not_leader() || m_leader == transfer)
 
238
    throw internal_error("Block::transfer_dissimilar(...) transfer is the leader.");
 
239
 
 
240
  m_notStalled -= transfer->stall() == 0;
 
241
 
 
242
  // Why not just delete? Gets done by completed(), though when
 
243
  // erasing the leader we need to remove dissimilar unless we have
 
244
  // another leader.
 
245
  
 
246
  transfer->set_state(BlockTransfer::STATE_ERASED);
 
247
  transfer->set_position(0);
 
248
  transfer->set_block(NULL);
 
249
}
 
250
 
 
251
void
 
252
Block::stalled_transfer(BlockTransfer* transfer) {
 
253
  if (transfer->stall() == 0) {
 
254
    if (m_notStalled == 0)
 
255
      throw internal_error("Block::stalled(...) m_notStalled == 0.");
 
256
 
 
257
    m_notStalled--;
 
258
 
 
259
    // Do magic here.
 
260
  }
 
261
 
 
262
  transfer->set_stall(transfer->stall() + 1);
 
263
}
 
264
 
 
265
void
 
266
Block::change_leader(BlockTransfer* transfer) {
 
267
  if (m_leader == transfer)
 
268
    throw internal_error("Block::change_leader(...) m_leader == transfer.");
 
269
 
 
270
  if (is_finished())
 
271
    throw internal_error("Block::change_leader(...) is_finished().");
 
272
 
 
273
  if (m_leader != NULL)
 
274
    m_leader->set_state(BlockTransfer::STATE_NOT_LEADER);
 
275
 
 
276
  m_leader = transfer;
 
277
  m_leader->set_state(BlockTransfer::STATE_LEADER);
 
278
}
 
279
 
 
280
void
 
281
Block::failed_leader() {
 
282
  if (!is_finished())
 
283
    throw internal_error("Block::failed_leader(...) !is_finished().");
 
284
 
 
285
  m_leader = NULL;
 
286
 
 
287
  if (m_failedList != NULL)
 
288
    m_failedList->set_current(BlockFailed::invalid_index);
 
289
}
 
290
 
 
291
void
 
292
Block::create_dummy(BlockTransfer* transfer, PeerInfo* peerInfo, const Piece& piece) {
 
293
  transfer->set_peer_info(peerInfo);
 
294
 
 
295
  if (peerInfo != NULL)
 
296
    peerInfo->set_transfer_counter(peerInfo->transfer_counter() + 1);
 
297
 
 
298
  transfer->set_block(NULL);
 
299
  transfer->set_piece(piece);
 
300
  transfer->set_state(BlockTransfer::STATE_ERASED);
 
301
 
 
302
  transfer->set_position(0);
 
303
  transfer->set_stall(0);
 
304
  transfer->set_failed_index(BlockTransfer::invalid_index);
 
305
}
 
306
 
 
307
void
 
308
Block::release(BlockTransfer* transfer) {
 
309
  if (!transfer->is_valid()) {
 
310
    if (transfer->peer_info() != NULL)
 
311
      transfer->peer_info()->set_transfer_counter(transfer->peer_info()->transfer_counter() - 1);
 
312
 
 
313
    delete transfer;
 
314
  } else {
 
315
    transfer->block()->erase(transfer);
 
316
  }
 
317
}
 
318
 
 
319
void
 
320
Block::invalidate_transfer(BlockTransfer* transfer) {
 
321
  if (transfer == m_leader)
 
322
    throw internal_error("Block::invalidate_transfer(...) transfer == m_leader.");
 
323
 
 
324
  // FIXME: Various other accounting like position and counters.
 
325
  if (!transfer->is_valid()) {
 
326
    if (transfer->peer_info() != NULL)
 
327
      transfer->peer_info()->set_transfer_counter(transfer->peer_info()->transfer_counter() - 1);
 
328
 
 
329
    delete transfer;
 
330
 
 
331
  } else {
 
332
    m_notStalled -= transfer->stall() == 0;
 
333
 
 
334
    transfer->set_block(NULL);
 
335
 
 
336
    // Do the canceling magic here. 
 
337
    if (transfer->peer_info()->connection() != NULL)
 
338
      transfer->peer_info()->connection()->cancel_transfer(transfer);
 
339
  }
 
340
}
 
341
 
 
342
void
 
343
Block::remove_erased_transfers() {
 
344
  transfer_list_type::iterator split = std::stable_partition(m_transfers.begin(), m_transfers.end(), std::not1(std::mem_fun(&BlockTransfer::is_erased)));
 
345
 
 
346
  std::for_each(split, m_transfers.end(), std::bind1st(std::mem_fun(&Block::invalidate_transfer), this));
 
347
  m_transfers.erase(split, m_transfers.end());
 
348
}
 
349
 
 
350
void
 
351
Block::remove_non_leader_transfers() {
 
352
  transfer_list_type::iterator split = std::stable_partition(m_transfers.begin(), m_transfers.end(), std::mem_fun(&BlockTransfer::is_leader));
 
353
 
 
354
  std::for_each(split, m_transfers.end(), std::bind1st(std::mem_fun(&Block::invalidate_transfer), this));
 
355
  m_transfers.erase(split, m_transfers.end());
 
356
}
 
357
 
 
358
BlockTransfer*
 
359
Block::find_queued(const PeerInfo* p) {
 
360
  transfer_list_type::iterator itr = std::find_if(m_queued.begin(), m_queued.end(), rak::equal(p, std::mem_fun(&BlockTransfer::peer_info)));
 
361
 
 
362
  if (itr == m_queued.end())
 
363
    return NULL;
 
364
  else
 
365
    return *itr;
 
366
}
 
367
 
 
368
const BlockTransfer*
 
369
Block::find_queued(const PeerInfo* p) const {
 
370
  transfer_list_type::const_iterator itr = std::find_if(m_queued.begin(), m_queued.end(), rak::equal(p, std::mem_fun(&BlockTransfer::peer_info)));
 
371
 
 
372
  if (itr == m_queued.end())
 
373
    return NULL;
 
374
  else
 
375
    return *itr;
 
376
}
 
377
 
 
378
BlockTransfer*
 
379
Block::find_transfer(const PeerInfo* p) {
 
380
  transfer_list_type::iterator itr = std::find_if(m_transfers.begin(), m_transfers.end(), rak::equal(p, std::mem_fun(&BlockTransfer::peer_info)));
 
381
 
 
382
  if (itr == m_transfers.end())
 
383
    return NULL;
 
384
  else
 
385
    return *itr;
 
386
}
 
387
 
 
388
const BlockTransfer*
 
389
Block::find_transfer(const PeerInfo* p) const {
 
390
  transfer_list_type::const_iterator itr = std::find_if(m_transfers.begin(), m_transfers.end(), rak::equal(p, std::mem_fun(&BlockTransfer::peer_info)));
 
391
 
 
392
  if (itr == m_transfers.end())
 
393
    return NULL;
 
394
  else
 
395
    return *itr;
 
396
}
 
397
 
 
398
}