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
41
#include <rak/functional.h>
44
#include "block_failed.h"
45
#include "block_list.h"
46
#include "block_transfer.h"
47
#include "exceptions.h"
52
BlockTransfer::create_dummy(PeerInfo* peerInfo, const Piece& piece) {
53
set_peer_info(peerInfo);
57
m_state = BlockTransfer::STATE_ERASED;
61
m_failedIndex = invalid_index;
67
std::for_each(m_queued.begin(), m_queued.end(), std::bind1st(std::mem_fun(&Block::invalidate_transfer), this));
70
std::for_each(m_transfers.begin(), m_transfers.end(), std::bind1st(std::mem_fun(&Block::invalidate_transfer), this));
73
if (m_notStalled != 0)
74
throw internal_error("Block::clear() m_stalled != 0.");
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).");
86
transfer_list_type::iterator itr = m_queued.insert(m_queued.end(), new BlockTransfer());
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);
94
(*itr)->set_failed_index(BlockFailed::invalid_index);
100
Block::erase(BlockTransfer* transfer) {
101
if (transfer->is_erased())
102
throw internal_error("Block::erase(...) transfer already erased.");
104
m_notStalled -= transfer->stall() == 0;
106
if (transfer->is_queued()) {
107
transfer_list_type::iterator itr = std::find(m_queued.begin(), m_queued.end(), transfer);
109
if (itr == m_queued.end())
110
throw internal_error("Block::erase(...) Could not find transfer.");
114
} else if (!transfer->is_finished()) {
115
transfer_list_type::iterator itr = std::find(m_transfers.begin(), m_transfers.end(), transfer);
117
if (itr == m_transfers.end())
118
throw internal_error("Block::erase(...) Could not find transfer.");
120
// Need to do something different here for now, i think.
121
m_transfers.erase(itr);
123
if (transfer == m_leader) {
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.
130
// Create a range containing transfers with
131
// is_not_leader(). Erased transfer will end up in the back.
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));
136
transfer_list_type::iterator newLeader = std::max_element(first, last, rak::less2(std::mem_fun(&BlockTransfer::position), std::mem_fun(&BlockTransfer::position)));
138
if (newLeader != last) {
139
m_leader = *newLeader;
140
m_leader->set_state(BlockTransfer::STATE_LEADER);
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();
153
throw internal_error("Block::erase(...) Transfer is finished.");
156
transfer->set_block(NULL);
161
Block::transfering(BlockTransfer* transfer) {
162
if (!transfer->is_valid())
163
throw internal_error("Block::transfering(...) transfer->block() == NULL.");
165
transfer_list_type::iterator itr = std::find(m_queued.begin(), m_queued.end(), transfer);
167
if (itr == m_queued.end())
168
throw internal_error("Block::transfering(...) not queued.");
171
m_transfers.insert(m_transfers.end(), transfer);
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
178
if (m_leader != NULL) {
179
transfer->set_state(BlockTransfer::STATE_NOT_LEADER);
185
transfer->set_state(BlockTransfer::STATE_LEADER);
191
Block::completed(BlockTransfer* transfer) {
192
if (!transfer->is_valid())
193
throw internal_error("Block::completed(...) transfer->block() == NULL.");
195
if (!transfer->is_leader())
196
throw internal_error("Block::completed(...) transfer is not the leader.");
198
// Special case where another ignored transfer finished before the
201
// Perhaps do magic to the transfer, erase it or something.
203
throw internal_error("Block::completed(...) !is_finished().");
205
if (transfer != m_leader)
206
throw internal_error("Block::completed(...) transfer != m_leader.");
208
m_parent->inc_finished();
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.");
213
m_notStalled -= transfer->stall() == 0;
215
transfer->set_block(NULL);
216
transfer->set_stall(~uint32_t());
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));
226
// We need to invalidate those unfinished and keep the one that
227
// finished for later reference.
228
remove_non_leader_transfers();
230
if (m_transfers.empty() || m_transfers.back() != transfer)
231
throw internal_error("Block::completed(...) m_transfers.empty() || m_transfers.back() != transfer.");
233
return m_parent->is_all_finished();
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
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.");
245
m_notStalled -= transfer->stall() == 0;
247
// Why not just delete? Gets done by completed(), though when
248
// erasing the leader we need to remove dissimilar unless we have
251
transfer->set_state(BlockTransfer::STATE_ERASED);
252
transfer->set_position(0);
253
transfer->set_block(NULL);
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.");
267
transfer->set_stall(transfer->stall() + 1);
271
Block::change_leader(BlockTransfer* transfer) {
272
if (m_leader == transfer)
273
throw internal_error("Block::change_leader(...) m_leader == transfer.");
276
throw internal_error("Block::change_leader(...) is_finished().");
278
if (m_leader != NULL)
279
m_leader->set_state(BlockTransfer::STATE_NOT_LEADER);
282
m_leader->set_state(BlockTransfer::STATE_LEADER);
286
Block::failed_leader() {
288
throw internal_error("Block::failed_leader(...) !is_finished().");
292
if (m_failedList != NULL)
293
m_failedList->set_current(BlockFailed::invalid_index);
297
Block::invalidate_transfer(BlockTransfer* transfer) {
298
if (transfer == m_leader)
299
throw internal_error("Block::invalidate_transfer(...) transfer == m_leader.");
301
// FIXME: Various other accounting like position and counters.
302
if (!transfer->is_valid()) {
306
m_notStalled -= transfer->stall() == 0;
308
transfer->set_block(NULL);
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)));
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());
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));
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());
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)));
332
if (itr == m_queued.end())
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)));
342
if (itr == m_queued.end())
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)));
352
if (itr == m_transfers.end())
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)));
362
if (itr == m_transfers.end())