[Zope] Re: Running more than one instance on windows often block each other

Tim Peters tim.peters at gmail.com
Thu Jul 28 22:33:55 EDT 2005


[Tim]
> ...
> At this point, I wouldn't consider using it [SO_EXCLUSIVEADDRUSE]
> unless someone first took the tedious time it needs to demonstrate that
> when it is used, the thing that _I_ think is a bug here goes away in its
> presence:  the seeming ability of Windows to sometimes permit more
> than one socket to bind to the same address simultaneously (not serially --
> Windows does seem to prevent that reliably).

I started, but didn't get that far.  The first time I ran a pair of
processes with the attached (Python 2.4.1, WinXP Pro SP2), one fell
over with

...
    w.connect((host, port))
  File "<string>", line 1, in connect
socket.error: (10048, 'Address already in use')

after about 20 minutes.

So, on the face of it, playing with SO_EXCLUSIVEADDRUSE is no better
than the ZODB 3.4 Windows socket dance.  Both appear mounds
better-behaved than the Medusa Windows socket dance without
SO_EXCLUSIVEADDRUSE, though.  Since there are fewer other problems
associated with the ZODB 3.4 version (see last email), I'd like to
repeat this part:

> If you can, I would like you to try the ZODB 3.4 Windows socket dance
> code, and see if it works for you in practice.  I know it's not
> bulletproof, but it's portable across all flavors of Windows and is
> much better-behaved in my tests so far than the Medusa Windows socket
> dance.

Bulletproof appears impossible due to what still look like race bugs
in the Windows socket implementation.

Here's the code.  Note that it changed to try (no more than) 10,000
ports, although I didn't see it need to go through more than 200:

import socket, errno
import time, random

class BindError(Exception):
    pass

def socktest15():
    """Like socktest1, but w/o pointless blocking games.
    Added SO_EXCLUSIVEADDRUSE to the server socket.
    """

    a = socket.socket()
    w = socket.socket()

    a.setsockopt(socket.SOL_SOCKET, socket.SO_EXCLUSIVEADDRUSE, 1)
    # set TCP_NODELAY to true to avoid buffering
    w.setsockopt(socket.IPPROTO_TCP, 1, 1)
    # tricky: get a pair of connected sockets
    host = '127.0.0.1'
    port = 19999

    while 1:
        try:
            a.bind((host, port))
            break
        except:
            if port <= 10000:
                raise BindError, 'Cannot bind trigger!'
            port -= 1

    port2count[port] = port2count.get(port, 0) + 1
    a.listen(1)
    w.connect((host, port))
    r, addr = a.accept()
    a.close()

    return (r, w)

def close(r, w):
    for s in r, w:
        s.close()
    return # the fancy stuff below didn't help or hurt
    for s in w, r:
        s.shutdown(socket.SHUT_WR)
    for s in w, r:
        while 1:
            msg = s.recv(10)
            if msg == "":
                break
            print "eh?!", repr(msg)
    for s in w, r:
        s.close()

port2count = {}

def dump():
    print
    items = port2count.items()
    items.sort()
    for pair in items:
        print "%5d %7d" % pair

sofar = []
i = 0
try:
   while 1:
       if i % 1000 == 0:
           dump()
       i += 1
       print '.',
       try:
           stuff = socktest15()
       except RuntimeError:
           raise
       sofar.append(stuff)
       time.sleep(random.random()/10)
       if len(sofar) == 50:
           tup = sofar.pop(0)
           r, w = tup
           msg = str(random.randrange(1000000))
           w.send(msg)
           msg2 = r.recv(100)
           assert msg == msg2, (msg, msg2, r.getsockname(), w.getsockname())
           close(r, w)
except KeyboardInterrupt:
   for tup in sofar:
       close(*tup)


More information about the Zope mailing list