[Twisted-Python] Win32 events main loop

Andrew Bennetts andrew-twisted at puzzling.org
Sun Mar 3 23:11:22 MST 2002


Hi all,

Here is the first *rough* cut of the Twisted main loop using Win32 events,
based upon internet.poll.

It seems to be okay for simpleserv.py, but I haven't tested it for anything
else.  I'll leave that to a day when I'm not feeling so ill...  I pretty
much guarantee it is mostly broken -- think of it as a proof of concept.

I think if I experiment with MsgWaitForMultipleObjects instead of
WaitForMultipleObjects, I can make it respond to Win32 window messages
properly; e.g. Ctrl-C and stopping when running as a service might work
nicely -- COM stuff would probably benefit too.

Enjoy!

-Andrew.

--- win32.py ---

# This library is free software; you can redistribute it and/or
# modify it under the terms of version 2.1 of the GNU Lesser General Public
# License as published by the Free Software Foundation.
# 
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.
# 
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

"""A win32event based implementation of the twisted main loop.

To install the event loop (and you should do this before any connections,
listeners or connectors are added):

    from twisted.internet import win32
    win32.install()

"""

# Win32 imports
from win32file import WSAEventSelect, FD_READ, FD_WRITE, FD_CLOSE, \
                      FD_ACCEPT, FD_CONNECT
from win32event import CreateEvent, WaitForMultipleObjects, \
                       WAIT_OBJECT_0, WAIT_TIMEOUT, INFINITE

# Twisted imports
from twisted.python import log, threadable
import main

# globals
reads = {}
writes = {}
events = {}

def _addEvent(fd, why, events=events):
    print '_addEvent:', fd, why
    event = CreateEvent(None, 0, 0, None)
    WSAEventSelect(fd, event, why)
    events[event] = (fd, why)
    return event

def addReader(reader, reads=reads):
    """Add a FileDescriptor for notification of data available to read.
    """
    print 'addReader:', reader
    if not reads.has_key(reader):
        reads[reader] = _addEvent(reader, FD_READ|FD_ACCEPT|FD_CONNECT)
        
def addWriter(writer, writes=writes):
    """Add a FileDescriptor for notification of data available to write.
    """
    print 'addWriter:', writer
    if not writes.has_key(writer):
        writes[writer] =_addEvent(writer, FD_WRITE)

def removeReader(reader):
    """Remove a Selectable for notification of data available to read.
    """
    if reads.has_key(reader):
        del events[reads[reader]]
        del reads[reader]

def removeWriter(writer, writes=writes):
    """Remove a Selectable for notification of data available to write.
    """
    if writes.has_key(writer):
        del events[writes[writer]]
        del writes[writer]

def removeAll():
    """Remove all selectables, and return a list of them."""
    result = reads.keys() + writes.keys()
    reads.clear()
    writes.clear()
    events.clear()
    return result

def doWaitForMultipleEvents(timeout,
                            reads=reads,
                            writes=writes):
    if timeout is None:
        #timeout = INFINITE
        timeout = 5000
    else:
        timeout = timeout * 1000
    print 'timeout is:', timeout
    
    handles = events.keys()
    val = WaitForMultipleObjects(handles, 0, timeout)
    print 'WaitForMultipleObjects returned:', val
    if val == WAIT_TIMEOUT:
        return
    elif val >= WAIT_OBJECT_0 and val < WAIT_OBJECT_0 + len(handles):
        print 'Here'
        fd, why = events[handles[val - WAIT_OBJECT_0]]
        print fd, why
        log.logOwner.own(fd)
        try:
            if why & FD_READ|FD_ACCEPT|FD_CONNECT:
                fd.doRead()
            elif why == FD_WRITE:
                fd.doWrite()
        except:
            log.deferr()
            why = FD_CLOSE

        if why == FD_CLOSE:
            removeReader(fd)
            removeWriter(fd)
            try:
                fd.connectionLost()
            except:
                log.deferr()

        log.logOwner.disown(fd)

def install():
    """Install the poll()-based event loop."""
    main.addReader = addReader
    main.addWriter = addWriter
    main.removeReader = removeReader
    main.removeWriter = removeWriter
    #main.doSelect = doPoll
    main.doSelect = doWaitForMultipleEvents
    main.removeAll = removeAll


def initThreads():
    """Do initialization for threads."""
    if main.wakerInstalled:
        # make sure waker is registered with us
        removeReader(main.waker)
        addReader(main.waker)

threadable.whenThreaded(initThreads)

__all__ = ["install"]




More information about the Twisted-Python mailing list