Blame | Last modification | View Log | Download
# Written by Bram Cohen# see LICENSE.txt for license informationfrom random import randrange, shufflefrom BitTornado.clock import clocktry:Trueexcept:True = 1False = 0class Choker:def __init__(self, config, schedule, picker, done = lambda: False):self.config = configself.round_robin_period = config['round_robin_period']self.schedule = scheduleself.picker = pickerself.connections = []self.last_preferred = 0self.last_round_robin = clock()self.done = doneself.super_seed = Falseself.paused = Falseschedule(self._round_robin, 5)def set_round_robin_period(self, x):self.round_robin_period = xdef _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_preferredif count > 0: # optimizationshuffle(cons)for c in cons:i = self.picker.next_have(self.connections[c], count > 0)if i is None:continueif i < 0:to_close.append(self.connections[c])continueself.connections[c].send_have(i)count -= 1for 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]breakself._rechoke()def _rechoke(self):preferred = []maxuploads = self.config['max_uploads']if self.paused:for c in self.connections:c.get_upload().choke()returnif maxuploads > 1:for c in self.connections:u = c.get_upload()if not u.is_interested():continueif self.done():r = u.get_rate()else:d = c.get_download()r = d.get_rate()if r < 1000 or d.is_snubbed():continuepreferred.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 = Falseto_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 += 1hit = Trueelse: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 connectionsself.connections[0].close()self.picker.set_superseed()self.super_seed = Truedef pause(self, flag):self.paused = flagself._rechoke()