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

« back to all changes in this revision

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