| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153 |
- # Written by Bram Cohen
- # see LICENSE.txt for license information
- from traceback import print_exc
- from binascii import b2a_hex
- from clock import clock
- from CurrentRateMeasure import Measure
- from cStringIO import StringIO
- from math import sqrt
- try:
- True
- except:
- True = 1
- False = 0
- try:
- sum([1])
- except:
- sum = lambda a: reduce(lambda x,y: x+y, a, 0)
- DEBUG = False
- MAX_RATE_PERIOD = 20.0
- MAX_RATE = 10e10
- PING_BOUNDARY = 1.2
- PING_SAMPLES = 7
- PING_DISCARDS = 1
- PING_THRESHHOLD = 5
- PING_DELAY = 5 # cycles 'til first upward adjustment
- PING_DELAY_NEXT = 2 # 'til next
- ADJUST_UP = 1.05
- ADJUST_DOWN = 0.95
- UP_DELAY_FIRST = 5
- UP_DELAY_NEXT = 2
- SLOTS_STARTING = 6
- SLOTS_FACTOR = 1.66/1000
- class RateLimiter:
- def __init__(self, sched, unitsize, slotsfunc = lambda x: None):
- self.sched = sched
- self.last = None
- self.unitsize = unitsize
- self.slotsfunc = slotsfunc
- self.measure = Measure(MAX_RATE_PERIOD)
- self.autoadjust = False
- self.upload_rate = MAX_RATE * 1000
- self.slots = SLOTS_STARTING # garbage if not automatic
- def set_upload_rate(self, rate):
- # rate = -1 # test automatic
- if rate < 0:
- if self.autoadjust:
- return
- self.autoadjust = True
- self.autoadjustup = 0
- self.pings = []
- rate = MAX_RATE
- self.slots = SLOTS_STARTING
- self.slotsfunc(self.slots)
- else:
- self.autoadjust = False
- if not rate:
- rate = MAX_RATE
- self.upload_rate = rate * 1000
- self.lasttime = clock()
- self.bytes_sent = 0
- def queue(self, conn):
- assert conn.next_upload is None
- if self.last is None:
- self.last = conn
- conn.next_upload = conn
- self.try_send(True)
- else:
- conn.next_upload = self.last.next_upload
- self.last.next_upload = conn
- self.last = conn
- def try_send(self, check_time = False):
- t = clock()
- self.bytes_sent -= (t - self.lasttime) * self.upload_rate
- self.lasttime = t
- if check_time:
- self.bytes_sent = max(self.bytes_sent, 0)
- cur = self.last.next_upload
- while self.bytes_sent <= 0:
- bytes = cur.send_partial(self.unitsize)
- self.bytes_sent += bytes
- self.measure.update_rate(bytes)
- if bytes == 0 or cur.backlogged():
- if self.last is cur:
- self.last = None
- cur.next_upload = None
- break
- else:
- self.last.next_upload = cur.next_upload
- cur.next_upload = None
- cur = self.last.next_upload
- else:
- self.last = cur
- cur = cur.next_upload
- else:
- self.sched(self.try_send, self.bytes_sent / self.upload_rate)
- def adjust_sent(self, bytes):
- self.bytes_sent = min(self.bytes_sent+bytes, self.upload_rate*3)
- self.measure.update_rate(bytes)
- def ping(self, delay):
- if DEBUG:
- print delay
- if not self.autoadjust:
- return
- self.pings.append(delay > PING_BOUNDARY)
- if len(self.pings) < PING_SAMPLES+PING_DISCARDS:
- return
- if DEBUG:
- print 'cycle'
- pings = sum(self.pings[PING_DISCARDS:])
- del self.pings[:]
- if pings >= PING_THRESHHOLD: # assume flooded
- if self.upload_rate == MAX_RATE:
- self.upload_rate = self.measure.get_rate()*ADJUST_DOWN
- else:
- self.upload_rate = min(self.upload_rate,
- self.measure.get_rate()*1.1)
- self.upload_rate = max(int(self.upload_rate*ADJUST_DOWN),2)
- self.slots = int(sqrt(self.upload_rate*SLOTS_FACTOR))
- self.slotsfunc(self.slots)
- if DEBUG:
- print 'adjust down to '+str(self.upload_rate)
- self.lasttime = clock()
- self.bytes_sent = 0
- self.autoadjustup = UP_DELAY_FIRST
- else: # not flooded
- if self.upload_rate == MAX_RATE:
- return
- self.autoadjustup -= 1
- if self.autoadjustup:
- return
- self.upload_rate = int(self.upload_rate*ADJUST_UP)
- self.slots = int(sqrt(self.upload_rate*SLOTS_FACTOR))
- self.slotsfunc(self.slots)
- if DEBUG:
- print 'adjust up to '+str(self.upload_rate)
- self.lasttime = clock()
- self.bytes_sent = 0
- self.autoadjustup = UP_DELAY_NEXT
|