def _setNextRotation(self, now=None): """Helper function: calculate the time when we next rotate the log.""" # ???? Lock to 24-hour cycle # This is a little weird. We won't save *until*: # - .75 * rotateInterval seconds are accumulated. # AND - rotateInterval seconds have elapsed since the last # rotation. # # IF the rotation interval is divisible by one hour, we also # round to the hour, up to 5 minutes down and 55 up. if not now: now = time() accumulatedTime = self.accumulatedTime + (now - self.lastSave) secToGo = max(0, self.rotateInterval * 0.75 - accumulatedTime) self.nextRotation = max(self.lastRotation + self.rotateInterval, now + secToGo) if self.nextRotation < now: self.nextRotation = now if (self.rotateInterval % 3600) == 0: mid = previousMidnight(self.nextRotation) rest = self.nextRotation - mid self.nextRotation = mid + 3600 * floorDiv(rest+55*60, 3600)
def _setNextRotation(self, now=None): """Helper function: calculate the time when we next rotate the log.""" # ???? Lock to 24-hour cycle # This is a little weird. We won't save *until*: # - .75 * rotateInterval seconds are accumulated. # AND - rotateInterval seconds have elapsed since the last # rotation. # # IF the rotation interval is divisible by one hour, we also # round to the hour, up to 5 minutes down and 55 up. if not now: now = time() accumulatedTime = self.accumulatedTime + (now - self.lastSave) secToGo = max(0, self.rotateInterval * 0.75 - accumulatedTime) self.nextRotation = max(self.lastRotation + self.rotateInterval, now + secToGo) if self.nextRotation < now: self.nextRotation = now if (self.rotateInterval % 3600) == 0: mid = previousMidnight(self.nextRotation) rest = self.nextRotation - mid self.nextRotation = mid + 3600 * floorDiv(rest + 55 * 60, 3600)
class PollAsyncServer(SelectAsyncServer): """Subclass of SelectAsyncServer that uses 'poll' where available. This is more efficient, but less universal.""" def __init__(self): SelectAsyncServer.__init__(self) self.poll = select.poll() self.EVENT_MASK = { (0, 0): 0, (1, 0): select.POLLIN + select.POLLERR, (0, 1): select.POLLOUT + select.POLLERR, (0, 2): select.POLLOUT + select.POLLERR, (1, 1): select.POLLIN + select.POLLOUT + select.POLLERR, (1, 2): select.POLLIN + select.POLLOUT + select.POLLERR } def process(self, timeout): if self.bucket is not None and self.bucket <= 0: time.sleep(timeout) return try: # (watch out: poll takes a timeout in msec, but select takes a # timeout in sec.) events = self.poll.poll(timeout * 1000) except select.error, e: if e[0] == errno.EINTR: return else: raise e if not events: return if self.bucket is None: cap = None else: cap = floorDiv(self.bucket, len(events)) #print events, self.connections.keys() for fd, mask in events: c = self.connections[fd] wr, ww, isopen, n = c.process( mask & select.POLLIN, mask & select.POLLOUT, mask & (select.POLLERR | select.POLLHUP), cap) if cap is not None: self.bucket -= n if not isopen: #print "unregister",fd self.poll.unregister(fd) del self.connections[fd] continue #print "register",fd self.poll.register(fd, self.EVENT_MASK[wr, ww])
for s in directory.getAllServers(): d = s.getDigest() serversByDir[fp].append(d) if not serverMap.has_key(d): serverMap[d] = s del directory.servers[:] # Save RAM if goodDirectories.has_key(fp): log.warn("Multiple directories with fingerprint %s; ignoring one from %s", fp, goodDirectories[fp][0]) goodDirectories[fp] = (src, directory) goodVotes = [ v for _,v in goodDirectories.values() ] # Next -- what is the result of the vote? (easy cases) threshold = floorDiv(len(voters)+1, 2) includedClientVersions = commonElements( [ d['Recommended-Software']['MixminionClient'] for d in goodVotes ], threshold) includedServerVersions = commonElements( [ d['Recommended-Software']['MixminionServer'] for d in goodVotes ], threshold) includedRecommended = commonElements( [ d['Directory-Info']['Recommended-Servers'] for d in goodVotes ], threshold) # Hard part -- what servers go in? # Identities go in if they have a consistant nickname, and most voters # include them. identNickname = {}
for s in directory.getAllServers(): d = s.getDigest() serversByDir[fp].append(d) if not serverMap.has_key(d): serverMap[d] = s del directory.servers[:] # Save RAM if goodDirectories.has_key(fp): LOG.warn("Multiple directories with fingerprint %s; ignoring one from %s", fp, goodDirectories[fp][0]) goodDirectories[fp] = (src, directory) goodVotes = [ v for _,v in goodDirectories.values() ] # Next -- what is the result of the vote? (easy cases) threshold = floorDiv(len(voters)+1, 2) includedClientVersions = commonElements( [ d['Recommended-Software']['MixminionClient'] for d in goodVotes ], threshold) includedServerVersions = commonElements( [ d['Recommended-Software']['MixminionServer'] for d in goodVotes ], threshold) includedRecommended = commonElements( [ d['Directory-Info']['Recommended-Servers'] for d in goodVotes ], threshold) # Hard part -- what servers go in? # Identities go in if they have a consistant nickname, and most voters # include them. identNickname = {}
class SelectAsyncServer: """AsyncServer is the core of a general-purpose asynchronous select-based server loop. AsyncServer maintains lists of Connection objects that are waiting for reads and writes (respectively), and waits for their underlying sockets to be available for the desired operations. """ ## Fields: # self.connections: a map from fd to Connection objects. # self.state: a map from fd to the latest wantRead,wantWrite tuples # returned by the connection objects' process or getStatus methods. # self.bandwidthPerTick: How many bytes of bandwidth do we use per tick, # on average? # self.maxBucket: How many bytes of bandwidth are we willing to use in # a single 1-tick burst? # self.bucket: How many bytes are we willing to use in the next tick? # # (NOTE: if no bandwidth limitation is used, the 3 fields above are # set to None.) # How many seconds pass between the 'ticks' at which we increment # our bandwidth bucket? TICK_INTERVAL = 1.0 def __init__(self): """Create a new AsyncServer with no readers or writers.""" self._timeout = None self.connections = {} self.state = {} self.bandwidthPerTick = self.bucket = self.maxBucket = None def process(self, timeout): """If any relevant file descriptors become available within 'timeout' seconds, call the appropriate methods on their connections and return immediately after. Otherwise, wait 'timeout' seconds and return. If we receive an unblocked signal, return immediately. """ readfds = [] writefds = [] exfds = [] for fd, (wr, ww) in self.state.items(): if wr: readfds.append(fd) if ww == 2: exfds.append(fd) if ww: writefds.append(fd) if not (readfds or writefds or exfds): # Windows 'select' doesn't timeout properly when we aren't # selecting on any FDs. This should never happen to us, # but we'll check for it anyway. time.sleep(timeout) return if self.bucket is not None and self.bucket <= 0: time.sleep(timeout) return try: readfds, writefds, exfds = select.select(readfds, writefds, exfds, timeout) except select.error, e: if e[0] == errno.EINTR: return else: raise e writefds += exfds active = [] for fd, c in self.connections.items(): r = fd in readfds w = fd in writefds if not (r or w): continue active.append((c, r, w, fd)) if not active: return if self.bucket is None: cap = None else: cap = floorDiv(self.bucket, len(active)) for c, r, w, fd in active: wr, ww, isopen, nbytes = c.process(r, w, 0, cap) if cap is not None: self.bucket -= nbytes if not isopen: del self.connections[fd] del self.state[fd] continue self.state[fd] = (wr, ww)