def doWaitForMultipleEvents(self, timeout): log.msg(channel='system', event='iteration', reactor=self) if timeout is None: #timeout = INFINITE timeout = 100 else: timeout = int(timeout * 1000) if not (self._events or self._writes): # sleep so we don't suck up CPU time time.sleep(timeout / 1000.0) return canDoMoreWrites = 0 for fd in self._writes.keys(): if log.callWithLogger(fd, self._runWrite, fd): canDoMoreWrites = 1 if canDoMoreWrites: timeout = 0 handles = self._events.keys() or [self.dummyEvent] val = MsgWaitForMultipleObjects(handles, 0, timeout, QS_ALLINPUT | QS_ALLEVENTS) if val == WAIT_TIMEOUT: return elif val == WAIT_OBJECT_0 + len(handles): exit = win32gui.PumpWaitingMessages() if exit: self.callLater(0, self.stop) return elif val >= WAIT_OBJECT_0 and val < WAIT_OBJECT_0 + len(handles): fd, action = self._events[handles[val - WAIT_OBJECT_0]] log.callWithLogger(fd, self._runAction, action, fd)
def disconnectAll(self): """Disconnect every reader, and writer in the system. """ selectables = self.removeAll() for reader in selectables: log.callWithLogger(reader, reader.connectionLost, failure.Failure(main.CONNECTION_LOST))
def doIteration(self, timeout): # This function sits and waits for an IO completion event. # # There are two requirements: process IO events as soon as they arrive # and process ctrl-break from the user in a reasonable amount of time. # # There are three kinds of waiting. # 1) GetQueuedCompletionStatus (self.port.getEvent) to wait for IO # events only. # 2) Msg* family of wait functions that can stop waiting when # ctrl-break is detected (then, I think, Python converts it into a # KeyboardInterrupt) # 3) *Ex family of wait functions that put the thread into an # "alertable" wait state which is supposedly triggered by IO completion # # 2) and 3) can be combined. Trouble is, my IO completion is not # causing 3) to trigger, possibly because I do not use an IO completion # callback. Windows is weird. # There are two ways to handle this. I could use MsgWaitForSingleObject # here and GetQueuedCompletionStatus in a thread. Or I could poll with # a reasonable interval. Guess what! Threads are hard. processed_events = 0 if timeout is None: timeout = MAX_TIMEOUT else: timeout = min(MAX_TIMEOUT, int(1000*timeout)) rc, bytes, key, evt = self.port.getEvent(timeout) while processed_events < EVENTS_PER_LOOP: if rc == WAIT_TIMEOUT: break if key != KEY_WAKEUP: assert key == KEY_NORMAL if not evt.ignore: log.callWithLogger(evt.owner, self._callEventCallback, rc, bytes, evt) processed_events += 1 rc, bytes, key, evt = self.port.getEvent(0)
def doIteration(self, timeout): # This function sits and waits for an IO completion event. # # There are two requirements: process IO events as soon as they arrive # and process ctrl-break from the user in a reasonable amount of time. # # There are three kinds of waiting. # 1) GetQueuedCompletionStatus (self.port.getEvent) to wait for IO # events only. # 2) Msg* family of wait functions that can stop waiting when # ctrl-break is detected (then, I think, Python converts it into a # KeyboardInterrupt) # 3) *Ex family of wait functions that put the thread into an # "alertable" wait state which is supposedly triggered by IO completion # # 2) and 3) can be combined. Trouble is, my IO completion is not # causing 3) to trigger, possibly because I do not use an IO completion # callback. Windows is weird. # There are two ways to handle this. I could use MsgWaitForSingleObject # here and GetQueuedCompletionStatus in a thread. Or I could poll with # a reasonable interval. Guess what! Threads are hard. processed_events = 0 if timeout is None: timeout = MAX_TIMEOUT else: timeout = min(MAX_TIMEOUT, int(1000 * timeout)) rc, bytes, key, evt = self.port.getEvent(timeout) while processed_events < EVENTS_PER_LOOP: if rc == WAIT_TIMEOUT: break if key != KEY_WAKEUP: assert key == KEY_NORMAL if not evt.ignore: log.callWithLogger(evt.owner, self._callEventCallback, rc, bytes, evt) processed_events += 1 rc, bytes, key, evt = self.port.getEvent(0)
def callback(self, source, condition): log.callWithLogger(source, self._readAndWrite, source, condition) self.simulate() # fire Twisted timers return 1 # 1=don't auto-remove the source
class KQueueReactor(posixbase.PosixReactorBase): """ A reactor that uses kqueue(2)/kevent(2). @ivar _kq: A L{kqueue} which will be used to check for I/O readiness. @ivar _selectables: A dictionary mapping integer file descriptors to instances of L{FileDescriptor} which have been registered with the reactor. All L{FileDescriptors} which are currently receiving read or write readiness notifications will be present as values in this dictionary. @ivar _reads: A dictionary mapping integer file descriptors to arbitrary values (this is essentially a set). Keys in this dictionary will be registered with C{_kq} for read readiness notifications which will be dispatched to the corresponding L{FileDescriptor} instances in C{_selectables}. @ivar _writes: A dictionary mapping integer file descriptors to arbitrary values (this is essentially a set). Keys in this dictionary will be registered with C{_kq} for write readiness notifications which will be dispatched to the corresponding L{FileDescriptor} instances in C{_selectables}. """ implements(IReactorFDSet) def __init__(self): """ Initialize kqueue object, file descriptor tracking dictionaries, and the base class. """ self._kq = kqueue() self._reads = {} self._writes = {} self._selectables = {} posixbase.PosixReactorBase.__init__(self) def _updateRegistration(self, *args): self._kq.kevent([kevent(*args)], 0, 0) def addReader(self, reader): """Add a FileDescriptor for notification of data available to read. """ fd = reader.fileno() if fd not in self._reads: self._selectables[fd] = reader self._reads[fd] = 1 self._updateRegistration(fd, EVFILT_READ, EV_ADD) def addWriter(self, writer): """Add a FileDescriptor for notification of data available to write. """ fd = writer.fileno() if fd not in self._writes: self._selectables[fd] = writer self._writes[fd] = 1 self._updateRegistration(fd, EVFILT_WRITE, EV_ADD) def removeReader(self, reader): """Remove a Selectable for notification of data available to read. """ fd = reader.fileno() if fd in self._reads: del self._reads[fd] if fd not in self._writes: del self._selectables[fd] self._updateRegistration(fd, EVFILT_READ, EV_DELETE) def removeWriter(self, writer): """Remove a Selectable for notification of data available to write. """ fd = writer.fileno() if fd in self._writes: del self._writes[fd] if fd not in self._reads: del self._selectables[fd] self._updateRegistration(fd, EVFILT_WRITE, EV_DELETE) def removeAll(self): """ Remove all selectables, and return a list of them. """ return self._removeAll([self._selectables[fd] for fd in self._reads], [self._selectables[fd] for fd in self._writes]) def getReaders(self): return [self._selectables[fd] for fd in self._reads] def getWriters(self): return [self._selectables[fd] for fd in self._writes] def doKEvent(self, timeout): """Poll the kqueue for new events.""" if timeout is None: timeout = 1000 else: timeout = int(timeout * 1000) # convert seconds to milliseconds try: l = self._kq.kevent([], len(self._selectables), timeout) except OSError, e: if e[0] == errno.EINTR: return else: raise _drdw = self._doWriteOrRead for event in l: why = None fd, filter = event.ident, event.filter try: selectable = self._selectables[fd] except KeyError: # Handles the infrequent case where one selectable's # handler disconnects another. continue log.callWithLogger(selectable, _drdw, selectable, fd, filter)
class EPollReactor(posixbase.PosixReactorBase): """ A reactor that uses epoll(4). @ivar _poller: A L{poll} which will be used to check for I/O readiness. @ivar _selectables: A dictionary mapping integer file descriptors to instances of L{FileDescriptor} which have been registered with the reactor. All L{FileDescriptors} which are currently receiving read or write readiness notifications will be present as values in this dictionary. @ivar _reads: A dictionary mapping integer file descriptors to arbitrary values (this is essentially a set). Keys in this dictionary will be registered with C{_poller} for read readiness notifications which will be dispatched to the corresponding L{FileDescriptor} instances in C{_selectables}. @ivar _writes: A dictionary mapping integer file descriptors to arbitrary values (this is essentially a set). Keys in this dictionary will be registered with C{_poller} for write readiness notifications which will be dispatched to the corresponding L{FileDescriptor} instances in C{_selectables}. """ implements(IReactorFDSet) def __init__(self): """ Initialize epoll object, file descriptor tracking dictionaries, and the base class. """ # Create the poller we're going to use. The 1024 here is just a hint # to the kernel, it is not a hard maximum. self._poller = _epoll.epoll(1024) self._reads = {} self._writes = {} self._selectables = {} posixbase.PosixReactorBase.__init__(self) def _add(self, xer, primary, other, selectables, event, antievent): """ Private method for adding a descriptor from the event loop. It takes care of adding it if new or modifying it if already added for another state (read -> read/write for example). """ fd = xer.fileno() if fd not in primary: cmd = _epoll.CTL_ADD flags = event if fd in other: flags |= antievent cmd = _epoll.CTL_MOD # epoll_ctl can raise all kinds of IOErrors, and every one # indicates a bug either in the reactor or application-code. # Let them all through so someone sees a traceback and fixes # something. We'll do the same thing for every other call to # this method in this file. self._poller._control(cmd, fd, flags) # Update our own tracking state *only* after the epoll call has # succeeded. Otherwise we may get out of sync. primary[fd] = 1 selectables[fd] = xer def addReader(self, reader): """ Add a FileDescriptor for notification of data available to read. """ self._add(reader, self._reads, self._writes, self._selectables, _epoll.IN, _epoll.OUT) def addWriter(self, writer): """ Add a FileDescriptor for notification of data available to write. """ self._add(writer, self._writes, self._reads, self._selectables, _epoll.OUT, _epoll.IN) def _remove(self, xer, primary, other, selectables, event, antievent): """ Private method for removing a descriptor from the event loop. It does the inverse job of _add, and also add a check in case of the fd has gone away. """ fd = xer.fileno() if fd == -1: for fd, fdes in selectables.items(): if xer is fdes: break else: return if fd in primary: cmd = _epoll.CTL_DEL flags = event if fd in other: flags = antievent cmd = _epoll.CTL_MOD else: del selectables[fd] del primary[fd] # See comment above _control call in _add. self._poller._control(cmd, fd, flags) def removeReader(self, reader): """ Remove a Selectable for notification of data available to read. """ self._remove(reader, self._reads, self._writes, self._selectables, _epoll.IN, _epoll.OUT) def removeWriter(self, writer): """ Remove a Selectable for notification of data available to write. """ self._remove(writer, self._writes, self._reads, self._selectables, _epoll.OUT, _epoll.IN) def removeAll(self): """ Remove all selectables, and return a list of them. """ return self._removeAll([self._selectables[fd] for fd in self._reads], [self._selectables[fd] for fd in self._writes]) def getReaders(self): return [self._selectables[fd] for fd in self._reads] def getWriters(self): return [self._selectables[fd] for fd in self._writes] def doPoll(self, timeout): """ Poll the poller for new events. """ if timeout is None: timeout = 1 timeout = int(timeout * 1000) # convert seconds to milliseconds try: # Limit the number of events to the number of io objects we're # currently tracking (because that's maybe a good heuristic) and # the amount of time we block to the value specified by our # caller. l = self._poller.wait(len(self._selectables), timeout) except IOError, err: if err.errno == errno.EINTR: return # See epoll_wait(2) for documentation on the other conditions # under which this can fail. They can only be due to a serious # programming error on our part, so let's just announce them # loudly. raise _drdw = self._doReadOrWrite for fd, event in l: try: selectable = self._selectables[fd] except KeyError: pass else: log.callWithLogger(selectable, _drdw, selectable, fd, event)
def callback(self, source, condition): log.callWithLogger(source, self._doReadOrWrite, source, condition) self.simulate() # fire Twisted timers return 1 # 1=don't auto-remove the source
class PollReactor(posixbase.PosixReactorBase): """ A reactor that uses poll(2). @ivar _poller: A L{poll} which will be used to check for I/O readiness. @ivar _selectables: A dictionary mapping integer file descriptors to instances of L{FileDescriptor} which have been registered with the reactor. All L{FileDescriptors} which are currently receiving read or write readiness notifications will be present as values in this dictionary. @ivar _reads: A dictionary mapping integer file descriptors to arbitrary values (this is essentially a set). Keys in this dictionary will be registered with C{_poller} for read readiness notifications which will be dispatched to the corresponding L{FileDescriptor} instances in C{_selectables}. @ivar _writes: A dictionary mapping integer file descriptors to arbitrary values (this is essentially a set). Keys in this dictionary will be registered with C{_poller} for write readiness notifications which will be dispatched to the corresponding L{FileDescriptor} instances in C{_selectables}. """ implements(IReactorFDSet) def __init__(self): """ Initialize polling object, file descriptor tracking dictionaries, and the base class. """ self._poller = poll() self._selectables = {} self._reads = {} self._writes = {} posixbase.PosixReactorBase.__init__(self) def _updateRegistration(self, fd): """Register/unregister an fd with the poller.""" try: self._poller.unregister(fd) except KeyError: pass mask = 0 if fd in self._reads: mask = mask | POLLIN if fd in self._writes: mask = mask | POLLOUT if mask != 0: self._poller.register(fd, mask) else: if fd in self._selectables: del self._selectables[fd] def _dictRemove(self, selectable, mdict): try: # the easy way fd = selectable.fileno() # make sure the fd is actually real. In some situations we can get # -1 here. mdict[fd] except: # the hard way: necessary because fileno() may disappear at any # moment, thanks to python's underlying sockets impl for fd, fdes in self._selectables.items(): if selectable is fdes: break else: # Hmm, maybe not the right course of action? This method can't # fail, because it happens inside error detection... return if fd in mdict: del mdict[fd] self._updateRegistration(fd) def addReader(self, reader): """Add a FileDescriptor for notification of data available to read. """ fd = reader.fileno() if fd not in self._reads: self._selectables[fd] = reader self._reads[fd] = 1 self._updateRegistration(fd) def addWriter(self, writer): """Add a FileDescriptor for notification of data available to write. """ fd = writer.fileno() if fd not in self._writes: self._selectables[fd] = writer self._writes[fd] = 1 self._updateRegistration(fd) def removeReader(self, reader): """Remove a Selectable for notification of data available to read. """ return self._dictRemove(reader, self._reads) def removeWriter(self, writer): """Remove a Selectable for notification of data available to write. """ return self._dictRemove(writer, self._writes) def removeAll(self): """ Remove all selectables, and return a list of them. """ return self._removeAll( [self._selectables[fd] for fd in self._reads], [self._selectables[fd] for fd in self._writes]) def doPoll(self, timeout): """Poll the poller for new events.""" if timeout is not None: timeout = int(timeout * 1000) # convert seconds to milliseconds try: l = self._poller.poll(timeout) except SelectError, e: if e[0] == errno.EINTR: return else: raise _drdw = self._doReadOrWrite for fd, event in l: try: selectable = self._selectables[fd] except KeyError: # Handles the infrequent case where one selectable's # handler disconnects another. continue log.callWithLogger(selectable, _drdw, selectable, fd, event)