def __init__(self, sock = None): """ Initialise a base SocketMultiplexer that uses 'sock' as its ManagedSocket instantiator. """ self._keep_running = False if sock is None: sock = ManagedSocket self._sock = sock self.eq = DeadEventQueue() self._alarm = None self._reads, self._writes = [], []
class SocketMultiplexer(object): """ Abstract socket multiplexer, useful for managing lots of sockets with a single thread. Inherit to create a multiplexed socket based application. You should only override the on***() callback methods. """ # Setup optimised listen queue default # The maximum should be 128 by default under linux 2.0 and up # To check do a 'cat /proc/sys/net/core/somaxconn' LISTEN_QUEUE_MAXIMUM = socket.SOMAXCONN LISTEN_QUEUE_DEFAULT = min(16, socket.SOMAXCONN) class Deadlock(Exception): """ This class represents the occurrence of a deadlock in the event processing system. (It would wait forever on nothing) """ def __init__(self, sock = None): """ Initialise a base SocketMultiplexer that uses 'sock' as its ManagedSocket instantiator. """ self._keep_running = False if sock is None: sock = ManagedSocket self._sock = sock self.eq = DeadEventQueue() self._alarm = None self._reads, self._writes = [], [] def startMultiplex(self): """ Begin multiplexing non-blocking sockets. This call does not return, until either a deadlock exception occurs or stopMultiplex is called. """ self._keep_running = True tick = None try: while self._keep_running: try: # Handle the events system if self.eq.nextEventTicks() is None: tick = None elif tick is None: tick = time() else: newtick = time() if newtick - tick > 0.0: self.eq.elapseTime(newtick - tick) tick = newtick del newtick # Guard against activity deadlocks # They really shouldn't occur, but it is good practice to # catch them. if len(self._reads) + len(self._writes) == 0 and \ self.eq.nextEventTicks() is None: raise SocketMultiplexer.Deadlock("No events left") # Wait for activity reads, writes, excepts = \ select(self._reads, self._writes, [], self.eq.nextEventTicks()) # Handle the events system # I know this isn't the nicest solution, but # this is required to fix a nasty bug triggering over # execution. if self.eq.nextEventTicks() is None: tick = None elif tick is None: tick = time() else: newtick = time() if newtick - tick > 0.0: self.eq.elapseTime(newtick - tick) tick = newtick del newtick except select_error, e: if e.args[0] == errno.EINTR: self.onSignal() continue raise e # Handle reads and writes for r in reads: r.handleRead() for w in writes: w.handleWrite() finally: self._keep_running = False return True def timeFlow(self): """ Executes the flow of time. This function will be used in the future to prevent clock jumps and streamline the events system. """ def stopMultiplex(self): """ Stop multiplexing. """ if not self._keep_running: return False self._keep_running = False return True def connect(self, ip, port, **keywords): """ Initiate a client connection to the specified server. Additionally you can specify 'sock = <some class' in the function call to override the default socket instantiator. Any additional keywords shall be passed on to the socket constructor. """ try: sock = keywords['sock'] del keywords['sock'] except KeyError: sock = self._sock new = sock(self, ip, port, **keywords) new.connect() return True def listen(self, ip, port, queue_length = None, **keywords): """ Create a new socket that will start listening on the specified address. Additionally you can specify 'sock = <some class' in the function call to override the default socket instantiator. Any additional keywords shall be passed on to the socket constructor. """ if queue_length == None: queue_length = SocketMultiplexer.LISTEN_QUEUE_DEFAULT try: sock = keywords['sock'] del keywords['sock'] except KeyError: sock = self._sock new = sock(self, ip, port, **keywords) if not new.listen(queue_length): return False return True def addReader(self, sock): """ Add socket to the list of sockets watched for reading """ if sock in self._reads: return False self._reads.append(sock) return True def delReader(self, sock): """ Delete socket from the list of sockets watched for reading """ try: self._reads.remove(sock) except ValueError: return False return True def addWriter(self, sock): """ Add socket to the list of sockets watched for writing """ if sock in self._writes: return False self._writes.append(sock) return True def delWriter(self, sock): """ Delete socket from the list of sockets watched for writing """ try: self._writes.remove(sock) except ValueError: return False return True def setAlarm(self, seconds): """ Sets an alarm that will occur in 'seconds' time, seconds may be fractional. If seconds is None any pending alarm will be cancelled """ if self._alarm is not None: self.eq.cancelEvent(self._alarm) if seconds is not None: self._alarm = DeferredCall(seconds, self.execAlarm) self.eq.scheduleEvent(self._alarm) return True def execAlarm(self): """ Handler that executes the onAlarm() method. """ self._alarm = None self.onAlarm() def onAlarm(self): """ Called when the alarm set by setAlarm() occurs """ def onSignal(self): """