1
# Written by Bram Cohen
2
# see LICENSE.txt for license information
4
from random import randrange
7
def __init__(self, max_uploads, schedule, done = lambda: False, min_uploads = None):
8
self.max_uploads = max_uploads
9
if min_uploads is None:
10
min_uploads = max_uploads
11
self.min_uploads = min_uploads
12
self.schedule = schedule
16
schedule(self._round_robin, 10)
18
def _round_robin(self):
19
self.schedule(self._round_robin, 10)
21
if self.count % 3 == 0:
22
for i in xrange(len(self.connections)):
23
u = self.connections[i].get_upload()
24
if u.is_choked() and u.is_interested():
25
self.connections = self.connections[i:] + self.connections[:i]
29
def _snubbed(self, c):
32
return c.get_download().is_snubbed()
36
return c.get_upload().get_rate()
38
return c.get_download().get_rate()
42
for c in self.connections:
43
if not self._snubbed(c) and c.get_upload().is_interested():
44
preferred.append((-self._rate(c), c))
46
del preferred[self.max_uploads - 1:]
47
preferred = [x[1] for x in preferred]
48
count = len(preferred)
50
for c in self.connections:
55
if count < self.min_uploads or not hit:
63
def connection_made(self, connection, p = None):
65
p = randrange(-2, len(self.connections) + 1)
66
self.connections.insert(max(p, 0), connection)
69
def connection_lost(self, connection):
70
self.connections.remove(connection)
71
if connection.get_upload().is_interested() and not connection.get_upload().is_choked():
74
def interested(self, connection):
75
if not connection.get_upload().is_choked():
78
def not_interested(self, connection):
79
if not connection.get_upload().is_choked():
82
def change_max_uploads(self, newval):
83
def foo(self=self, newval=newval):
84
self._change_max_uploads(newval)
85
self.schedule(foo, 0);
87
def _change_max_uploads(self, newval):
88
self.max_uploads = newval
95
def __call__(self, func, delay):
96
self.s.append((func, delay))
98
class DummyConnection:
99
def __init__(self, v = 0):
100
self.u = DummyUploader()
101
self.d = DummyDownloader(self)
104
def get_upload(self):
107
def get_download(self):
110
class DummyDownloader:
111
def __init__(self, c):
115
def is_snubbed(self):
137
def is_interested(self):
140
def test_round_robin_with_no_downloads():
144
assert s.s[0][1] == 10
148
assert s.s[0][1] == 10
158
choker = Choker(1, s)
159
c1 = DummyConnection()
160
c2 = DummyConnection(1)
161
c3 = DummyConnection(2)
162
c4 = DummyConnection(3)
165
choker.connection_made(c1)
167
choker.connection_made(c2, 1)
170
choker.connection_made(c3, 1)
176
choker.connection_made(c4, 1)
181
choker.connection_lost(c4)
192
choker = Choker(1, s)
193
c1 = DummyConnection()
194
c2 = DummyConnection(1)
195
c3 = DummyConnection(2)
198
choker.connection_made(c1)
200
choker.connection_made(c2, 1)
203
choker.connection_made(c3, 1)
208
choker.not_interested(c3)
213
choker.interested(c3)
217
choker.connection_lost(c3)
221
def test_robin_interest():
223
choker = Choker(1, s)
224
c1 = DummyConnection(0)
225
c2 = DummyConnection(1)
227
choker.connection_made(c2)
229
choker.connection_made(c1, 0)
233
choker.not_interested(c1)
237
choker.interested(c1)
240
choker.connection_lost(c1)
243
def test_skip_not_interested():
245
choker = Choker(1, s)
246
c1 = DummyConnection(0)
247
c2 = DummyConnection(1)
248
c3 = DummyConnection(2)
251
choker.connection_made(c2)
253
choker.connection_made(c1, 0)
256
choker.connection_made(c3, 2)
274
def test_connection_lost_no_interrupt():
276
choker = Choker(1, s)
277
c1 = DummyConnection(0)
278
c2 = DummyConnection(1)
279
c3 = DummyConnection(2)
283
choker.connection_made(c1)
284
choker.connection_made(c2, 1)
285
choker.connection_made(c3, 2)
307
choker.connection_lost(c3)
313
choker.connection_lost(c2)
316
def test_connection_made_no_interrupt():
318
choker = Choker(1, s)
319
c1 = DummyConnection(0)
320
c2 = DummyConnection(1)
321
c3 = DummyConnection(2)
325
choker.connection_made(c1)
326
choker.connection_made(c2, 1)
336
choker.connection_made(c3, 1)
345
def test_round_robin():
347
choker = Choker(1, s)
348
c1 = DummyConnection(0)
349
c2 = DummyConnection(1)
352
choker.connection_made(c1)
353
choker.connection_made(c2, 1)
378
choker = Choker(4, s)
379
c1 = DummyConnection(0)
380
c2 = DummyConnection(0)
381
c3 = DummyConnection(0)
382
c4 = DummyConnection(8)
383
c5 = DummyConnection(0)
384
c6 = DummyConnection(0)
385
c7 = DummyConnection(6)
386
c8 = DummyConnection(0)
387
c9 = DummyConnection(9)
388
c10 = DummyConnection(7)
389
c11 = DummyConnection(10)
390
choker.connection_made(c1, 0)
391
choker.connection_made(c2, 1)
392
choker.connection_made(c3, 2)
393
choker.connection_made(c4, 3)
394
choker.connection_made(c5, 4)
395
choker.connection_made(c6, 5)
396
choker.connection_made(c7, 6)
397
choker.connection_made(c8, 7)
398
choker.connection_made(c9, 8)
399
choker.connection_made(c10, 9)
400
choker.connection_made(c11, 10)