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

« back to all changes in this revision

Viewing changes to src/download/choke_manager.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:
38
38
 
39
39
#include <algorithm>
40
40
#include <functional>
41
 
#include <stdlib.h>
 
41
#include <numeric>
 
42
#include <cstdlib>
42
43
 
43
44
#include "protocol/peer_connection_base.h"
44
45
 
45
46
#include "choke_manager.h"
 
47
#include "choke_manager_node.h"
46
48
 
47
49
namespace torrent {
48
50
 
 
51
void
 
52
choke_manager_erase(ChokeManager::container_type* container, PeerConnectionBase* pc) {
 
53
  ChokeManager::container_type::iterator itr = std::find_if(container->begin(), container->end(),
 
54
                                                            rak::equal(pc, rak::mem_ref(&ChokeManager::value_type::first)));
 
55
 
 
56
  if (itr == container->end())
 
57
    throw internal_error("choke_manager_remove(...) itr == m_unchoked.end().");
 
58
 
 
59
  *itr = container->back();
 
60
  container->pop_back();
 
61
}
 
62
 
49
63
ChokeManager::~ChokeManager() {
50
 
  if (m_currentlyUnchoked != 0)
 
64
  if (m_unchoked.size() != 0)
51
65
    throw internal_error("ChokeManager::~ChokeManager() called but m_currentlyUnchoked != 0.");
52
66
 
53
 
  if (m_currentlyUnchoked != 0)
54
 
    throw internal_error("ChokeManager::~ChokeManager() called but m_currentlyInterested != 0.");
55
 
}
56
 
 
57
 
struct choke_manager_read_rate_increasing {
58
 
  bool operator () (PeerConnectionBase* p1, PeerConnectionBase* p2) const {
59
 
    return *p1->peer_chunks()->download_throttle()->rate() < *p2->peer_chunks()->download_throttle()->rate();
60
 
  }
61
 
};
62
 
 
63
 
struct choke_manager_write_rate_increasing {
64
 
  bool operator () (PeerConnectionBase* p1, PeerConnectionBase* p2) const {
65
 
    return *p1->peer_chunks()->upload_throttle()->rate() < *p2->peer_chunks()->upload_throttle()->rate();
66
 
  }
67
 
};
68
 
 
69
 
struct choke_manager_read_rate_decreasing {
70
 
  bool operator () (PeerConnectionBase* p1, PeerConnectionBase* p2) const {
71
 
    return *p1->peer_chunks()->download_throttle()->rate() > *p2->peer_chunks()->download_throttle()->rate();
72
 
  }
73
 
};
74
 
 
75
 
struct choke_manager_is_remote_not_uploading {
76
 
  bool operator () (PeerConnectionBase* p1) const {
77
 
    return *p1->peer_chunks()->download_throttle()->rate() == 0;
78
 
  }
79
 
};
80
 
 
81
 
struct choke_manager_is_remote_uploading {
82
 
  bool operator () (PeerConnectionBase* p1) const {
83
 
    return *p1->peer_chunks()->download_throttle()->rate() != 0;
84
 
  }
85
 
};
86
 
 
87
 
struct choke_manager_is_interested {
88
 
  bool operator () (PeerConnectionBase* p) const {
89
 
    return p->is_upload_wanted();
90
 
  }
91
 
};
92
 
 
93
 
struct choke_manager_not_recently_unchoked {
94
 
  bool operator () (PeerConnectionBase* p) const {
95
 
    return p->time_last_choked() + rak::timer::from_seconds(10) < cachedTime;
96
 
  }
97
 
};
98
 
 
99
 
inline ChokeManager::iterator
100
 
ChokeManager::seperate_interested(iterator first, iterator last) {
101
 
  return std::partition(first, last, choke_manager_is_interested());
102
 
}
103
 
 
104
 
inline ChokeManager::iterator
105
 
ChokeManager::seperate_unchoked(iterator first, iterator last) {
106
 
  return std::partition(first, last, std::not1(std::mem_fun(&PeerConnectionBase::is_up_choked)));
 
67
  if (m_queued.size() != 0)
 
68
    throw internal_error("ChokeManager::~ChokeManager() called but m_currentlyQueued != 0.");
107
69
}
108
70
 
109
71
// 1  > 1
113
75
// 33 > 5 < 41
114
76
// 65 > 9 < 81
115
77
 
116
 
inline unsigned int
 
78
inline uint32_t
117
79
ChokeManager::max_alternate() const {
118
 
  if (m_currentlyUnchoked < 31)
119
 
    return (m_currentlyUnchoked + 7) / 8;
 
80
  if (m_unchoked.size() < 31)
 
81
    return (m_unchoked.size() + 7) / 8;
120
82
  else
121
 
    return (m_currentlyUnchoked + 9) / 10;
122
 
}
123
 
 
124
 
inline void
125
 
ChokeManager::alternate_ranges(iterator firstUnchoked, iterator lastUnchoked,
126
 
                               iterator firstChoked, iterator lastChoked,
127
 
                               unsigned int max) {
128
 
  max = std::min(max, std::min<unsigned int>(std::distance(firstUnchoked, lastUnchoked), std::distance(firstChoked, lastChoked)));
129
 
 
130
 
  // Do unchoke first, then choke. Take the return value of the first
131
 
  // unchoke and use it for choking. Don't need the above min call.
132
 
 
133
 
  choke_range(firstUnchoked, lastUnchoked, max);
134
 
  unchoke_range(firstChoked, lastChoked, max);
135
 
}
136
 
 
137
 
inline void
138
 
ChokeManager::swap_with_shift(iterator first, iterator source) {
139
 
  while (first != source) {
140
 
    iterator tmp = source;
141
 
    std::iter_swap(tmp, --source);
142
 
  }
 
83
    return (m_unchoked.size() + 9) / 10;
143
84
}
144
85
 
145
86
// Would propably replace maxUnchoked with whatever max the global
150
91
 
151
92
void
152
93
ChokeManager::balance() {
153
 
  // Return if no balance is needed.
154
 
  if (m_currentlyUnchoked == m_maxUnchoked)
 
94
  // Return if no balancing is needed. Don't return if is_unlimited()
 
95
  // as we might have just changed the value and have interested that
 
96
  // can be unchoked.
 
97
  if (m_unchoked.size() == m_maxUnchoked)
155
98
    return;
156
99
 
157
 
  iterator beginUninterested = seperate_interested(m_connectionList->begin(), m_connectionList->end());
158
 
  iterator beginChoked       = seperate_unchoked(m_connectionList->begin(), beginUninterested);
159
 
 
160
 
  int adjust = m_maxUnchoked - m_currentlyUnchoked;
 
100
  int adjust = m_maxUnchoked - m_unchoked.size();
161
101
 
162
102
  if (adjust > 0) {
163
 
    adjust = unchoke_range(beginChoked, beginUninterested,
164
 
                           std::min((unsigned int)adjust, m_slotCanUnchoke()));
 
103
    adjust = unchoke_range(m_queued.begin(), m_queued.end(),
 
104
                           std::min((uint32_t)adjust, m_slotCanUnchoke()));
165
105
 
166
106
    m_slotUnchoke(adjust);
167
107
 
169
109
    // We can do the choking before the slot is called as this
170
110
    // ChokeManager won't be unchoking the same peers due to the
171
111
    // call-back.
172
 
    adjust = choke_range(m_connectionList->begin(), beginChoked, -adjust);
 
112
    adjust = choke_range(m_unchoked.begin(), m_unchoked.end(), -adjust);
173
113
 
174
 
    m_slotChoke(adjust);
 
114
    m_slotUnchoke(-adjust);
175
115
  }
176
116
}
177
117
 
178
118
int
179
 
ChokeManager::cycle(unsigned int quota) {
180
 
  iterator lastChoked = seperate_interested(m_connectionList->begin(), m_connectionList->end());
181
 
  iterator firstChoked = seperate_unchoked(m_connectionList->begin(), lastChoked);
182
 
 
183
 
  // Partition away the connections we shall not choke.
184
 
 
185
 
  if (std::distance(m_connectionList->begin(), firstChoked) != (ConnectionList::difference_type)m_currentlyUnchoked)
186
 
    throw internal_error("ChokeManager::cycle() std::distance(m_connectionList->begin(), firstChoked) != m_currentlyUnchoked.");
187
 
 
188
 
  if (std::distance(m_connectionList->begin(), lastChoked) != (ConnectionList::difference_type)m_currentlyInterested)
189
 
    throw internal_error("ChokeManager::cycle() std::distance(m_connectionList->begin(), lastChoked) != m_currentlyInterested.");
190
 
 
191
 
  iterator firstUnchoked = m_connectionList->begin();
192
 
  iterator lastUnchoked = firstChoked;
193
 
 
194
 
  int cycled, adjust;
195
 
 
196
 
  // We don't call the resource manager slots, the number of un/choked
197
 
  // connections is returned.
198
 
 
199
 
  adjust = std::min(quota, m_maxUnchoked) - m_currentlyUnchoked;
200
 
 
201
 
  if (adjust > 0)
202
 
    firstChoked += (cycled = unchoke_range(firstChoked, lastChoked, adjust));
203
 
  else if (adjust < 0)
204
 
    firstUnchoked -= (cycled = -choke_range(firstUnchoked, lastUnchoked, -adjust));
205
 
  else
206
 
    cycled = 0;
207
 
 
208
 
  adjust = max_alternate() - std::abs(cycled);
209
 
 
210
 
  // Consider rolling this into the above calls.
211
 
  if (adjust > 0)
212
 
    alternate_ranges(firstUnchoked, lastUnchoked, firstChoked, lastChoked, adjust);
213
 
 
214
 
  if (m_currentlyUnchoked > quota)
215
 
    throw internal_error("ChokeManager::cycle() m_currentlyUnchoked > quota.");
216
 
 
217
 
  return cycled;
218
 
}
219
 
 
220
 
void
221
 
ChokeManager::set_interested(PeerConnectionBase* pc) {
222
 
  m_currentlyInterested++;
223
 
 
224
 
  if (!pc->is_up_choked())
225
 
    return;
226
 
 
227
 
  if (m_currentlyUnchoked < m_maxUnchoked &&
228
 
      pc->time_last_choked() + rak::timer::from_seconds(10) < cachedTime &&
229
 
      m_slotCanUnchoke()) {
230
 
    pc->receive_choke(false);
231
 
 
232
 
    m_currentlyUnchoked++;
233
 
    m_slotUnchoke(1);
234
 
  }
235
 
}
236
 
 
237
 
void
238
 
ChokeManager::set_not_interested(PeerConnectionBase* pc) {
239
 
  m_currentlyInterested--;
240
 
 
241
 
  if (pc->is_up_choked())
242
 
    return;
243
 
 
244
 
  pc->receive_choke(true);
245
 
 
246
 
  m_currentlyUnchoked--;
247
 
  m_slotChoke(1);
248
 
}
249
 
 
250
 
// We are no longer be in m_connectionList.
251
 
void
252
 
ChokeManager::disconnected(PeerConnectionBase* pc) {
253
 
  if (pc->is_upload_wanted())
254
 
    m_currentlyInterested--;
255
 
 
256
 
  if (!pc->is_up_choked()) {
257
 
    m_currentlyUnchoked--;
258
 
    m_slotChoke(1);
259
 
  }
260
 
}
261
 
 
262
 
unsigned int
263
 
ChokeManager::choke_range(iterator first, iterator last, unsigned int max) {
264
 
  max = std::min(max, (unsigned int)distance(first, last));
265
 
 
266
 
  std::sort(first, last, choke_manager_read_rate_increasing());
267
 
 
268
 
  iterator split;
269
 
  split = std::stable_partition(first, last, choke_manager_not_recently_unchoked());
270
 
  split = std::find_if(first, split, choke_manager_is_remote_uploading());
271
 
 
272
 
  std::sort(first, split, choke_manager_write_rate_increasing());
273
 
 
274
 
  std::for_each(first, first + max, std::bind2nd(std::mem_fun(&PeerConnectionBase::receive_choke), true));
275
 
 
276
 
  m_currentlyUnchoked -= max;
277
 
  return max;
278
 
}
279
 
  
280
 
unsigned int
281
 
ChokeManager::unchoke_range(iterator first, iterator last, unsigned int max) {
282
 
  std::sort(first, last, choke_manager_read_rate_decreasing());
283
 
 
284
 
  unsigned int count = 0;
285
 
 
286
 
  // Find the split between the ones that are uploading to us, and
287
 
  // those that arn't. When unchoking, circa every third unchoke is of
288
 
  // a connection in the list of those not uploading to us.
289
 
  //
290
 
  // Perhaps we should prefer those we are interested in?
291
 
 
292
 
  iterator split = std::find_if(first, last, choke_manager_is_remote_not_uploading());
293
 
 
294
 
  for ( ; count != max && first != last; count++, first++) {
295
 
 
296
 
    if (split != last &&
297
 
        (*(*first)->peer_chunks()->download_throttle()->rate() < 500 || ::random() % m_generousUnchokes == 0)) {
298
 
      // Use a random connection that is not uploading to us.
299
 
      std::iter_swap(split, split + ::random() % std::distance(split, last));
300
 
      swap_with_shift(first, split++);
301
 
    }
302
 
    
303
 
    (*first)->receive_choke(false);
304
 
  }
305
 
 
306
 
  m_currentlyUnchoked += count;
307
 
  return count;
 
119
ChokeManager::cycle(uint32_t quota) {
 
120
  quota = std::min(quota, m_maxUnchoked);
 
121
 
 
122
  // Does this properly handle 'unlimited' quota?
 
123
  uint32_t oldSize  = m_unchoked.size();
 
124
  uint32_t unchoked = unchoke_range(m_queued.begin(), m_queued.end(),
 
125
                                    std::max<uint32_t>(m_unchoked.size() < quota ? quota - m_unchoked.size() : 0,
 
126
                                                       std::min(quota, max_alternate())));
 
127
 
 
128
  if (m_unchoked.size() > quota)
 
129
    choke_range(m_unchoked.begin(), m_unchoked.end() - unchoked, m_unchoked.size() - quota);
 
130
 
 
131
  if (m_unchoked.size() > quota)
 
132
    throw internal_error("ChokeManager::cycle() m_unchoked.size() > quota.");
 
133
 
 
134
  return m_unchoked.size() - oldSize;
 
135
}
 
136
 
 
137
void
 
138
ChokeManager::set_queued(PeerConnectionBase* pc, ChokeManagerNode* base) {
 
139
  if (base->queued() || base->unchoked())
 
140
    return;
 
141
 
 
142
  base->set_queued(true);
 
143
 
 
144
  if (base->snubbed())
 
145
    return;
 
146
 
 
147
  if ((m_flags & flag_unchoke_all_new || (!is_full() && m_slotCanUnchoke())) &&
 
148
      base->time_last_choke() + rak::timer::from_seconds(10) < cachedTime) {
 
149
    m_unchoked.push_back(value_type(pc, 0));
 
150
    m_slotConnection(pc, false);
 
151
 
 
152
    m_slotUnchoke(1);
 
153
 
 
154
  } else {
 
155
    m_queued.push_back(value_type(pc, 0));
 
156
  }
 
157
}
 
158
 
 
159
void
 
160
ChokeManager::set_not_queued(PeerConnectionBase* pc, ChokeManagerNode* base) {
 
161
  if (!base->queued())
 
162
    return;
 
163
 
 
164
  base->set_queued(false);
 
165
 
 
166
  if (base->snubbed())
 
167
    return;
 
168
 
 
169
  if (base->unchoked()) {
 
170
    choke_manager_erase(&m_unchoked, pc);
 
171
    m_slotConnection(pc, true);
 
172
    m_slotUnchoke(-1);
 
173
 
 
174
  } else {
 
175
    choke_manager_erase(&m_queued, pc);
 
176
  }
 
177
}
 
178
 
 
179
void
 
180
ChokeManager::set_snubbed(PeerConnectionBase* pc, ChokeManagerNode* base) {
 
181
  if (base->snubbed())
 
182
    return;
 
183
 
 
184
  base->set_snubbed(true);
 
185
 
 
186
  if (base->unchoked()) {
 
187
    choke_manager_erase(&m_unchoked, pc);
 
188
    m_slotConnection(pc, true);
 
189
    m_slotUnchoke(-1);
 
190
 
 
191
  } else if (base->queued()) {
 
192
    choke_manager_erase(&m_queued, pc);
 
193
  }
 
194
 
 
195
  base->set_queued(false);
 
196
}
 
197
 
 
198
void
 
199
ChokeManager::set_not_snubbed(PeerConnectionBase* pc, ChokeManagerNode* base) {
 
200
  if (!base->snubbed())
 
201
    return;
 
202
 
 
203
  base->set_snubbed(false);
 
204
 
 
205
  if (!base->queued())
 
206
    return;
 
207
 
 
208
  if (base->unchoked())
 
209
    throw internal_error("ChokeManager::set_not_snubbed(...) base->unchoked().");
 
210
  
 
211
  if ((m_flags & flag_unchoke_all_new || (!is_full() && m_slotCanUnchoke())) &&
 
212
      base->time_last_choke() + rak::timer::from_seconds(10) < cachedTime) {
 
213
    m_unchoked.push_back(value_type(pc, 0));
 
214
    m_slotConnection(pc, false);
 
215
 
 
216
    m_slotUnchoke(1);
 
217
 
 
218
  } else {
 
219
    m_queued.push_back(value_type(pc, 0));
 
220
  }
 
221
}
 
222
 
 
223
// We are no longer in m_connectionList.
 
224
void
 
225
ChokeManager::disconnected(PeerConnectionBase* pc, ChokeManagerNode* base) {
 
226
  if (base->snubbed()) {
 
227
    // Do nothing.
 
228
 
 
229
  } else if (base->unchoked()) {
 
230
    choke_manager_erase(&m_unchoked, pc);
 
231
    m_slotUnchoke(-1);
 
232
 
 
233
  } else if (base->queued()) {
 
234
    choke_manager_erase(&m_queued, pc);
 
235
  }
 
236
 
 
237
  base->set_queued(false);
 
238
}
 
239
 
 
240
struct choke_manager_less {
 
241
  bool operator () (ChokeManager::value_type v1, ChokeManager::value_type v2) const { return v1.second < v2.second; }
 
242
};
 
243
 
 
244
void
 
245
choke_manager_allocate_slots(ChokeManager::iterator first, ChokeManager::iterator last,
 
246
                             uint32_t max, uint32_t* weights, ChokeManager::target_type* target) {
 
247
  std::sort(first, last, choke_manager_less());
 
248
 
 
249
  // 'weightTotal' only contains the weight of targets that have
 
250
  // connections to unchoke. When all connections are in a group are
 
251
  // to be unchoked, then the group's weight is removed.
 
252
  uint32_t weightTotal = 0;
 
253
  uint32_t unchoke = max;
 
254
 
 
255
  target[0].second = first;
 
256
 
 
257
  for (uint32_t i = 0; i < ChokeManager::order_max_size; i++) {
 
258
    target[i].first = 0;
 
259
    target[i + 1].second = std::find_if(target[i].second, last,
 
260
                                        rak::less(i * ChokeManager::order_base + (ChokeManager::order_base - 1),
 
261
                                                  rak::mem_ref(&ChokeManager::value_type::second)));
 
262
 
 
263
    if (std::distance(target[i].second, target[i + 1].second) != 0)
 
264
      weightTotal += weights[i];
 
265
  }
 
266
 
 
267
  // Spread available unchoke slots as long as we can give everyone an
 
268
  // equal share.
 
269
  while (weightTotal != 0 && unchoke / weightTotal > 0) {
 
270
    uint32_t base = unchoke / weightTotal;
 
271
 
 
272
    for (uint32_t itr = 0; itr < ChokeManager::order_max_size; itr++) {
 
273
      uint32_t s = std::distance(target[itr].second, target[itr + 1].second);
 
274
 
 
275
      if (weights[itr] == 0 || target[itr].first >= s)
 
276
        continue;
 
277
      
 
278
      uint32_t u = std::min(s - target[itr].first, base * weights[itr]);
 
279
 
 
280
      unchoke -= u;
 
281
      target[itr].first += u;
 
282
 
 
283
      if (target[itr].first >= s)
 
284
        weightTotal -= weights[itr];
 
285
    }
 
286
  }
 
287
 
 
288
  // Spread the remainder starting from a random position based on the
 
289
  // total weight. This will ensure that aggregated over time we
 
290
  // spread the unchokes equally according to the weight table.
 
291
  if (weightTotal != 0 && unchoke != 0) {
 
292
    uint32_t start = ::random() % weightTotal;
 
293
    unsigned int itr = 0;
 
294
 
 
295
    for ( ; ; itr++) {
 
296
      uint32_t s = std::distance(target[itr].second, target[itr + 1].second);
 
297
 
 
298
      if (weights[itr] == 0 || target[itr].first >= s)
 
299
        continue;
 
300
 
 
301
      if (start < weights[itr])
 
302
        break;
 
303
 
 
304
      start -= weights[itr];
 
305
    }
 
306
 
 
307
    for ( ; weightTotal != 0 && unchoke != 0; itr = (itr + 1) % ChokeManager::order_max_size) {
 
308
      uint32_t s = std::distance(target[itr].second, target[itr + 1].second);
 
309
 
 
310
      if (weights[itr] == 0 || target[itr].first >= s)
 
311
        continue;
 
312
 
 
313
      uint32_t u = std::min(unchoke, std::min(s - target[itr].first, weights[itr] - start));
 
314
 
 
315
      start = 0;
 
316
      unchoke -= u;
 
317
      target[itr].first += u;
 
318
 
 
319
      if (target[itr].first >= s)
 
320
        weightTotal -= weights[itr];
 
321
    }
 
322
  }
 
323
}
 
324
 
 
325
uint32_t
 
326
ChokeManager::choke_range(iterator first, iterator last, uint32_t max) {
 
327
  m_slotChokeWeight(first, last);
 
328
 
 
329
  target_type target[order_max_size + 1];
 
330
  choke_manager_allocate_slots(first, last, max, m_chokeWeight, target);
 
331
 
 
332
  // Now do the actual unchoking.
 
333
  uint32_t count = 0;
 
334
 
 
335
  // Iterate in reverse so that the iterators in 'target' remain vaild
 
336
  // even though we remove random ranges.
 
337
  for (target_type* itr = target + order_max_size; itr != target; itr--) {
 
338
    if ((itr - 1)->first > (uint32_t)std::distance((itr - 1)->second, itr->second))
 
339
      throw internal_error("ChokeManager::choke_range(...) itr->first > std::distance((itr - 1)->second, itr->second).");
 
340
 
 
341
    if (itr->second - (itr - 1)->first > itr->second ||
 
342
        itr->second - (itr - 1)->first < m_unchoked.begin() ||
 
343
        itr->second - (itr - 1)->first > m_unchoked.end() ||
 
344
        (itr - 1)->second < m_unchoked.begin() ||
 
345
        (itr - 1)->second > m_unchoked.end())
 
346
      throw internal_error("ChokeManager::choke_range(...) bad iterator range.");
 
347
 
 
348
    count += (itr - 1)->first;
 
349
 
 
350
    // We move the connections that return true, while the ones that
 
351
    // return false get thrown out. The function called must update
 
352
    // ChunkManager::m_queued if false is returned.
 
353
    //
 
354
    // The C++ standard says std::partition will call the predicate
 
355
    // max 'last - first' times, so we can assume it gets called once
 
356
    // per element.
 
357
    iterator split = std::partition(itr->second - (itr - 1)->first, itr->second,
 
358
                                    rak::on(rak::mem_ref(&value_type::first), std::bind2nd(m_slotConnection, true)));
 
359
 
 
360
    m_queued.insert(m_queued.end(), itr->second - (itr - 1)->first, split);
 
361
    m_unchoked.erase(itr->second - (itr - 1)->first, itr->second);
 
362
  }
 
363
 
 
364
  if (count > max)
 
365
    throw internal_error("ChokeManager::choke_range(...) count > max.");
 
366
 
 
367
  return count;
 
368
}
 
369
  
 
370
uint32_t
 
371
ChokeManager::unchoke_range(iterator first, iterator last, uint32_t max) {
 
372
  m_slotUnchokeWeight(first, last);
 
373
 
 
374
  target_type target[order_max_size + 1];
 
375
  choke_manager_allocate_slots(first, last, max, m_unchokeWeight, target);
 
376
 
 
377
  // Now do the actual unchoking.
 
378
  uint32_t count = 0;
 
379
 
 
380
  for (target_type* itr = target + order_max_size; itr != target; itr--) {
 
381
    if ((itr - 1)->first > (uint32_t)std::distance((itr - 1)->second, itr->second))
 
382
      throw internal_error("ChokeManager::unchoke_range(...) itr->first > std::distance((itr - 1)->second, itr->second).");
 
383
 
 
384
    if (itr->second - (itr - 1)->first > itr->second ||
 
385
        itr->second - (itr - 1)->first < m_queued.begin() ||
 
386
        itr->second - (itr - 1)->first > m_queued.end() ||
 
387
        (itr - 1)->second < m_queued.begin() ||
 
388
        (itr - 1)->second > m_queued.end())
 
389
      throw internal_error("ChokeManager::unchoke_range(...) bad iterator range.");
 
390
 
 
391
    count += (itr - 1)->first;
 
392
 
 
393
    std::for_each(itr->second - (itr - 1)->first, itr->second,
 
394
                  rak::on(rak::mem_ref(&value_type::first), std::bind2nd(m_slotConnection, false)));
 
395
 
 
396
    m_unchoked.insert(m_unchoked.end(), itr->second - (itr - 1)->first, itr->second);
 
397
    m_queued.erase(itr->second - (itr - 1)->first, itr->second);
 
398
  }
 
399
 
 
400
  if (count > max)
 
401
    throw internal_error("ChokeManager::unchoke_range(...) count > max.");
 
402
 
 
403
  return count;
 
404
}
 
405
 
 
406
// Note that these algorithms fail if the rate >= 2^30.
 
407
 
 
408
// Need to add the recently unchoked check here?
 
409
 
 
410
uint32_t weights_upload_choke[ChokeManager::order_max_size]   = { 1, 1, 1, 1 };
 
411
uint32_t weights_upload_unchoke[ChokeManager::order_max_size] = { 1, 3, 9, 0 };
 
412
 
 
413
void
 
414
calculate_upload_choke(ChokeManager::iterator first, ChokeManager::iterator last) {
 
415
  while (first != last) {
 
416
    // Very crude version for now.
 
417
    uint32_t downloadRate = first->first->peer_chunks()->download_throttle()->rate()->rate();
 
418
    first->second = ChokeManager::order_base - 1 - downloadRate;
 
419
 
 
420
    first++;
 
421
  }
 
422
}
 
423
 
 
424
void
 
425
calculate_upload_unchoke(ChokeManager::iterator first, ChokeManager::iterator last) {
 
426
  while (first != last) {
 
427
    if (first->first->is_down_local_unchoked()) {
 
428
      uint32_t downloadRate = first->first->peer_chunks()->download_throttle()->rate()->rate();
 
429
 
 
430
      // If the peer transmits at less than 1KB, we should consider it
 
431
      // to be a rather stingy peer, and should look for new ones.
 
432
 
 
433
      if (downloadRate < 1000)
 
434
        first->second = downloadRate;
 
435
      else
 
436
        first->second = 2 * ChokeManager::order_base + downloadRate;
 
437
 
 
438
    } else {
 
439
      // This will be our optimistic unchoke queue, should be
 
440
      // semi-random. Give lower weights to known stingy peers.
 
441
 
 
442
      first->second = 1 * ChokeManager::order_base + ::random() % (1 << 10);
 
443
    }
 
444
 
 
445
    first++;
 
446
  }
 
447
}
 
448
 
 
449
// Fix this, but for now just use something simple.
 
450
 
 
451
uint32_t weights_download_choke[ChokeManager::order_max_size]   = { 1, 1, 1, 1 };
 
452
uint32_t weights_download_unchoke[ChokeManager::order_max_size] = { 1, 1, 1, 1 };
 
453
 
 
454
void
 
455
calculate_download_choke(ChokeManager::iterator first, ChokeManager::iterator last) {
 
456
  while (first != last) {
 
457
    // Very crude version for now.
 
458
    uint32_t downloadRate = first->first->peer_chunks()->download_throttle()->rate()->rate();
 
459
    first->second = ChokeManager::order_base - 1 - downloadRate;
 
460
 
 
461
    first++;
 
462
  }
 
463
}
 
464
 
 
465
void
 
466
calculate_download_unchoke(ChokeManager::iterator first, ChokeManager::iterator last) {
 
467
  while (first != last) {
 
468
    // Very crude version for now.
 
469
    uint32_t downloadRate = first->first->peer_chunks()->download_throttle()->rate()->rate();
 
470
    first->second = downloadRate;
 
471
 
 
472
    first++;
 
473
  }
308
474
}
309
475
 
310
476
}