| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128 |
- # Written by Bram Cohen
- # see LICENSE.txt for license information
- from random import randrange, shuffle
- from BitTornado.clock import clock
- try:
- True
- except:
- True = 1
- False = 0
- class Choker:
- def __init__(self, config, schedule, picker, done = lambda: False):
- self.config = config
- self.round_robin_period = config['round_robin_period']
- self.schedule = schedule
- self.picker = picker
- self.connections = []
- self.last_preferred = 0
- self.last_round_robin = clock()
- self.done = done
- self.super_seed = False
- self.paused = False
- schedule(self._round_robin, 5)
- def set_round_robin_period(self, x):
- self.round_robin_period = x
- def _round_robin(self):
- self.schedule(self._round_robin, 5)
- if self.super_seed:
- cons = range(len(self.connections))
- to_close = []
- count = self.config['min_uploads']-self.last_preferred
- if count > 0: # optimization
- shuffle(cons)
- for c in cons:
- i = self.picker.next_have(self.connections[c], count > 0)
- if i is None:
- continue
- if i < 0:
- to_close.append(self.connections[c])
- continue
- self.connections[c].send_have(i)
- count -= 1
- for c in to_close:
- c.close()
- if self.last_round_robin + self.round_robin_period < clock():
- self.last_round_robin = clock()
- for i in xrange(1, len(self.connections)):
- c = self.connections[i]
- u = c.get_upload()
- if u.is_choked() and u.is_interested():
- self.connections = self.connections[i:] + self.connections[:i]
- break
- self._rechoke()
- def _rechoke(self):
- preferred = []
- maxuploads = self.config['max_uploads']
- if self.paused:
- for c in self.connections:
- c.get_upload().choke()
- return
- if maxuploads > 1:
- for c in self.connections:
- u = c.get_upload()
- if not u.is_interested():
- continue
- if self.done():
- r = u.get_rate()
- else:
- d = c.get_download()
- r = d.get_rate()
- if r < 1000 or d.is_snubbed():
- continue
- preferred.append((-r, c))
- self.last_preferred = len(preferred)
- preferred.sort()
- del preferred[maxuploads-1:]
- preferred = [x[1] for x in preferred]
- count = len(preferred)
- hit = False
- to_unchoke = []
- for c in self.connections:
- u = c.get_upload()
- if c in preferred:
- to_unchoke.append(u)
- else:
- if count < maxuploads or not hit:
- to_unchoke.append(u)
- if u.is_interested():
- count += 1
- hit = True
- else:
- u.choke()
- for u in to_unchoke:
- u.unchoke()
- def connection_made(self, connection, p = None):
- if p is None:
- p = randrange(-2, len(self.connections) + 1)
- self.connections.insert(max(p, 0), connection)
- self._rechoke()
- def connection_lost(self, connection):
- self.connections.remove(connection)
- self.picker.lost_peer(connection)
- if connection.get_upload().is_interested() and not connection.get_upload().is_choked():
- self._rechoke()
- def interested(self, connection):
- if not connection.get_upload().is_choked():
- self._rechoke()
- def not_interested(self, connection):
- if not connection.get_upload().is_choked():
- self._rechoke()
- def set_super_seed(self):
- while self.connections: # close all connections
- self.connections[0].close()
- self.picker.set_superseed()
- self.super_seed = True
- def pause(self, flag):
- self.paused = flag
- self._rechoke()
|