class EpollSelectTest(unittest.TestCase): def setUp(self): self.es = EpollSelect() self.server = ForkingTCPServer(("localhost", 0), TCPEcho) self.ip, self.port = self.server.server_address self.server.start() def tearDown(self): self.es.close() self.server.stop() def test_create(self): pass def test_read_one_socket(self): c = socket.create_connection((self.ip, self.port)) ret = self.es.select([c], [], [c], 0.1) self.assertEqual(([], [], []), ret) # socket is ready to send? ret = self.es.select([c], [c], [c], 0.1) self.assertEqual(([], [c], []), ret) # send stuff c.send("Hallo\n") # now we have something to read, right? ret = self.es.select([c], [], [c], 0.5) self.assertEqual(([c], [], []), ret) def test_write_more_sockets(self): c1 = socket.create_connection((self.ip, self.port)) c2 = socket.create_connection((self.ip, self.port)) c3 = socket.create_connection((self.ip, self.port)) # note don't throw away the socket -- else it will be garbage collected raw = c3.fileno() seq = [[c1], [c2], [c1, c2], [c1, c2, raw], [c1], [raw]] check = lambda a, b: self.assertEqual(sort_fdlists(*a), sort_fdlists(*b )) #just the writes for sockets in seq: check(([], sockets, []), self.es.select(sockets, sockets, sockets, 0)) # writes and reads in different order for sockets in seq: check(([], [], []), self.es.select(sockets, [], sockets, 0)) check(([], sockets, []), self.es.select(sockets, sockets, sockets, 0))
class EpollSelectTest(unittest.TestCase): def setUp(self): self.es = EpollSelect() self.server = ForkingTCPServer(("localhost", 0), TCPEcho) self.ip, self.port = self.server.server_address self.server.start() def tearDown(self): self.es.close() self.server.stop() def test_create(self): pass def test_read_one_socket(self): c = socket.create_connection( (self.ip, self.port)) ret = self.es.select([c], [], [c], 0.1) self.assertEqual(([],[],[]), ret) # socket is ready to send? ret = self.es.select([c], [c], [c], 0.1) self.assertEqual(([],[c],[]), ret) # send stuff c.send("Hallo\n") # now we have something to read, right? ret = self.es.select([c], [], [c], 0.5) self.assertEqual(([c],[],[]), ret) def test_write_more_sockets(self): c1 = socket.create_connection( (self.ip, self.port)) c2 = socket.create_connection( (self.ip, self.port)) c3 = socket.create_connection( (self.ip, self.port)) # note don't throw away the socket -- else it will be garbage collected raw = c3.fileno() seq = [ [c1], [c2], [c1,c2], [c1,c2, raw], [c1], [raw]] check = lambda a,b: self.assertEqual(sort_fdlists(*a), sort_fdlists(*b)) #just the writes for sockets in seq: check(([],sockets,[]),self.es.select(sockets, sockets, sockets, 0)) # writes and reads in different order for sockets in seq: check( ([],[],[]), self.es.select(sockets, [], sockets, 0)) check( ([],sockets,[]), self.es.select(sockets, sockets, sockets, 0))
class SelectHub (object): """ This class is a single select() loop that handles all Select() requests for a scheduler as well as timed wakes (i.e., Sleep()). """ def __init__ (self, scheduler, useEpoll=False, threaded=True): # We store tuples of (elapse-time, task) self._incoming = Queue() # Threadsafe queue for new items self._scheduler = scheduler self._pinger = pox.lib.util.makePinger() self.epoll = EpollSelect() if useEpoll else None self._tasks = {} self._thread = None if threaded: self._thread = Thread(target = self._threadProc) self._thread.daemon = True self._thread.start() self._event = threading.Event() def idle (self): """ Called by the scheduler when the scheduler has nothing to do This should block until there's IO or until break_idle(). (Or at least should block up to CYCLE_MAXIMUM) """ if self._thread: # We're running select on another thread self._event.wait(CYCLE_MAXIMUM) # Wait for a while self._event.clear() else: # We're running select on the same thread as scheduler self._select(self._tasks, {}) def break_idle (self): """ Break a call to idle() """ if self._thread: self._event.set() else: self._cycle() def _threadProc (self): tasks = self._tasks rets = {} _select = self._select _scheduler = self._scheduler while not _scheduler._hasQuit: _select(tasks, rets) def _select (self, tasks, rets): #print("SelectHub cycle") #NOTE: Everything you select on eventually boils down to file descriptors, # which are unique, obviously. It might be possible to leverage this # to reduce hashing cost (i.e. by picking a really good hashing # function), though this is complicated by wrappers, etc... rl = {} wl = {} xl = {} timeout = None timeoutTask = None now = time.time() expired = None #TODO: Fix this. It's pretty expensive. There had been some code which # priority heaped this, but I don't think a fully working version # ever quite made it. for t,trl,twl,txl,tto in tasks.itervalues(): if tto != None: if tto <= now: # Already expired if expired is None: expired = [] expired.append(t) if tto-now > 0.1: print("preexpired",tto,now,tto-now) continue tt = tto - now if tt < timeout or timeout is None: timeout = tt timeoutTask = t if trl: for i in trl: rl[i] = t if twl: for i in twl: wl[i] = t if txl: for i in txl: xl[i] = t if expired: for t in expired: del tasks[t] self._return(t, ([],[],[])) if timeout is None: timeout = CYCLE_MAXIMUM if self.epoll: ro, wo, xo = self.epoll.select( rl.keys() + [self._pinger], wl.keys(), xl.keys(), timeout ) else: ro, wo, xo = select.select( rl.keys() + [self._pinger], wl.keys(), xl.keys(), timeout ) if len(ro) == 0 and len(wo) == 0 and len(xo) == 0 and timeoutTask != None: # IO is idle - dispatch timers / release timeouts del tasks[timeoutTask] self._return(timeoutTask, ([],[],[])) else: # We have IO events if self._pinger in ro: self._pinger.pongAll() while not self._incoming.empty(): stuff = self._incoming.get(True) task = stuff[0] assert task not in tasks tasks[task] = stuff self._incoming.task_done() if len(ro) == 1 and len(wo) == 0 and len(xo) == 0: # Just recycle return ro.remove(self._pinger) # At least one thread is going to be resumed for i in ro: task = rl[i] if task not in rets: rets[task] = ([],[],[]) rets[task][0].append(i) for i in wo: task = wl[i] if task not in rets: rets[task] = ([],[],[]) rets[task][1].append(i) for i in xo: task = xl[i] if task not in rets: rets[task] = ([],[],[]) rets[task][2].append(i) for t,v in rets.iteritems(): del tasks[t] self._return(t, v) rets.clear() def registerSelect (self, task, rlist = None, wlist = None, xlist = None, timeout = None, timeIsAbsolute = False): if not timeIsAbsolute: if timeout != None: timeout += time.time() self._incoming.put((task, rlist, wlist, xlist, timeout)) self._cycle() def _cycle (self): """ Cycle the wait thread so that new timers or FDs can be picked up """ self._pinger.ping() def registerTimer (self, task, timeToWake, timeIsAbsolute = False): """ Register a task to be wakened up interval units in the future. It means timeToWake seconds in the future if absoluteTime is False. """ return self.registerSelect(task, None, None, None, timeToWake, timeIsAbsolute) def _return (self, sleepingTask, returnVal): #print("reschedule", sleepingTask) sleepingTask.rv = returnVal self._scheduler.fast_schedule(sleepingTask)
class SelectHub(object): """ This class is a single select() loop that handles all Select() requests for a scheduler as well as timed wakes (i.e., Sleep()). """ def __init__(self, scheduler, useEpoll=False): # We store tuples of (elapse-time, task) self._sleepers = [] # Sleeping items stored as a heap self._incoming = Queue() # Threadsafe queue for new items self._scheduler = scheduler self._pinger = pox.lib.util.makePinger() self.epoll = EpollSelect() if useEpoll else None self._ready = False self._thread = Thread(target=self._threadProc) self._thread.daemon = True self._thread.start() # Ugly busy wait for initialization #while self._ready == False: def _threadProc(self): tasks = {} timeouts = [] rets = {} while self._scheduler._hasQuit == False: #print("SelectHub cycle") if len(timeouts) == 0: timeout = None else: timeout = self._sleepers[0][0] - time.time() if timeout < 0: timeout = 0 #NOTE: Everything you select on eventually boils down to file descriptors, # which are unique, obviously. It might be possible to leverage this # to reduce hashing cost (i.e. by picking a really good hashing # function), though this is complicated by wrappers, etc... rl = {} wl = {} xl = {} timeout = None timeoutTask = None now = time.time() expired = None for t, trl, twl, txl, tto in tasks.itervalues(): if tto != None: if tto <= now: # Already expired if expired is None: expired = [] expired.append(t) if tto - now > 0.1: print("preexpired", tto, now, tto - now) continue tt = tto - now if tt < timeout or timeout is None: timeout = tt timeoutTask = t if trl: for i in trl: rl[i] = t if twl: for i in twl: wl[i] = t if txl: for i in txl: xl[i] = t if expired: for t in expired: del tasks[t] self._return(t, ([], [], [])) if timeout is None: timeout = CYCLE_MAXIMUM if self.epoll: ro, wo, xo = self.epoll.select(rl.keys() + [self._pinger], wl.keys(), xl.keys(), timeout) else: ro, wo, xo = select.select(rl.keys() + [self._pinger], wl.keys(), xl.keys(), timeout) if len(ro) == 0 and len(wo) == 0 and len( xo) == 0 and timeoutTask != None: # IO is idle - dispatch timers / release timeouts del tasks[timeoutTask] self._return(timeoutTask, ([], [], [])) else: # We have IO events if self._pinger in ro: self._pinger.pongAll() while not self._incoming.empty(): stuff = self._incoming.get(True) task = stuff[0] assert task not in tasks tasks[task] = stuff self._incoming.task_done() if len(ro) == 1 and len(wo) == 0 and len(xo) == 0: # Just recycle continue ro.remove(self._pinger) # At least one thread is going to be resumed for i in ro: task = rl[i] if task not in rets: rets[task] = ([], [], []) rets[task][0].append(i) for i in wo: task = wl[i] if task not in rets: rets[task] = ([], [], []) rets[task][1].append(i) for i in xo: task = xl[i] if task not in rets: rets[task] = ([], [], []) rets[task][2].append(i) for t, v in rets.iteritems(): del tasks[t] self._return(t, v) rets.clear() def registerSelect(self, task, rlist=None, wlist=None, xlist=None, timeout=None, timeIsAbsolute=False): if not timeIsAbsolute: if timeout != None: timeout += time.time() self._incoming.put((task, rlist, wlist, xlist, timeout)) self._cycle() def _cycle(self): """ Cycle the wait thread so that new timers or FDs can be picked up """ self._pinger.ping() def registerTimer(self, task, timeToWake, timeIsAbsolute=False): """ Register a task to be wakened up interval units in the future. It means timeToWake seconds in the future if absoluteTime is False. """ return self.registerSelect(task, None, None, None, timeToWake, timeIsAbsolute) def _return(self, sleepingTask, returnVal): #print("reschedule", sleepingTask) sleepingTask.rv = returnVal self._scheduler.fast_schedule(sleepingTask)